Nested properties for structs with unknown property names? - go

I'm using JSON to get some values into a variable from an external source.
I have a type like this that json.Unmarshal puts values into:
type Frame struct {
Type string
Value map[string]interface{}
}
var data Frame
After unmarshal, I can access a the type by: data.Type
but if I try doing something like:
if data.Type == "image" {
fmt.Printf("%s\n", data.Value.Imagedata)
}
The compiler complains about no such value data.Value.Imagedata.
So my question is, how do I reference properties in Go that I know will be there depending on some condition?
Doing this works:
type Image struct {
Filename string
}
type Frame struct {
Type string
Value map[string]interface{}
}
But that isn't very flexible as I will be receiving different Values.

json.Unmarshal will do its best to place the data where it best aligns with your type. Technically your first example will work, but you are trying to access the Value field with dot notation, even though you declared it to be a map:
This should give you some form of output:
if data.Type == 'image'{
fmt.Printf("%v\n", data.Value["Imagedata"])
}
… considering that "Imagedata" was a key in the JSON.
You have the option of defining the type as deeply as you want or expect the structure to be, or using an interface{} and then doing type assertions on the values. With the Value field being a map, you would always access the keys like Value[key], and the value of that map entry is an interface{} which you could type assert like Value[key].(float64).
As for doing more explicit structures, I have found that you could either break up the objects into their own types, or define it nested in one place:
Nested (with anonymous struct)
type Frame struct {
Type string
Value struct {
Imagedata string `json:"image_data"`
}
}
Seperate structs
type Frame struct {
Type string
Value value
}
type value struct {
Imagedata string `json:"image_data"`
}
I'm still learning Go myself, so this the extent of my current understanding :-).

Related

Read top level struct tags

How to get the tags from a struct field in Go? I have a nested struct which I want to pass as an argument to another function and read the tags there. I know that by accessing it as a field is possible, but I am searching for a way to it.
type MyStruct struct {
Nested struct{} `bson:"nested"`
}
func main() {
val := reflect.ValueOf(MyStruct{})
val.Type().Field(0).Tag.Get("bson") // I want to avoid this
val := reflect.ValueOf(MyStruct{}.Nested)
val.Tag???
}
The tag you want to access belongs to MyStruct. If you pass the value of the Nested field, a copy is made which will be completely detached from MyStruct. There's no way to tell if the value passed originates from a field of MyStruct or from another struct, or from any other source (e.g. from a composite literal). So this is not possible.

Unmarshal json without elements names

I´m trying to read a json file and parse into jsonObject in my Go class.
The json has a random names and number of elements when I receive it.
For example:
{"707514313":1505680270,"1568212945":1505676950,"732898933":1505681884}
So all the examples that I´ve seen that use an struct to define the interface for the unmarshal, where they put the names of the json values, but in my case I cannot do it since I dont know how many and the name of the values of the json.
var settings struct {
Name1 string `json:"707514313"`
Name2 string `json:"1568212945"`
Who knows how many more and with which names?!
}
So I end up unmarshalling with the default interface
func loadFileToJson(filename string) {
plan, _ := ioutil.ReadFile(filename)
var data interface{}
checkError(json.Unmarshal(plan, &data))
fmt.Println("Data %s ", data)
}
That load in data a (map[String]interface{})
Any idea how to achieve what I want.
EDIT:
I create this struct
type Structure struct {
Name map[string]uint64
}
And changing the old default by
var jsonObject []Structure
checkError(json.Unmarshal(plan, &jsonObject))
Is giving me this error
json: cannot unmarshal object into Go value of type []main.StructureData %s []
As #Anzel pointed out your data appears to be perfect for a map[string]uint64. This assumes a couple things, namely that your object keys are always strings (as in your example) and that the values are always uint64 (again as your sample data suggested). As such, unmarshal into that data type instead of interface{}
plan := []byte(`{"707514313":1505680270,"1568212945":1505676950,"732898933":1505681884}`)
var data map[string]uint64
json.Unmarshal(plan, &data)
fmt.Printf("Data is %+v\n", data)
OUTPUT
Data is map[1568212945:1505676950 732898933:1505681884 707514313:1505680270]
As commented, you just need to set the field type as map[string]uint64 and implement a few methods to parse the file and get the map value.
See in this playground for some pseudo code:
playground
However, depending on your map values, you may need to define the field type as map[string]uint64 or whatever reflecting the json structure, e.g. map[string]interface{} or even a separate embedded struct with nested structure.
Hope this helps.

Pointer within a custom structure

Recently, I got exposed to following source code from "Go Programming Blueprints" book.
type googleGeometry stuct {
*googleLocation `json:"location"`
}
type googleLocation struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
}
What I don't understand is why googleGeometry structure uses pointer instead of literal, and what is the reason behind such declaration?
I would expect below declaration instead of a pointer.
type googleGeometry stuct {
gl googleLocation `json:"location"`
}
I suppose it is because location can be null or would be absent at all.
Such cases can be represented with pointers. Because if it would be literal you will always have default values for lat and lng.
Please take a look on this example: Why pointer?
And this with a value type instead of reference: Value types always have default values
The main reason is JSON (de)serialization. If you want to unmarshal JSON into a struct and validate if certain attributes are present in the document using a pointer is a convenient way. Since the unmarshaller will leave missing fields nil.
The following code code will print: missing location attribute
func main() {
doc := []byte("{}") // json that misses a location member
var geometry googleGeometry
json.Unmarshal(doc, &geometry)
if geometry.googleLocation == nil {
fmt.Println("missing location attribute")
} else {
fmt.Println("location attribute unmarshalled correctly")
}
}
See: https://play.golang.org/p/hTj5HvI-AE
googleGeometry embeds a pointer to a googleLocation. It is essentially an unnamed field so that the Lat and Lng fields are accessible as if they were top level fields.
Why use
type googleGeometry stuct {
*googleLocation `json:"location"`
}
instead of
type googleGeometry stuct {
googleLocation `json:"location"`
}
?
I think they made the wrong call here. The pointer is nillable so if you do:
g := googleGeometry{}
fmt.Println(g.Lat)
you will get a nil reference panic. If you embed a non-pointer struct, the fields will be automatically initialized to zeros.
I'm not sure about the entire context of the question, however when a pointer is embedded inside the struct, even when the variable of type googleGeometry is passed by value the embedded googleLocation pointer still points to the same memory address as the initial variable (as the address is simply copied). Therefore although the original struct is passed by value both original and the copied variables share the same embedded pointer. That might be the intended behaviour.

What are the second pair of braces in this Golang struct?

var cache = struct {
sync.Mutex
mapping map[string]string
} {
mapping: make(map[string]string),
}
This looks like a struct with an embedded field sync.Mutex but I can't get my head around the second set of braces. It compiles and executes but what's up? Why does the label on the make instruction matter (it does) and the comma? Thanks...
The example you have is equivalent to:
type Cache struct {
sync.Mutex
mapping map[string]string
}
cache := Cache{
mapping: make(map[string]string),
}
Except in your example you do not declare a type of Cache and instead have an anonymous struct. In your example, as oppose to my Cache type, the type is the entire
struct {
sync.Mutex
mapping map[string]string
}
So think of the second pair of braces as the
cache := Cache{
mapping: make(map[string]string),
}
part.
make is a built in function that works similarly to C's calloc() which both initialize a data structure filled with 0'd values, in Go's case, certain data structures need to be initialized this way, other's (for the most part structs) are initialized with 0'd values automatically. The field there is needed so that the compiler now's cache.mapping is a empty map[string]string.
The comma there is part of Go's formatting, you can do Cache{mapping: make(map[string]string)} all on one line, but the moment the field's assignment is on a different line than the opening and closing braces, it requires a comma.
This is called a "struct literal" or an "anonymous struct" and is, in fact, how you always create structs in Go, it just may not be immediately obvious since you might be used to creating new types for struct types to make declaring them a bit less verbose.
An entire struct definition is actually a type in Go, just like int or []byte or string. Just as you can do:
type NewType int
var a NewType = 5 // a is a NewType (which is based on an int)
or:
a := 5 // a is an int
and both are distinct types that look like ints, you can also do the same thing with structs:
// a is type NewType (which is a struct{}).
type NewType struct{
A string
}
a := NewType{
A: "test string",
}
// a is type struct{A string}
a := struct{
A string
}{
A: "test string",
}
the type name (NewType) has just been replaced with the type of the struct itself, struct{A string}. Note that they are not the same type (an alias) for the purpose of comparison or assignment, but they do share the same semantics.

unmarshalling generic json with a type lookup map

I'm following up on Golang Decoding Generic JSON Objects to One of Many Formats as a way to unmarshal generic json. I'm going to have a multitude of different types tho which can be added by others, so hardcoding case statements is not feasible.
I also don't want to hardcode the type as a string, but let the ones using the library chose the "lookup" name, in case they want to rename their underlying structs later.
I am basically looking for something like this:
type myInterface interface {
Something() // irrelevant, just to show you It's not about interface{}
}
type myBar struct {} // fulfils myInterface
type mySomething struct {} // fulfils myInterface
var types = make(map[string]type) // <--- Obvious Pseudo code ;)
types["foo:bar"] = myBar // done by whoever uses the library
types["1230988"] = mySomething // ...
type storageWrapper struct {
Type string
Data json.RawMessage
}
func loadSomething(id string) myInterface {
buf := db.load(id) // pseudo code but you get the idea
sw := &storageWrapper{}
json.Unmarshal(buf, sw)
// now the interesting part
targetType := types[sw.Type]
thing := &targetType{}
json.Unmarshal(sw.Data, thing)
return thing
}
I have this feeling that I'm overthinking the whole Problem. Or that I'm trying to bend Go into something that conflicts with its underlying philosophy. I'm very open and thankful for any advice that suggests a different approach to the whole Problem
Have types be a map[string]myInterface, and to register a type, have callers store an empty value of that type (not a reference) into the map. Then, to unmarshal, you can "get the type" by copying the empty value out of the map, unmarshaling into it, and returning it (or a reference to it). The interface value will do the job of identifying which type is wanted. Plus, if users want to default some fields to non-zero/empty values in case they're not provided in the JSON, they can actually do that by storing those values within the struct in the type map.

Resources