Q: golang pointer to map[string]interface{} - json

I (golang newbie) am trying to create a map[string]interfaces{} in a function. The code compiles and runs but the map is empty.
package main
import (
"fmt"
"encoding/json"
)
func main() {
var f interface{}
var sJson string // JSON string from VT
var err error // errors
var b []byte // bytearray of JSON string
var rootMap map[string]interface{}
rootMap = make(map[string]interface{})
sJson=`{"key": "foo"}`
fmt.Println(sJson)
err = json2map(&b, &sJson, f, rootMap)
if err != nil { return }
switch v := rootMap["key"].(type) {
case float64:
fmt.Printf("Value: %d",v)
case string:
fmt.Printf("Value: %s", v)
case nil:
fmt.Println("key is nil")
default:
fmt.Println("type is unknown")
}
}
func json2map(b *[]byte, sJson *string, f interface{}, myMap map[string]interface{}) error {
var err error
*b = []byte(*sJson)
err = json.Unmarshal(*b,&f)
myMap = f.(map[string]interface{})
return err
}
The output is:
{"key": "foo"}
key is nil
I found this article which describes how to use map[string]string. This code works as expected:
package main
import (
"fmt"
)
type MyType struct {
Value1 int
Value2 string
}
func main() {
myMap := make(map[string]string)
myMap["key"] = "foo"
ChangeMyMap(myMap)
fmt.Printf("Value : %s\n", myMap["key"])
}
func ChangeMyMap(TheMap map[string]string) {
TheMap["key"] = "bar"
}
So I think my problem has to do with the map being of type interface instead of string but I cannot tell why the first code doesn't work, while the 2nd does.

There's a number of things causing confusion here:
You don't need b at all. You're passing a pointer to a byte slice that you're reassigning inside the function. Just use the string directly.
There's no need to us a pointer to the sJson string. Strings are immutable, and you're not trying to reassign the sJson variable.
You're unmarshaling into an empty interface, and then trying to reassign the myMap variable to the contents of f. Since myMap isn't a pointer, that assignment is only scoped to within the function. Just unmarshal directly into myMap
If you change those things, you'll find the json2map function ends up being one line, and can be dropped altogether:
func json2map(sJson string, myMap map[string]interface{}) error {
return json.Unmarshal([]byte(sJson), &myMap)
}

Related

Unmarshal JSON to Struct of Map

I have below program. I need to convert JSON data into object type cache which contains a single field of map[string]string type. There is something that I am doing wrong w.r.t. initialising map and unmarshalling JSON but unable to identify the syntax issue.
Note: I have marshalled data just for convenience sake to have sample JSON data.
package main
import (
"fmt"
"encoding/json"
"strconv"
)
const data = `{"Mode":{"ID-1":"ON","ID-2":"OFF","ID-3":"ON"}}`
type Cache struct {
Mode map[string]string `json:"Mode"`
}
func main() {
jsonData, _ := json.Marshal(data)
fmt.Println(strconv.Unquote(string(jsonData)))
var c Cache
c.Mode = make(map[string]string) //I want to initialise map so that I can store data in next step, but this is wrong I know
c.Mode["ID-4"] = "ON" //Want to store data like this
json.Unmarshal(jsonData, &c)
fmt.Println(c) //I am getting output as nil map i.e. {map[]}
for k, v := range c.Mode {
fmt.Println(k, v) //I am getting NO output i.e. blank
}
}
Here is your your code fixed https://play.golang.org/p/5ftaiz_Q5wl (attaching blow the copy)
package main
import (
"encoding/json"
"fmt"
"log"
)
const data = `{"Mode":{"ID-1":"ON","ID-2":"OFF","ID-3":"ON"}}`
type Cache struct {
Mode map[string]string `json:"Mode"`
}
func main() {
c := Cache{}
err := json.Unmarshal([]byte(data), &c)
if err != nil {
log.Println(err)
}
c.Mode["ID-4"] = "ON" //Want to store data like this
fmt.Println(c)
for k, v := range c.Mode {
fmt.Println(k, v)
}
}
Here is the output:
{map[ID-1:ON ID-2:OFF ID-3:ON ID-4:ON]}
ID-2 OFF
ID-3 ON
ID-4 ON
ID-1 ON

Convert struct to []json.RawMessage

Trying to convert a struct to []json.RawMessage. My understanding is that json.Marshal() converts it to byte[] as is []json.RawMessage. I cannot however seem to convert between the two. My function expects to receive input as []json.RawMessage.
Have tried several different methods including myIn := json.RawMessage(&myJsonStruct{"string1", "string2"}) and myIn := (*json.RawMessage)(json.Marshal(&myJsonStruct{"string1", "string2"}))
type myJsonStruct struct {
myString string `json:"myString"`
mySecongString string `json:"mySecondString"`
}
myIn := json.Marshal(&myJsonStruct{"string1", "string2"})
myFunction(myIn)
myFunction(receivedIn []json.RawMessage) {
//do work
return
}
There are a few things here:
You need to create a new slice of json.RawMessage in order to pass that expected type into your function myFunction as an argument
Store the result of marshaling your custom struct myJsonStruct in a variable myIn (type []byte)
Create a new variable of myInRaw (type json.RawMessage) and append that to the previously created slice of json.RawMessage.
The above steps will then allow you to pass in the slice of json.RawMessage to your function for further work to be done.
See example below or working example in the playground:
package main
import (
"encoding/json"
)
type myJsonStruct struct {
myString string `json:"myString"`
mySecongString string `json:"mySecondString"`
}
func myFunction(receivedIn []json.RawMessage) {
//do work
return
}
func main() {
var rawJSONSlice []json.RawMessage
myIn, err := json.Marshal(
&myJsonStruct{
myString: "string1",
mySecongString: "string2",
},
)
if err != nil {
// catch err
}
myInRaw := json.RawMessage(myIn)
rawJSONSlice = append(rawJSONSlice, myInRaw)
myFunction(rawJSONSlice)
}

Go Unmarshal reflect.Type got map[string]interface{}

I want to convert json string to some struct ,use as func param
I use reflect to get Param Type,this work fine ,
but if I use json.Unmarshal I always get map[string]interface{}
this is a mini Run sample,File name is json_test.go
package testJson
import (
"reflect"
"encoding/json"
"testing"
"log"
)
type LoginForm struct {
Name string
}
func Login(login LoginForm) {
}
func TestRun(t *testing.T) {
_func := Login
f := reflect.ValueOf(_func)
funcType := f.Type()
paramType := funcType.In(0)
log.Print(paramType.Name())
v := reflect.New(paramType).Elem().Interface()
jsonstr := `{"Name":"123"}`
log.Print(jsonstr)
log.Print(reflect.TypeOf(v).Name())
log.Print("%+v", v)
err := json.Unmarshal([]byte(jsonstr), &v)
if err != nil {
panic(err)
}
log.Print(reflect.TypeOf(v).Name())
log.Print( v)
var in []reflect.Value
in = make([]reflect.Value, funcType.NumIn())
in[0] = reflect.ValueOf(v)
f.Call(in)
}
Thanks for your help!
Don't reflect.New a pointer type because the result of that is a pointer to the pointer, instead reflect.New the Elem of the pointer type.
Change this:
v := reflect.New(paramType).Elem().Interface()
To this:
v := reflect.New(paramType.Elem()).Interface()
At this point v is already a pointer to LoginForm so you don't need to get its address with & when passing to Unmarshal.
https://play.golang.org/p/JM4fRXb7fo
Also the reason you got map[string]interface{} is because the return type of the Interface() method is unsurprisingly enough interface{}, so while v's underlying type could be anything its "topmost" type is interface{}. So then, doing &v will get you *interface{} as opposed to the inteded *LoginForm. And in the docs you can find that trying to unmarshal a JSON object into an interface{} will result in a value of type map[string]interface{}.

Golang Function Call in Map

I am a newbie at GO Programming. Here is scenario :-
There exists a JSON file that looks like this :-
{
"template": "linuxbase1",
"checkname": ["check_disk"],
"checkmethod": ["check_disk"]
}
I am Unmarshalling this data into a structure :-
package func1
import (
"io/ioutil"
"os"
"encoding/json"
"fmt"
)
type pluginfunc func() string
type Plugindata struct {
Template string `json:"template"`
Checkname []string `json:"checkname"`
Checkmethod []pluginfunc `json:"checkmethod"`
}
var (
Templatepath = "json_sample1.json"
Templateitems Plugindata
)
func Gettemplatedata() {
tdata, err := ioutil.ReadFile(Templatepath)
if err != nil {
fmt.Printf("Unable to read file %s. Error - %v\n",Templatepath, err.Error())
os.Exit(3)
}
json.Unmarshal(tdata, &Templateitems)
}
The "check_disk" function is here :-
package func1
func check_disk() string {
return "Called check_disk"
}
This is the program with main() :-
package main
import (
"fmt"
"checksexpt/func1"
)
func main() {
func1.Gettemplatedata()
fmt.Printf("Templateitems in Main() => %v\n",func1.Templateitems)
for index,funcname := range func1.Templateitems.Checkmethod {
fmt.Printf("%d = %s\n",index,funcname())
}
}
As expected, when I run main(); I see the error :-
Templateitems in Main() => {linuxbase1 [check_cpu check_disk] [<nil> <nil>]}
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x40115e]
goroutine 1 [running]:
panic(0x50e980, 0xc82000a100)
/opt/go/src/runtime/panic.go:481 +0x3e6
So, I am trying to grab a string from the JSON file and treat it as a function call. That obviously fails ! But, the primary constraint here is that the function names have to be picked from the JSON file. How can I do this ? I know that I can create a static map as follows :-
type checkfunc func() string
var (
Templateitems = map[string]map[string]checkfunc {
"linuxbase1": {
"check_disk": check_disk,
},
}
)
So, A call like - Templateitems["linuxbase1"]["check_disk"]() would work just fine. But, I dont want to create any such static map as the elements in that map needs to keep growing. Any ideas on this?
There is no direct way to parse a function directly from a JSON value. Also, you cannot use string values to refer to variables. So a string check_cpu would not be able to refer to the function with the same name directly.
What you can do instead is parse the json string as is and have a global map for functions. That way, you can call your functions like so:
var funcMap = map[string]pluginfunc{
"check_disk": check_disk,
"check_cpu": check_cpu
}
In your main loop:
for index, funcname := range func1.Templateitems.Checkmethod {
fmt.Printf("%d = %s\n", index, funcMap[funcname]())
}
If however, you really need to put the value in your structure, you can try implementing UnmarshalJSON from the json.Unmarshaler interface. A simple example would be:
type pf map[string]pluginfunc
type Plugindata struct {
Template string `json:"template"`
Checkname []string `json:"checkname"`
Checkmethod pf `json:"checkmethod"`
}
func (p *pf) UnmarshalJSON(data []byte) error {
d := []string{}
if err := json.Unmarshal(data, &d); err != nil {
return err
}
*p = make(pf)
for _, s := range d {
(*p)[s] = funcMap[s]
}
return nil
}
var funcMap = pf{
"check_disk": check_disk,
"check_cpu": check_cpu
}
func main() {
json.Unmarshal(tdata, &Templateitems)
for k, f := range Templateitems.Checkmethod {
fmt.Printf("%s -- %s\n", k, f())
}
}
Working code
Note that this way is not as readable or simple as the first method and it still relies on a function map.
You can read more about json.Unmarshaler here.

Is there a convenient way to convert a JSON-like map[string]interface{} to struct , and vice versa, with tags in Go? [duplicate]

I want to convert a struct to map in Golang. It would also be nice if I could use the JSON tags as keys in the created map (otherwise defaulting to field name).
Edit Dec 14, 2020
Since structs repo was archived, you can use mapstructure instead.
Edit TL;DR version, Jun 15, 2015
If you want the fast solution for converting a structure to map, see the accepted answer, upvote it and use that package.
Happy coding! :)
Original Post
So far I have this function, I am using the reflect package but I don't understand well how to use the package, please bear with me.
func ConvertToMap(model interface{}) bson.M {
ret := bson.M{}
modelReflect := reflect.ValueOf(model)
if modelReflect.Kind() == reflect.Ptr {
modelReflect = modelReflect.Elem()
}
modelRefType := modelReflect.Type()
fieldsCount := modelReflect.NumField()
var fieldData interface{}
for i := 0; i < fieldsCount; i++ {
field := modelReflect.Field(i)
switch field.Kind() {
case reflect.Struct:
fallthrough
case reflect.Ptr:
fieldData = ConvertToMap(field.Interface())
default:
fieldData = field.Interface()
}
ret[modelRefType.Field(i).Name] = fieldData
}
return ret
}
Also I looked at JSON package source code, because it should contain my needed implementation (or parts of it) but don't understand too much.
I also had need for something like this. I was using an internal package which was converting a struct to a map. I decided to open source it with other struct based high level functions. Have a look:
https://github.com/fatih/structs
It has support for:
Convert struct to a map
Extract the fields of a struct to a []string
Extract the values of a struct to a []values
Check if a struct is initialized or not
Check if a passed interface is a struct or a pointer to struct
You can see some examples here: http://godoc.org/github.com/fatih/structs#pkg-examples
For example converting a struct to a map is a simple:
type Server struct {
Name string
ID int32
Enabled bool
}
s := &Server{
Name: "gopher",
ID: 123456,
Enabled: true,
}
// => {"Name":"gopher", "ID":123456, "Enabled":true}
m := structs.Map(s)
The structs package has support for anonymous (embedded) fields and nested structs. The package provides to filter certain fields via field tags.
From struct to map[string]interface{}
package main
import (
"fmt"
"encoding/json"
)
type MyData struct {
One int
Two string
Three int
}
func main() {
in := &MyData{One: 1, Two: "second"}
var inInterface map[string]interface{}
inrec, _ := json.Marshal(in)
json.Unmarshal(inrec, &inInterface)
// iterate through inrecs
for field, val := range inInterface {
fmt.Println("KV Pair: ", field, val)
}
}
go playground here
Here is a function I've written in the past to convert a struct to a map, using tags as keys
// ToMap converts a struct to a map using the struct's tags.
//
// ToMap uses tags on struct fields to decide which fields to add to the
// returned map.
func ToMap(in interface{}, tag string) (map[string]interface{}, error){
out := make(map[string]interface{})
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
// we only accept structs
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("ToMap only accepts structs; got %T", v)
}
typ := v.Type()
for i := 0; i < v.NumField(); i++ {
// gets us a StructField
fi := typ.Field(i)
if tagv := fi.Tag.Get(tag); tagv != "" {
// set key of map to value in struct field
out[tagv] = v.Field(i).Interface()
}
}
return out, nil
}
Runnable example here.
Note, if you have multiple fields with the same tag value, then you will obviously not be able to store them all within a map. It might be prudent to return an error if that happens.
I like the importable package for the accepted answer, but it does not translate my json aliases. Most of my projects have a helper function/class that I import.
Here is a function that solves my specific problem.
// Converts a struct to a map while maintaining the json alias as keys
func StructToMap(obj interface{}) (newMap map[string]interface{}, err error) {
data, err := json.Marshal(obj) // Convert to a json string
if err != nil {
return
}
err = json.Unmarshal(data, &newMap) // Convert to a map
return
}
And in the main, this is how it would be called...
package main
import (
"fmt"
"encoding/json"
"github.com/fatih/structs"
)
type MyStructObject struct {
Email string `json:"email_address"`
}
func main() {
obj := &MyStructObject{Email: "test#test.com"}
// My solution
fmt.Println(StructToMap(obj)) // prints {"email_address": "test#test.com"}
// The currently accepted solution
fmt.Println(structs.Map(obj)) // prints {"Email": "test#test.com"}
}
package main
import (
"fmt"
"reflect"
)
type bill struct {
N1 int
N2 string
n3 string
}
func main() {
a := bill{4, "dhfthf", "fdgdf"}
v := reflect.ValueOf(a)
values := make(map[string]interface{}, v.NumField())
for i := 0; i < v.NumField(); i++ {
if v.Field(i).CanInterface() {
values[v.Type().Field(i).Name] = v.Field(i).Interface()
} else {
fmt.Printf("sorry you have a unexported field (lower case) value you are trying to sneak past. I will not allow it: %v\n", v.Type().Field(i).Name)
}
}
fmt.Println(values)
passObject(&values)
}
func passObject(v1 *map[string]interface{}) {
fmt.Println("yoyo")
}
I'm a bit late but I needed this kind of feature so I wrote this. Can resolve nested structs. By default, uses field names but can also use custom tags. A side effect is that if you set the tagTitle const to json, you could use the json tags you already have.
package main
import (
"fmt"
"reflect"
)
func StructToMap(val interface{}) map[string]interface{} {
//The name of the tag you will use for fields of struct
const tagTitle = "kelvin"
var data map[string]interface{} = make(map[string]interface{})
varType := reflect.TypeOf(val)
if varType.Kind() != reflect.Struct {
// Provided value is not an interface, do what you will with that here
fmt.Println("Not a struct")
return nil
}
value := reflect.ValueOf(val)
for i := 0; i < varType.NumField(); i++ {
if !value.Field(i).CanInterface() {
//Skip unexported fields
continue
}
tag, ok := varType.Field(i).Tag.Lookup(tagTitle)
var fieldName string
if ok && len(tag) > 0 {
fieldName = tag
} else {
fieldName = varType.Field(i).Name
}
if varType.Field(i).Type.Kind() != reflect.Struct {
data[fieldName] = value.Field(i).Interface()
} else {
data[fieldName] = StructToMap(value.Field(i).Interface())
}
}
return data
}
map := Structpb.AsMap()
// map is the map[string]interface{}