unmarshalling generic json with a type lookup map - go

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.

Related

Can I get a variable of a type based on reflect.Type [duplicate]

I have a function which takes an interface, like this:
func method(data interface{})
.. because I need to process different structs which have common fields/methods. In this function I use data tens or hundreds of times, in different places. It's really unpleasant to add switch a.(type) { case .. case .. all the time.
Is there a way to create a variable with just one switch with needed type and then just use this variable everywhere later? Something like:
var a .... // something here
switch data.(type) {
case *Struct1:
a = data.(*Struct1)
case *Struct2:
a = data.(*Struct2)
}
// Continue with 'a' only
a.Param = 15
fmt.Println(a.String())
Go is a statically typed language, the type of a must be known at compile time. And since Go does not support generics yet, you can't do what you want.
Try to come up with some other solution, e.g. abstract away the things you want to do with a into an interface, and have the concrete types implement that interface. Then a can be a variable of this interface type, and you can call methods of it.
If you can achieve this, actually you can even change the parameter of the data type to this interface, and no type assertion or type switch is needed.
Alternatively you could use reflection to access common fields (either for get or set) identified by their name, but reflection provides no compile-time guarantee, and it's usually less efficient. For an example how to do that, see this question: Assert interface to its type
You can't do what you ask for in your question directly, go is statically typed, so you can't have one variable that can hold different types, and still access that variable as if it is typed.
If you're only working on the common struct fields in your method, you are perhaps better off gathering all the common variables in its own struct, illustrated below as the commons struct and have your method take that type as an argument
package main
import (
"fmt"
)
type commons struct {
name string
age int
}
type structA struct {
commons
other_stuff int
}
type structB struct {
commons
foo string
}
func method(c* commons) {
fmt.Println(c)
c.age +=1
}
func main() {
a := structA{commons{"foo", 44}, 1}
b := structB{commons{"bar", 33}, "test"}
method(&a.commons)
method(&b.commons)
fmt.Println(a)
}
Go playground
I can't figure out what is your real goal but if the "method" you want to write handles common fields from similar structures, and you cannot fix original structures using Type Embedding, as #nos said above, then you can try to make another structure for method-internal use:
var v Vehicle // with common fields
switch data.(type) {
case *Car:
v.Handle = data.(*Car).Handle // or CircleHandle
case *Motorcycle:
v.Handle = data.(*Motorcycle).Handle // or BarHandle
}
v.Degree = 15
v.Speed = 50
v.Direction = "left"
v.Style = "rough"
/// so many things on `v`...
steering(v)
I think it is not a good approach but sometimes... :-)

How to create a variable with needed type instead of type assertion

I have a function which takes an interface, like this:
func method(data interface{})
.. because I need to process different structs which have common fields/methods. In this function I use data tens or hundreds of times, in different places. It's really unpleasant to add switch a.(type) { case .. case .. all the time.
Is there a way to create a variable with just one switch with needed type and then just use this variable everywhere later? Something like:
var a .... // something here
switch data.(type) {
case *Struct1:
a = data.(*Struct1)
case *Struct2:
a = data.(*Struct2)
}
// Continue with 'a' only
a.Param = 15
fmt.Println(a.String())
Go is a statically typed language, the type of a must be known at compile time. And since Go does not support generics yet, you can't do what you want.
Try to come up with some other solution, e.g. abstract away the things you want to do with a into an interface, and have the concrete types implement that interface. Then a can be a variable of this interface type, and you can call methods of it.
If you can achieve this, actually you can even change the parameter of the data type to this interface, and no type assertion or type switch is needed.
Alternatively you could use reflection to access common fields (either for get or set) identified by their name, but reflection provides no compile-time guarantee, and it's usually less efficient. For an example how to do that, see this question: Assert interface to its type
You can't do what you ask for in your question directly, go is statically typed, so you can't have one variable that can hold different types, and still access that variable as if it is typed.
If you're only working on the common struct fields in your method, you are perhaps better off gathering all the common variables in its own struct, illustrated below as the commons struct and have your method take that type as an argument
package main
import (
"fmt"
)
type commons struct {
name string
age int
}
type structA struct {
commons
other_stuff int
}
type structB struct {
commons
foo string
}
func method(c* commons) {
fmt.Println(c)
c.age +=1
}
func main() {
a := structA{commons{"foo", 44}, 1}
b := structB{commons{"bar", 33}, "test"}
method(&a.commons)
method(&b.commons)
fmt.Println(a)
}
Go playground
I can't figure out what is your real goal but if the "method" you want to write handles common fields from similar structures, and you cannot fix original structures using Type Embedding, as #nos said above, then you can try to make another structure for method-internal use:
var v Vehicle // with common fields
switch data.(type) {
case *Car:
v.Handle = data.(*Car).Handle // or CircleHandle
case *Motorcycle:
v.Handle = data.(*Motorcycle).Handle // or BarHandle
}
v.Degree = 15
v.Speed = 50
v.Direction = "left"
v.Style = "rough"
/// so many things on `v`...
steering(v)
I think it is not a good approach but sometimes... :-)

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.

Why does golang prohibit assignment to same underlying type when one is a native type?

Consider this code:
package main
import "fmt"
type specialString string
func printString(s string) {
fmt.Println(s)
}
// unlike, say, C++, this is not legal GO, because it redeclares printString
//func printString(s specialString) {
// fmt.Println("Special: " + s)
//}
func main() {
ss := specialString("cheese")
// ... so then why shouldn't this be allowed?
printString(ss)
}
My question is: why is the language defined so that the call to printString(ss) in main() is not allowed? (I'm not looking for answers that point to the Golang rules on assignment; I have already read them, and I see that both specialString and string have the same 'underlying type' and both types are 'named' -- if you consider the generic type 'string' to be named, which Golang apparently does -- and so they are not assignable under the rules.)
But why are the rules like that? What problem is solved by treating the built-in types as 'named' types, and preventing you from passing named types to all the standard library functions that accepting the same underlying built-in type? Does anybody know what the language designers had in mind here?
From my point of view, it seems to create a lot of pointless type conversion in the code, and discourages the use of strong typing where it actually would make sense..
I believe the initial authors' logic here is that named type is named for a reason - it represents something different, not just underlying type.
I guess I've read it somewhere in golang-nuts, but can't remember exact discussion.
Consider the following example:
type Email string
You named it Email, because you need to represent e-mail entity, and 'string' is just simplified representation of it, sufficient for the very start. But later, you may want to change Email to something more complex, like:
type Email struct {
Address string
Name string
Surname string
}
And that will break all your code that work with Email implicitly assuming it's a string.
This is because Go does not have class inheritance. It uses struct composition instead. Named types do not inherit properties from their underlying type (that's why it's not called "base type").
So when you declare a named type specialString with an underlying type of a predefined type string, your new type is a completely different type from the underlying one. This is because Go assumes you will want to assign different behaviors to your new type, and will not check its underlying type until run-time. This is why Go is both a static and dynamic language.
When you print
fmt.Println(reflect.TypeOf(ss)) // specialString
You get specialString, not string. If you take a look at Println() the definition is as follows:
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
This means you can print any predeclared types (int, float64, string) because all of them implements at least zero methods, which makes them already conform to the empty interface and pass as "printable", but not your named type specialString which remains unknown to Go during compile time. We can check by printing the type of our interface{} against specialString.
type specialString string
type anything interface{}
s := string("cheese")
ss := specialString("special cheese")
at := anything("any cheese")
fmt.Println(reflect.TypeOf(ss)) // specialString
fmt.Println(reflect.TypeOf(s)) // string
fmt.Println(reflect.TypeOf(at)) // Wow, this is also string!
You can see that specialString keeps being naughty to its identity. Now, see how it does when passed into a function at run-time
func printAnything(i interface{}) {
fmt.Println(i)
}
fmt.Println(ss.(interface{})) // Compile error! ss isn't interface{} but
printAnything(ss) // prints "special cheese" alright
ss has become passable as interface{} to the function. By that time Go has already made ss an interface{}.
If you really want to understand deep down the hood this article on interfaces is really priceless.
It's called nominal typing. It simply means that the type is identified by it's name and it has to be made explicit to be useful.
From a convenience point of view it is easy to critique but it super useful.
For example, let's say you have a parameter to a function that is a string but it cannot be just any string, there are rules you need to check. If you change the type from string to something that implies that you checked the string for potential problems you made a good design decision because it's now clear from just looking at the code that the string needs to go via some function to validate the input first (and enrich it's type in the process).
type Validated string
func Validate(input string): (Validated, err) {
return Validated(input), nil // assuming you actually did validate the string
}
Go makes these tradeoffs because it does improve readability (i.e. the ability of someone unfamiliar with your code to quickly understand how things work) and that's something they (the Go language designers) value above all else.

Instantiating a struct via name using a string in go

I am trying to create a function that takes a []byte and an interface{} (standing for the struct) and returns an interface{} as the struct type passed into the func.
Something like this:
package main
import (
"encoding/json"
)
func UnmarshalFromJSONArray(sms []byte,tt string) (interface{}) {
var ts = new(tt)
err := json.Unmarshal(sms,&ts)
if(err != nil) {
fmt.Println(err)
}
return sms
}
So that method would run something like this:
// let's say a struct has the following definition:
type MyStructType struct {
Id int
Name string
Desc string
}
// we can some how get its fully qualified class name (this may require reflection?) or pass it into the UnMarshal method direction some how.
mst := "package.MyStructType",
// and then assume a byte array ba that has JSON format for
ba := []byte(`{"Id":"3","Name":"Jack","Desc":"the man"}`)
stct := UnmarshalFromJSONArray(ba,mst)
MyStructureType out := stct
// leaving "stct" being the unmarshalled byte array which can be used like any other struct of type "MyStructureType"
The key being that I never need to know what the fields of MyStructureType are before unmarshalling. All I need are the name of the struct and some way to instance one and then populate it with JSON byte array data that matches its fields. Hopefully that is possible (it is trivial in java using reflection). So I want to basically unmarshal an anonymous struct type by it's name without needing to know what fields it has.
Any suggestions?
The short answer is that this is impossible. There is no string to type translator in Go. You can make a map of strings to reflect.Type's, but you would need to know the possible options ahead of time or you need to provide the caller with a way to register types (perhaps in init).
Assuming you have found a way to resolve the string to its reflect.Type, you can simply call reflect.New(typ).Interface() to get the pointer you need to pass to json.Unmarshal().
The best answer is to avoid trying this all together. Writing idiomatic Java in Go isn't really possible. If I knew more about your problem, I could give you a more idiomatic Go solution.

Resources