I'm trying to create a JSON representation within Go using a map[string]interface{} type. I'm dealing with JSON strings and I'm having a hard time figuring out how to avoid the JSON unmarshaler to automatically deal with numbers as float64s. As a result the following error occurs.
Ex.
"{ 'a' : 9223372036854775807}" should be map[string]interface{} = [a 9223372036854775807 but in reality it is map[string]interface{} = [a 9.2233720368547758088E18]
I searched how structs can be used to avoid this by using json.Number but I'd really prefer using the map type designated above.
The go json.Unmarshal(...) function automatically uses float64 for JSON numbers. If you want to unmarshal numbers into a different type then you'll have to use a custom type with a custom unmarshaler. There is no way to force the unmarshaler to deserialize custom values into a generic map.
For example, here's how you could parse values of the "a" property as a big.Int.
package main
import (
"encoding/json"
"fmt"
"math/big"
)
type MyDoc struct {
A BigA `json:"a"`
}
type BigA struct{ *big.Int }
func (a BigA) UnmarshalJSON(bs []byte) error {
_, ok := a.SetString(string(bs), 10)
if !ok {
return fmt.Errorf("invalid integer %s", bs)
}
return nil
}
func main() {
jsonstr := `{"a":9223372036854775807}`
mydoc := MyDoc{A: BigA{new(big.Int)}}
err := json.Unmarshal([]byte(jsonstr), &mydoc)
if err != nil {
panic(err)
}
fmt.Printf("OK: mydoc=%#v\n", mydoc)
// OK: mydoc=main.MyDoc{A:9223372036854775807}
}
func jsonToMap(jsonStr string) map[string]interface{} {
result := make(map[string]interface{})
json.Unmarshal([]byte(jsonStr), &result)
return result
}
Example - https://goplay.space/#ra7Gv8A5Heh
Related questions - create a JSON data as map[string]interface with the given data
Related
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)
}
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{}.
I have this struct
type SyncInfo struct {
Target string
}
Now I query some json data from ElasticSearch. Source is of type json.RawMessage.
All I want is to map source to my SyncInfo which I created the variable mySyncInfo for.
I even figured out how to do that...but it seems weird. I first call MarshalJSON() to get a []byte and then feed that to json.Unmarshal() which takes an []byte and a pointer to my struct.
This works fine but it feels as if I'm doing an extra hop. Am I missing something or is that the intended way to get from a json.RawMessage to a struct?
var mySyncInfo SyncInfo
jsonStr, _ := out.Hits.Hits[0].Source.MarshalJSON()
json.Unmarshal(jsonStr, &mySyncInfo)
fmt.Print(mySyncInfo.Target)
As said, the underlying type of json.RawMessage is []byte, so you can use a json.RawMessage as the data parameter to json.Unmarshal.
However, your problem is that you have a pointer (*json.RawMessage) and not a value. All you have to do is to dereference it:
err := json.Unmarshal(*out.Hits.Hits[0].Source, &mySyncInfo)
Working example:
package main
import (
"encoding/json"
"fmt"
)
type SyncInfo struct {
Target string
}
func main() {
data := []byte(`{"target": "localhost"}`)
Source := (*json.RawMessage)(&data)
var mySyncInfo SyncInfo
// Notice the dereferencing asterisk *
err := json.Unmarshal(*Source, &mySyncInfo)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", mySyncInfo)
}
Output:
{Target:localhost}
Playground: http://play.golang.org/p/J8R3Qrjrzx
json.RawMessage is really just a slice of bytes. You should be able to feed it directly into json.Unmarshal directly, like so:
json.Unmarshal(out.Hits.Hits[0].Source, &mySyncInfo)
Also, somewhat unrelated, but json.Unmarshal can return an error and you want to handle that.
err := json.Unmarshal(*out.Hits.Hits[0].Source, &mySyncInfo)
if err != nil {
// Handle
}
I am trying to simultaneously unmarshal and strip fields from a number of different JSON responses into appropriate Go structs. To do this, I created a Wrappable interface that defines the Unwrap method (which strips the appropriate fields) and pass that interface to the code that unmarshals and unwraps. It looks like the following example (also at http://play.golang.org/p/fUGveHwiz9):
package main
import (
"encoding/json"
"fmt"
)
type Data struct {
A string `json:"a"`
B string `json:"b"`
}
type DataWrapper struct {
Elements []Data `json:"elems"`
}
type Wrapper interface {
Unwrap() []interface{}
}
func (dw DataWrapper) Unwrap() []interface{} {
result := make([]interface{}, len(dw.Elements))
for i := range dw.Elements {
result[i] = dw.Elements[i]
}
return result
}
func unmarshalAndUnwrap(data []byte, wrapper Wrapper) []interface{} {
err := json.Unmarshal(data, &wrapper)
if err != nil {
panic(err)
}
return wrapper.Unwrap()
}
func main() {
data := `{"elems": [{"a": "data", "b": "data"}, {"a": "data", "b": "data"}]}`
res := unmarshalAndUnwrap([]byte(data), DataWrapper{})
fmt.Println(res)
}
However, when I run the code, Go panics with the following error:
panic: json: cannot unmarshal object into Go value of type main.Wrapper
It seems the unmarshaller doesn't want to be passed a pointer to an interface. I am somewhat surprised by this given that I can get at the underlying type and fields using the reflect package within the unmarshalAndUnwrap method. Can anyone provide insight into this problem and how I might work around it?
As you stated, passing a non-pointer fails. Why are you trying to do this anyway?
Replace
res := unmarshalAndUnwrap([]byte(data), DataWrapper{})
by
res := unmarshalAndUnwrap([]byte(data), &DataWrapper{})
It should do the trick and it avoid unnecessary copy.
This error should help you understand: http://play.golang.org/p/jXxCxPQDOw
My websocket server will receive and unmarshal JSON data. This data will always be wrapped in an object with key/value pairs. The key-string will act as value identifier, telling the Go server what kind of value it is. By knowing what type of value, I can then proceed to JSON unmarshal the value into the correct type of struct.
Each json-object might contain multiple key/value pairs.
Example JSON:
{
"sendMsg":{"user":"ANisus","msg":"Trying to send a message"},
"say":"Hello"
}
Is there any easy way using the "encoding/json" package to do this?
package main
import (
"encoding/json"
"fmt"
)
// the struct for the value of a "sendMsg"-command
type sendMsg struct {
user string
msg string
}
// The type for the value of a "say"-command
type say string
func main(){
data := []byte(`{"sendMsg":{"user":"ANisus","msg":"Trying to send a message"},"say":"Hello"}`)
// This won't work because json.MapObject([]byte) doesn't exist
objmap, err := json.MapObject(data)
// This is what I wish the objmap to contain
//var objmap = map[string][]byte {
// "sendMsg": []byte(`{"user":"ANisus","msg":"Trying to send a message"}`),
// "say": []byte(`"hello"`),
//}
fmt.Printf("%v", objmap)
}
Thanks for any kind of suggestion/help!
This can be accomplished by Unmarshaling into a map[string]json.RawMessage.
var objmap map[string]json.RawMessage
err := json.Unmarshal(data, &objmap)
To further parse sendMsg, you could then do something like:
var s sendMsg
err = json.Unmarshal(objmap["sendMsg"], &s)
For say, you can do the same thing and unmarshal into a string:
var str string
err = json.Unmarshal(objmap["say"], &str)
EDIT: Keep in mind you will also need to export the variables in your sendMsg struct to unmarshal correctly. So your struct definition would be:
type sendMsg struct {
User string
Msg string
}
Example: https://play.golang.org/p/OrIjvqIsi4-
Here is an elegant way to do similar thing. But why do partly JSON unmarshal? That doesn't make sense.
Create your structs for the Chat.
Decode json to the Struct.
Now you can access everything in Struct/Object easily.
Look below at the working code. Copy and paste it.
import (
"bytes"
"encoding/json" // Encoding and Decoding Package
"fmt"
)
var messeging = `{
"say":"Hello",
"sendMsg":{
"user":"ANisus",
"msg":"Trying to send a message"
}
}`
type SendMsg struct {
User string `json:"user"`
Msg string `json:"msg"`
}
type Chat struct {
Say string `json:"say"`
SendMsg *SendMsg `json:"sendMsg"`
}
func main() {
/** Clean way to solve Json Decoding in Go */
/** Excellent solution */
var chat Chat
r := bytes.NewReader([]byte(messeging))
chatErr := json.NewDecoder(r).Decode(&chat)
errHandler(chatErr)
fmt.Println(chat.Say)
fmt.Println(chat.SendMsg.User)
fmt.Println(chat.SendMsg.Msg)
}
func errHandler(err error) {
if err != nil {
fmt.Println(err)
return
}
}
Go playground
Further to Stephen Weinberg's answer, I have since implemented a handy tool called iojson, which helps to populate data to an existing object easily as well as encoding the existing object to a JSON string. A iojson middleware is also provided to work with other middlewares. More examples can be found at https://github.com/junhsieh/iojson
Example:
func main() {
jsonStr := `{"Status":true,"ErrArr":[],"ObjArr":[{"Name":"My luxury car","ItemArr":[{"Name":"Bag"},{"Name":"Pen"}]}],"ObjMap":{}}`
car := NewCar()
i := iojson.NewIOJSON()
if err := i.Decode(strings.NewReader(jsonStr)); err != nil {
fmt.Printf("err: %s\n", err.Error())
}
// populating data to a live car object.
if v, err := i.GetObjFromArr(0, car); err != nil {
fmt.Printf("err: %s\n", err.Error())
} else {
fmt.Printf("car (original): %s\n", car.GetName())
fmt.Printf("car (returned): %s\n", v.(*Car).GetName())
for k, item := range car.ItemArr {
fmt.Printf("ItemArr[%d] of car (original): %s\n", k, item.GetName())
}
for k, item := range v.(*Car).ItemArr {
fmt.Printf("ItemArr[%d] of car (returned): %s\n", k, item.GetName())
}
}
}
Sample output:
car (original): My luxury car
car (returned): My luxury car
ItemArr[0] of car (original): Bag
ItemArr[1] of car (original): Pen
ItemArr[0] of car (returned): Bag
ItemArr[1] of car (returned): Pen