mgo error when unmarshal map[string]interface{} - json

I want to store an arbitrary json object in a struct:
type C struct {
Name string `json:"name" bson:"name"`
Config map[string]interface{} `json:"config" bson:"config"`
}
This works fine when I store any deeply nested json object, but when I go retrieve it and mgo tries to Unmarshal it, I get:
Unmarshal can't deal with struct values. Use a pointer.
I'm not sure what's supposed to be a pointer. I get the same errors if I change it to:
Config *map[string]interface{}
The error occurs here: https://github.com/MG-RAST/golib/blob/master/mgo/bson/bson.go#L493
I don't know what it's reflecting on though.

So when you are unmarshaling the input argument takes a pointer to the struct, and you need to define a type in order to use a pointer to a struct.
type myMap map[string]interface{}
Then you can make a pointer to that type notice the ampersand to indicate pointer to your struct for type myMap, with json you could do something like so:
json := []Byte{`{"name": "value"}`}
c := &myMap{"value": "name"}
json.Unmarshal(c, json)
So you need *myMap to the struct not a pointer to the type. In order to explain the specific solution to this problem you need to add the context of how mongodb is unmarshaling your json.

Related

Why is Golang json.Unmarshall not working with "e" and "E" properties?

Suppose we want to unmarshal the JSON string {"e": "foo", "E": 1}.
Unmarshalling using the type messageUppercaseE works like expected. When using the type message though, the error json: cannot unmarshal number into Go struct field message.e of type string is returned.
Why are we not able to unmarshal the JSON, if only the "e" struct tag is present?
How would I be able to unmarshal the JSON? (I know that I am able to do this via Jeffail/gabs, but would like to stick to the type based approach.)
type message struct {
EventType string `json:"e"`
}
type messageUppercaseE struct {
EventType string `json:"e"`
UppercaseE uint64 `json:"E"`
}
Try it yourself at https://play.golang.org/p/T6KMJRLy7TN
Quoting the docs for unmarshal:
To unmarshal JSON into a struct, Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag), preferring an exact match but also accepting a case-insensitive match.
In this case, it is the case-insensitive match that causes the trouble.

Marshal map[string] interface{} but with tags

I know Go has a way to json.Marshal struct and also json.Marshal map[string]interface{}.
But when it marshals a struct, it can uses its json tags'. Is there a way, in standard or with another library to json.Marshal a map[string]interface{} and to pass it the same options that would have been stored in a field tag for a struct?
Actually there is a way, a bit tricky, but still.
You have to create a struct from reflect package.
You iterate over each element in your map and for each of them you create a struct field :
fields = append(fields, reflect.StructField{
Name: ,
Type: ,
Tag: ,
})
You can set the tags to whatever you want, meaning you can set json values, like the omitempty one.
Then you have to create the struct itself :
s := reflect.New(reflect.StructOf(fields))
Then another iteration over your map will be needed, to fill the struct with values.
s.Elem().FieldByName(name).Set(WhateverValue)
Finally you can pass your struct to json.Marshall(s.Interface()).

Dynamic Type in Unmarshal

I currently having the following problem:
I get a []byte / string via websocket which looks like
eventname {"JSON": "data", "In": "different formats"}
I split the string by the whitespace between the eventname and the JSON data and, depending on the eventname, I want to json.Unmarshal() the JSON data into a specific type or a var of a specific type, to make sure its all type safe.
So I would probably have a map which holds all the possible eventnames and the corresponding type for the JSON data, but im not sure how I would save a type, maybe by reference or by the stringified name?
type EventTypeList map[string]*interface{}
or
type EventTypeList map[string]string
So I can lookup if the event is in the EventTypeList and then let the Unmarshal func parse the data into the type from the map.
So basicly instead of the specific type "SpecificData":
type SpecificData struct {
JSON string
In string
}
socketData := SpecificData{}
err := json.Unmarshal(jsonData, &socketData)
I want to dynamically create the socketData var by the type from the EventTypeList
socketData := [dynamically determine this Type from EventTypeList]{}
After I parsed the data into that type, I want to call all listeners for that event and give them the socketData with the right type, so they can work with the expected data.
But im really not sure how I would accomplish this, if this is even possible or if this is even the right way...
Thanks for any help!
Ok, after reasearching, I think what I wanted to achive is not possible, because the type needs to be know at compile time, as it has been mentioned here:
Golang: cast an interface to a typed variable dynamically
But I think I can just pass the raw JSON string to the listener callback and Unmarshal the data in that function where the type of the data is known.
Thanks for all contributions!

how to initialize the struct of following structure in golang

I have the following data structure. I need to initialize it without using nested initialization. This data structure will be flushed to output a json file later.
type GeneratePlan struct{
Mode string `json:"mode"`
Name string `json:"name"`
Schema string `json:"schema"`
Version string `json:"version"`
Attack_plans []struct1 `json:"attack-plans"`
}
type struct1 struct {
Attack_plan Attack_plan `json:"attack-plan"`
}
type Attack_plan struct{
Attack_resouces []struct2 `json:"attack-resources"`
}
type struct2 struct {
Attack_resource Attack_resource `json:"attack-resource"`
}
Problem is when I try to append the variable of type struct2 to the Attack_resources[] slice, it gives the error as
cannot use struct2 (type *structs.Struct2) as type structs.Struct2 in append
How can we initialize the struct without using new or any ptr? As, if we use any of the standard struct initialization technique, it will give the above error.
If I change the above data structure and make it hold a pointer to another struct, it doesn't store the values correctly. I am very new to golang. Any help is appreciated. Thanks in advance!
You can initialize a struct value using:
resource := struct2{}
As #nothingmuch pointed out, if you have a struct pointer and need the underlying value, you can dereference the pointer using:
deref := *resource

Unmarshall into an interface type

I expected below code to print an object of type struct J, however it prints a map object of type map[string]interface{}. I can feel why it acts like that, however when I run, reflect.ValueOf(i).Kind(), it returns Struct, so it kinda gives me the impression that Unmarshal method should return type J instead of a map. Could anyone enlighten me ?
type J struct {
Text string
}
func main() {
j := J{}
var i interface{} = j
js := "{\"Text\": \"lala\"}"
json.Unmarshal([]byte(js), &i)
fmt.Printf("%#v", i)
}
The type you're passing into Unmarshal is not *J, you're passing in an *interface{}.
When the json package reflects what the type is of the pointer it received, it sees interface{}, so it then uses the default types of the package to unmarshal into, which are
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
There is almost never a reason to use a pointer to an interface. If you find yourself using a pointer to an interface, and you don't know exactly why, then it's probably a mistake. If you want to unmarshal into J, then pass that in directly. If you need to assign that to an intermediary interface, make sure you use a pointer to the original value, not a pointer to its interface.
http://play.golang.org/p/uJDFKfSIxN
j := J{}
var i interface{} = &j
js := "{\"Text\": \"lala\"}"
json.Unmarshal([]byte(js), i)
fmt.Printf("%#v", i)
This is expected behavior: instead of giving json.Unmarshal a pointer to a properly typed place in memory you give it a pointer to a place in memory with type interface{}. It can essentially store anything in there under the type the JSON defines, so it does just that.
See it like this:
Unmarshal gets a place to store the data v with type interface{}
Unmarshal detects a map encoded as JSON
Unmarshal sees that the target type is of type interface{}, creates a go map from it and stores it in v
If you would have given it a different type than interface{} the process would have looked like this:
Unmarshal gets a place to store the data v with type struct main.J
Unmarshal detects a map encoded as JSON
Unmarshal sees that the target type is struct main.J and begins to recursively fit the data to the type
The main point is, that the initial assignment
var i interface{} = j
is completely ignored by json.Unmarshal.