Go: Convert map[interface {}]interface {} to map[string]interface {} in a deep structure [duplicate] - go

Services:
- Orders:
- ID: $save ID1
SupplierOrderCode: $SupplierOrderCode
- ID: $save ID2
SupplierOrderCode: 111111
I want to convert this yaml string to json, cause the source data is dynamic, so I can't map it to a struct:
var body interface{}
err := yaml.Unmarshal([]byte(s), &body)
Then I want to convert that interface to json string again:
b, _ := json.Marshal(body)
But an error occur:
panic: json: unsupported type: map[interface {}]interface {}

Foreword: I optimized and improved the below solution, and released it as a library here: github.com/icza/dyno. The below convert() function is available as dyno.ConvertMapI2MapS().
The problem is that if you use the most generic interface{} type to unmarshal into, the default type used by the github.com/go-yaml/yaml package to unmarshal key-value pairs will be map[interface{}]interface{}.
First idea would be to use map[string]interface{}:
var body map[string]interface{}
But this attempt falls short if the depth of the yaml config is more than one, as this body map will contain additional maps whose type will again be map[interface{}]interface{}.
The problem is that the depth is unknown, and there may be other values than maps, so using map[string]map[string]interface{} is not good.
A viable approach is to let yaml unmarshal into a value of type interface{}, and go through the result recursively, and convert each encountered map[interface{}]interface{} to a map[string]interface{} value. Both maps and slices have to be handled.
Here's an example of this converter function:
func convert(i interface{}) interface{} {
switch x := i.(type) {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k.(string)] = convert(v)
}
return m2
case []interface{}:
for i, v := range x {
x[i] = convert(v)
}
}
return i
}
And using it:
func main() {
fmt.Printf("Input: %s\n", s)
var body interface{}
if err := yaml.Unmarshal([]byte(s), &body); err != nil {
panic(err)
}
body = convert(body)
if b, err := json.Marshal(body); err != nil {
panic(err)
} else {
fmt.Printf("Output: %s\n", b)
}
}
const s = `Services:
- Orders:
- ID: $save ID1
SupplierOrderCode: $SupplierOrderCode
- ID: $save ID2
SupplierOrderCode: 111111
`
Output:
Input: Services:
- Orders:
- ID: $save ID1
SupplierOrderCode: $SupplierOrderCode
- ID: $save ID2
SupplierOrderCode: 111111
Output: {"Services":[{"Orders":[
{"ID":"$save ID1","SupplierOrderCode":"$SupplierOrderCode"},
{"ID":"$save ID2","SupplierOrderCode":111111}]}]}
One thing to note: by switching from yaml to JSON via Go maps you'll lose the order of the items, as elements (key-value pairs) in a Go map are not ordered. This may or may not be a problem.

http://sigs.k8s.io/yaml is "a wrapper around go-yaml designed to enable a better way of handling YAML when marshaling to and from structs". Among other things, it provides yaml.YAMLToJSON method that should do what you want.

I was having the same issue and the icza's answer helped me.
Additionally I improved the convert() function a little bit, so it visits existing map[string]interface{} nodes to search for inherited map[interface{}]interface{} nodes deep inside.
func convert(i interface{}) interface{} {
switch x := i.(type) {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k.(string)] = convert(v)
}
return m2
case map[string]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
m2[k] = convert(v)
}
return m2
case []interface{}:
for i, v := range x {
x[i] = convert(v)
}
}
return i
}

Related

How to create a Golang struct instance from an array?

Let's say I have a bunch of array of strings, for example:
data := [4]string{"a", "3.0", "2.5", "10.7"}
And a struct definition:
type Record struct {
name string
x float64
y float64
mag float64
}
I'd like to create an instance of this struct from each array.
I need to match the first item of the array to the first field of the struct and so on. Is it possible to do this?
Each array corresponds to one line of a file, so I can actually decide how to read these values in case a different approach is better.
An easy way is to use reflection to iterate over the fields of the struct, obtain their address (pointer), and use fmt.Sscan() to scan the string value into the field. fmt.Sscan() will handle the different types of fields for you. This is in no way an efficient solution, it is just a short, easy and flexible solution. If you need an efficient solution, you have to handle all fields explicitly, manually.
This only works if the fields of the struct are exported, e.g.:
type Record struct {
Name string
X float64
Y float64
Mag float64
Age int
}
The function that loads a string slice into a struct value:
func assign(recordPtr interface{}, data []string) error {
v := reflect.ValueOf(recordPtr).Elem()
max := v.NumField()
if max > len(data) {
max = len(data)
}
for i := 0; i < max; i++ {
if _, err := fmt.Sscan(data[i], v.Field(i).Addr().Interface()); err != nil {
return err
}
}
return nil
}
Note that this implementation tries to fill as many fields as possible (e.g. it does not return an error if the struct has more or less fields than input data provided). Also note that this assign() function can fill any other structs, not just Record, that's why it's flexible.
Example testing it:
data := []string{"a", "3.0", "2.5", "10.7", "23"}
var r Record
if err := assign(&r, data); err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", r)
Output (try it on the Go Playground):
{Name:a X:3 Y:2.5 Mag:10.7 Age:23}
There is no simple way to do this. You have to assign struct members one by one.
for _, x := range data {
x, err := strconv.ParseFloat(x[1])
y, err := strconv.ParseFloat(x[2])
max, err := strconv.ParseFloat(x[3])
strData = append(strData, Record{name: x[0], x: x, y: y, mag: mag})
}
You also have to deal with parse errors.
Of course it it possible to do that in Go.
The following code example will assign to s the record filled with the fields of data.
package main
import (
"fmt"
"strconv"
)
type Record struct {
name string
x float64
y float64
mag float64
}
func main() {
data := [4]string{"a", "3.0", "2.5", "10.7"}
x, err := strconv.ParseFloat(data[1], 64)
if err != nil {
panic(err)
}
y, err := strconv.ParseFloat(data[2], 64)
if err != nil {
panic(err)
}
mag, err := strconv.ParseFloat(data[3], 64)
if err != nil {
panic(err)
}
s := Record{ name: data[0], x: x, y: y, mag: mag}
fmt.Println(s)
}

convert a map[string]interface{} to JSON and same JSON to map[string]interface{} (preserve interface{} types)

I am putting structs names and types into a map, Marshal this map into a JSON and Unmarshal this JSON back into a map which I can extract the types of each struct the same as before.
Here is what tried:
1 - have predefined structs
2 - define a map[string]interface{}
3 - populate this map with the struct name as key and struct type as interface() value.
4 - Marshaled this map into a JSON.
5 - Define a new map[string]interface{}
6 - Unmarshal the JSON into this new map for later use in the program.
My hope is to preserve the the reflect types of the interface{} the same.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
// Gallery :
type Gallery struct {
ID int
Name string
Images []Image
}
// Image :
type Image struct {
Source string
Height int
Width int
}
func main() {
myMap := make(map[string]interface{})
myMap["Gallery"] = Gallery{}
// encode this map into JSON, jStr:
jStr, err := json.Marshal(&myMap)
if err != nil {
fmt.Println(err)
}
// print the JSON string, just for testing.
fmt.Printf("jStr is: %v \n", string(jStr))
// create a newMap and populate it with the JSON jStr
newMap := make(map[string]interface{})
err = json.Unmarshal(jStr, &newMap)
if err != nil {
fmt.Println(err)
}
fmt.Println()
// iterate over myMap and inspect the reflect type of v
for k, v := range myMap {
fmt.Printf("myMap: k is: %v \t v is: %v \n", k, v)
fmt.Printf("myMap: reflect.TypeOf(v) is: %v \n", reflect.TypeOf(v).Kind())
}
fmt.Println()
// iterate over newMap and inspect the reflect type of v
for k, v := range newMap {
fmt.Printf("newMap: k is: %v \t v is: %v \n", k, v)
fmt.Printf("newMap: reflect.TypeOf(v) is: %v \n", reflect.TypeOf(v).Kind())
fmt.Println()
}
The reflect.TypeOf(v).Kind() for both maps should be struct. But it is not the case. for myMap, the Kind is struct (which is correct), but for newMap, the Kind is map which is NOT correct. What am I missing here. Here is the full code on play.golang.org: https://play.golang.org/p/q8BBLlw3fMA

How to convert interface to interfaces slice?

My input is an interface{}, and I know it can be an array of any type.
I'd like to read one of the elements of my input, so I try to convert my interface{} into an []interface{}, but go will give me the following error:
panic: interface conversion: interface {} is []map[string]int, not []interface {}
How can I do that conversion? (without reflect if possible).
Playground test
Thanks
The solution involving the reflect package.
package main
import (
"fmt"
"reflect"
)
func main() {
var v interface{} = []string{"a", "b", "c"}
var out []interface{}
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Slice {
for i := 0; i < rv.Len(); i++ {
out = append(out, rv.Index(i).Interface())
}
}
fmt.Println(out)
}
// Output:
// [a b c]
I'm actually working on this right now as my issue involves taking something from a json object (map[string]interface{}) which may or may not contain a particular key ({"someKey": [a, b, c, ...]) and if it does contain that key then we want to take that (which will necessarily be interface{} type) and convert it to []interface{}. The method I've found so far is to use json marshall/unmarshall. This seems a little hacky to me, will update if I find a more elegant solution. Til then, you can have my method:
https://play.golang.org/p/4VAwQQE4O0b
type a map[string]interface{}
type b []string
func main() {
obj := a{
"someKey": b{"a", "b", "c"},
}
if obj["someKey"] != nil { // check the value exists
var someArr []interface{}
//marshal interface to byte and then unmarshal to []interface{}
somebytes, _ := json.Marshal(obj["someKey"])
err := json.Unmarshal(somebytes, &someArr)
if err != nil {
fmt.Println("Error in unmarshal")
}
fmt.Println(someArr)
}
}
How can I do that conversion? (without reflect if possible).
Please consider type switches.
Reflection is expensive.
func toSlice(i interface{}) []interface{} {
var out []interface{}
switch v := i.(type) {
case []interface{}:
for x := 0; x < len(v); x++ {
out = append(out, v[x])
}
default:
fmt.Printf("invalid type: %T\n", v)
}
return out
}
The point of the interface is to define the behaviour you want to use, if you use an empty interface, you know nothing about the types in that slice.
If you want to print it, you can use println or printf with no conversion.
If you want to access it, and must allow any type, you can use reflect (slow and complex to use).
If you want to acess it, and use common behaviour/ data that you can define functions for, define an interface, e.g. :
type Doer interface {
Do() error
}
parentStruct := []Doer{...}
testStruct.Do()
If none of that works, wait for Go 2 and generics.
For anyone finding this in 2022, now that we have generics you can do it like this:
func convertSlice[T any](data []T) []interface{} {
output := make([]interface{}, len(data))
for idx, item := range data {
output[idx] = item
}
return output
}
I think what you are looking is type assertion
package main
import (
"fmt"
)
func main() {
parentStruct := map[string]interface{}{
"test": []map[string]int{
{"a": 1, "b": 2},
{"c": 3},
},
}
testStruct := parentStruct["test"].([]map[string]int)
fmt.Println(testStruct)
}
read this link: https://golang.org/ref/spec#Type_assertions
https://play.golang.org/p/81uL2hgrN3l

golang - converting [ ]Interface to [ ]strings or joined string

How can I convert []interface to []strings or just as a joined single string with all elements in []interface ? Below is the screenshot showing exact value of "scope" which of type []interface with length of 2. In below code, where case is "slice" Currently i am doing this using reflect but this is not looking good. Wondering there should be some good way to join a slice\array of interface elements.
I also did try using json unmarshall like this "json.Unmarshal([]byte(jwtResp),&mymap)" but having trouble with type issues in converting jwt.MapClaims to byte[]
parsedkey, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(key))
parsedToken, jwtErr := jwt.Parse(bearerToken, func(token *jwt.Token) (interface{}, error) {
return parsedkey, nil
})
jwtValues = make(map[string]string)
// we dont know which data types we are dealing with here, so using swtich case based on value data type
jwtResp := parsedToken.Claims.(jwt.MapClaims)
for k, v := range jwtResp {
switch reflect.TypeOf(v).Kind() {
case reflect.Slice:
fmt.Println("tp is : ", reflect.TypeOf(v)) // this is []interface{}
fmt.Println("type is : ", reflect.TypeOf(v).Kind()) // this is slice
s := reflect.ValueOf(v)
for i := 0; i < s.Len(); i++ {
jwtValues[k] += s.Index(i).Interface().(string)
jwtValues[k] += "|"
}
case reflect.String:
jwtValues[k] = v.(string)
default:
fmt.Println("unknown datatype")
}
}
Thanks for the suggestions . Below is the closest solution i found replacing 'switch' with 'if' condition along with type assertions. 'reflect' is costly. Using below code, i am finding if values of jwtResp is a slice, or string or something else. If slice, traverse through the values([]interface with string type elements) and join those as one concatenated string.
for k, v := range jwtResp {
if s, ok := v.(string); ok {
JwtValues[k] = s
} else if s, ok := v.([]interface{}); ok {
sslice := make([]string, len(s))
for i, v := range s {
sslice[i] = v.(string)
}
JwtValues[k] = strings.Join(sslice, "|")
} else {
logger.Log(util.LogDebug, "unknown data type")
}
}
Not sure to have fully understood your question, but it seems you're on the right track.
parts := make([]string, s.Len())
for i := 0; i < s.Len(); i++ {
parts = append(parts, s.Index(i).String())
}
jwtValues[k] = strings.Join(parts, "|")

Reading bytes into structs using reflection

I'm trying to write functions that will allow me to marshal/unmarshal simple structs into byte arrays. I've succeeded in writing Marshal, with help from the kind folks at #go-nuts, but I'm running into trouble writing Unmarshal.
// Unmarshal unpacks the binary data and stores it in the packet using
// reflection.
func Unmarshal(b []byte, t reflect.Type) (pkt interface{}, err error) {
buf := bytes.NewBuffer(b)
p := reflect.New(t)
v := reflect.ValueOf(p)
for i := 0; i < t.NumField(); i++ {
f := v.Field(i)
switch f.Kind() {
case reflect.String:
// length of string
var l int16
var e error
e = binary.Read(buf, binary.BigEndian, &l)
if e != nil {
err = e
return
}
// read length-of-string bytes from the buffer
raw := make([]byte, l)
_, e = buf.Read(raw)
if e != nil {
err = e
return
}
// convert the bytes to a string
f.SetString(bytes.NewBuffer(raw).String())
default:
e := binary.Read(buf, binary.BigEndian, f.Addr())
if e != nil {
err = e
return
}
}
}
pkt = p
return
}
The problem with the code above is that the call to f.Addr() near the end is apparently trying to get the address of an unaddressable value.
If there is an alternative solution, I would appreciate that as well. Either way, any help would be much appreciated.
Thanks!
I think you should use
v := p.Elem() // Get the value that 'p' points to
instead of
v := reflect.ValueOf(p)
Working example with lots of assumptions and a trivial data format:
package main
import (
"fmt"
"reflect"
"strconv"
)
// example marshalled format. lets say that marshalled data will have
// four bytes of a formatted floating point number followed by two more
// printable bytes.
type m42 []byte
// example struct we'd like to unmarshal into.
type packet struct {
S string // exported fields required for reflection
F float64
}
// example usage
func main() {
var p packet
if err := Unmarshal(m42("3.14Pi"), &p); err == nil {
fmt.Println(p)
} else {
fmt.Println(err)
}
}
func Unmarshal(data m42, structPtr interface{}) error {
vp := reflect.ValueOf(structPtr)
ve := vp.Elem() // settable struct Value
vt := ve.Type() // type info for struct
nStructFields := ve.NumField()
for i := 0; i < nStructFields; i++ {
fv := ve.Field(i) // settable field Value
sf := vt.Field(i) // StructField type information
// struct field name indicates which m42 field to unmarshal.
switch sf.Name {
case "S":
fv.SetString(string(data[4:6]))
case "F":
s := string(data[0:4])
if n, err := strconv.ParseFloat(s, 64); err == nil {
fv.SetFloat(n)
} else {
return err
}
}
}
return nil
}
Appropriate alternative solutions would depend heavily on the real data you need to support.
I'm going to bet that the reason f.Addr() has the problem because it actually isn't addressable.
the reflect package Type object has a method that will tell you if the type is addressable called CanAddr(). Assuming the field is addressable if it's not a string is not always true. If the struct is not passed in as a pointer to a struct then it's fields won't be addressable. For more details about what is and isn't addressable see: http://weekly.golang.org/pkg/reflect/#Value.CanAddr which outlines the correct rules.
Essentially for your code to work I think you need to ensure you always call it with a pointer to a struct.

Resources