I have some JSON and I need to be able to access the properties. As the JSON properties can vary I can't create a struct to unmarshal into.
Example
The JSON could be this:
{"name" : "John Doe", "email" : "john#doe.com"}
or this:
{"town" : "Somewhere", "email" : "john#doe.com"}
or anything else.
How can I access each of the properties?
You can unmarshal it into an interface{}. If you do that, json.Unmarshal will unmarshal a JSON object into a Go map.
For example:
var untypedResult interface{}
err := json.Unmarshal(..., &untypedResult)
result := untypedResult.(map[string]interface{})
// ... now you can iterate over the keys and values of result ...
See <http://blog.golang.org/json-and-go#TOC_5.> for a complete example.
If you just happen to have fields that may not be specified, you can unmarshal your input into a struct with pointer. If the field isn't present, the pointer will be nil.
package main
import (
"encoding/json"
"fmt"
)
type Foo struct {
A *string
B *string
C *int
}
func main() {
var input string = `{"A": "a","C": 3}`
var foo Foo
json.Unmarshal([]byte(input), &foo)
fmt.Printf("%#v\n", foo)
}
Playground
If you really want something more flexible, you can also unmarshal your input into a map[string]interface{}.
package main
import (
"encoding/json"
"fmt"
)
func main() {
var input string = `{"A": "a","C": 3}`
var foo map[string]interface{} = make(map[string]interface{})
json.Unmarshal([]byte(input), &foo)
fmt.Printf("%#v\n", foo)
}
Playground
When unmarshalling a JSON document, not all properties that are defined in the struct need to be present in the JSON document. In your example, you could define the following struct:
type MyJson struct {
Name string `json:"name"`
Town string `json:"town"`
Email string `json:"email"`
}
When your use this struct to unmarshal a JSON document that has one or more of these properties missing, they will be initialized with the respective type's null value (an empty string for string properties).
Alternatively, you can use the generic interface{} type for unmarshalling and then use type assertions. This is documented in-depth in the blog post "JSON and GO":
var jsonDocument interface{}
err := json.Unmarshal(jsonString, &jsonDocument)
map := jsonDocument.(map[string]interface{})
town := map["town"].(string);
Related
I have a small Go program like this:
package main
import "encoding/json"
func main() {
bs := []byte(`{"items": ["foo", "bar"]}`)
data := Data{}
err := json.Unmarshal(bs, &data)
if err != nil {
panic(err)
}
}
type Data struct {
Items []Raw `json:"items"`
}
// If I do this, this program panics with "illegal base64 data at input byte 0"
type Raw json.RawMessage
// This works
type Raw = json.RawMessage
Why does json.Unmarshal work with type alias but not type definition? What does that error message mean?
This is a new type definition:
type Raw json.RawMessage
The Raw type is derived from json.RawMessage, but it is a new type without any of the methods defined for json.RawMessage. So, if json.RawMessage has a json unmarshaler method, Raw does not have that. Code such as following will not recognize a variable t of type Raw as a json.RawMessage:
if t, ok:=item.(json.RawMessage); ok {
...
}
This is a type alias:
type Raw = json.RawMessage
Here, Raw has all the methods json.RawMessage has, and type assertion to json.RawMessage will work, because Raw is simply another name for json.RawMessage.
type Req struct {
apiVersion string
path string
resourceEndpoint string
accessKey string
log *logrus.Entry
incomingReq interface{}
httpClient lib.HTTPClient
redisClient redis.Cmdable
ctx context.Context
}
type TestReq struct {
Req
}
According to this this question and its answers, I feel like I should be able to do the following:
req := &Req{}
req = TestReq(req)
But I get this error in VsCode:
cannot convert req (variable of type *Req) to TestReq compiler(InvalidConversion)
Don't these two structs have the same underlying fields? If so, why can't the first be converted into the second?
Don't these two structs have the same underlying fields?
No, they don't. Req has several fields, TestReq has a single field of type Req so they are not convertible into each other. Embedding does not "copy" the fields of the embedded type to the embedder type. Embedding adds a single field which can be referred to by the unqualified type name of its type.
The use of embedding is not to automatically "copy" the fiels, but rather to have them "promoted", also promoting the methods of the embedded type.
If you have a value of type TestReq, you may use the unqualified type name Req to refer to the embedded field, so you may do something like this:
var tr TestReq
var r Req
r = tr.Req // Valid
tr.Req = r // This is also valid
The above operations (statements) are assignments and as such, they copy the whole Req struct value. If you want to avoid that, you may embed a pointer, for example:
type TestReq struct {
*Req
}
And then the following assignments will only copy a pointer value:
var tr = &TestReq{Req: &Req{}}
var r *Req
r = tr.Req // Valid
tr.Req = r // This is also valid
(Note: tr itself may or may not be a pointer here, it doesn't matter.)
Based on the suggestion icza, using the type name req to assign value to embedded field.Here is a simple code for the same , for simplicity I converted redis,logrus,context and http types as interface{}
package main
import (
"fmt"
)
type Req struct {
apiVersion string
path string
resourceEndpoint string
accessKey string
log interface{}
incomingReq interface{}
httpClient interface{}
redisClient interface{}
ctx interface{}
}
type TestReq struct {
Req
}
func main() {
req1 := Req{"api01", "c:/doc/folder", "endkey", "ackey", "logs", [2]float64{2.0, 7.88}, "http", "redis", "ctx"}
fmt.Println("ReqObject",req1)
var testReq TestReq
testReq.Req = req1
fmt.Println("TestReqObject",testReq)
}
Output:
ReqObject {api01 c:/doc/folder endkey ackey logs [2 7.88] http redis ctx}
TestReqObject {{api01 c:/doc/folder endkey ackey logs [2 7.88] http redis ctx}}
I'm trying to parse JSON using Go. Can anyone tell me why my code is not working as expected?
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
type Person struct {
M Message
}
func get_content() {
body := []byte(`{"person":{"Name":"Alice","Body":"Hello","Time":1294706395881547000}}`)
var data Person
err := json.Unmarshal(body, &data)
if err != nil {
panic(err.Error())
}
fmt.Printf("%v",data.M.Name)
}
func main() {
get_content()
}
I'm expecting it to print the Name.
Go playground Code
There are two problems in the code.
The first one is what #umar-hayat mentioned above -> you are unmarshalling into the data object and you should be aiming at the data.M field.
The second problem is that your JSON's structure doesn't match your struct's structure. Your Person has a single field called M. If we want to represent this as JSON it would look like this:
{
"M": {
"Name": "Joe",
"Body": "Hi",
"time": 2600
}
}
Instead, you have a field called person in your JSON which cannot be matched to any field in your struct. The fact that it's similar to the name of the struct's type doesn't help in any way, I'm afraid.
So, you can either change your JSON and your target:
body := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
var data Person
err := json.Unmarshal(body, &data.M)
Or just your JSON:
body := []byte(`{"M":{"Name":"Alice","Body":"Hello","Time":1294706395881547000}}`)
var data Person
err := json.Unmarshal(body, &data)
But it's essential that the names of the fields in your JSON match the names of the fields in your struct. Or, as mentioned by Konstantinos, you can use tags in order to specify particular names with which your struct's fields will be represented in the JSON.
You might find this helpful: https://gobyexample.com/json
Here is how to Unmarshel JSON to the struct. you can check it on Go Playground here:
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
type Person struct {
M Message
}
func get_content() {
body := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
var data Person
err := json.Unmarshal(body, &data.M)
if err != nil {
panic(err.Error())
}
fmt.Printf(data.M.Name)
}
func main() {
get_content()
}
Replace data with data.M in below line.
err := json.Unmarshal(body, &data)
As long as you intent to map Json keys on structs whose fields have different names you should add tags:
type Message struct {
Name string `json:"Name"`
Body string `json:"Body"`
Time int64 `json:"Time"`
}
type Person struct {
M Message `json:"person"`
}
You can find more information here
In addition this answer explains in an nutshell the purpose of tags in go.
I wants to assign a map to an interface where underlying value is of type map[string]interface.
type Data struct{
data interface{}
}
result := make(map[string]interface{})
data := Data{
data:result
}
details := make(map[string]interface{})
details["CreatedFor"] = "dfasfasdf"
details["OwnedBy"] = "fasfsad"
How can I insert details value to data interface inside Data struct ?
To be able to treat an interface as a map, you need to type check it as a map first.
I've modified your sample code a bit to make it clearer, with inline comments explaining what it does:
package main
import "fmt"
func main() {
// Data struct containing an interface field.
type Data struct {
internal interface{}
}
// Assign a map to the field.
type myMap map[string]interface{}
data := Data{
internal: make(myMap),
}
// Now, we want to access the field again, but as a map:
// check that it matches the type we want.
internalMap, ok := data.internal.(myMap)
if !ok {
panic("data.internal is not a map")
}
// Now what we have the correct type, we can treat it as a map.
internalMap["CreatedFor"] = "dfasfasdf"
internalMap["OwnedBy"] = "fasfsad"
// Print the overall struct.
fmt.Println(data)
}
This outputs:
{map[CreatedFor:dfasfasdf OwnedBy:fasfsad]}
I have a function that has a parameter with the type interface{}, something like:
func LoadTemplate(templateData interface{}) {
In my case, templateData is a struct, but each time it has a different structure. I used the type "interface{}" because it allows me to send all kind of data.
I'm using this templateData to send the data to the template:
err := tmpl.ExecuteTemplate(w, baseTemplateName, templateData)
But now I want to append some new data and I don't know how to do it because the "interface" type doesn't allow me to add/append anything.
I tried to convert the interface to a struct, but I don't know how to append data to a struct with an unknown structure.
If I use the following function I can see the interface's data:
templateData = appendAssetsToTemplateData(templateData)
func appendAssetsToTemplateData(t interface{}) interface{} {
switch reflect.TypeOf(t).Kind() {
case reflect.Struct:
fmt.Println("struct")
s := reflect.ValueOf(t)
fmt.Println(s)
//create a new struct based on current interface data
}
return t
}
Any idea how can I append a child to the initial interface parameter (templateData)? Or how can I transform it to a struct or something else in order to append the new child/data?
Adrian is correct. To take it a step further, you can only do anything with interfaces if you know the type that implements that interface. The empty interface, interface{} isn't really an "anything" value like is commonly misunderstood; it is just an interface that is immediately satisfied by all types.
Therefore, you can only get values from it or create a new "interface" with added values by knowing the type satisfying the empty interface before and after the addition.
The closest you can come to doing what you want, given the static typing, is by embedding the before type in the after type, so that everything can still be accessed at the root of the after type. The following illustrates this.
https://play.golang.org/p/JdF7Uevlqp
package main
import (
"fmt"
)
type Before struct {
m map[string]string
}
type After struct {
Before
s []string
}
func contrivedAfter(b interface{}) interface{} {
return After{b.(Before), []string{"new value"}}
}
func main() {
b := Before{map[string]string{"some": "value"}}
a := contrivedAfter(b).(After)
fmt.Println(a.m)
fmt.Println(a.s)
}
Additionally, since the data you are passing to the template does not require you to specify the type, you could use an anonymous struct to accomplish something very similar.
https://play.golang.org/p/3KUfHULR84
package main
import (
"fmt"
)
type Before struct {
m map[string]string
}
func contrivedAfter(b interface{}) interface{} {
return struct{
Before
s []string
}{b.(Before), []string{"new value"}}
}
func main() {
b := Before{map[string]string{"some": "value"}}
a := contrivedAfter(b)
fmt.Println(a)
}
You can't append data arbitrarily to a struct; they're statically typed. You can only assign values to the fields defined for that specific struct type. Your best bet is probably to use a map instead of structs for this.
Not recommended, but you can create structs dynamically using the reflect package.
Here is an example:
package main
import (
"encoding/json"
"os"
"reflect"
)
type S struct {
Name string
}
type D struct {
Pants bool
}
func main() {
a := Combine(&S{"Bob"}, &D{true})
json.NewEncoder(os.Stderr).Encode(a)
}
func Combine(v ...interface{}) interface{} {
f := make([]reflect.StructField, len(v))
for i, u := range v {
f[i].Type = reflect.TypeOf(u)
f[i].Anonymous = true
}
r := reflect.New(reflect.StructOf(f)).Elem()
for i, u := range v {
r.Field(i).Set(reflect.ValueOf(u))
}
return r.Addr().Interface()
}
You could use something like the Combine function above to shmush any number of structs together. Unfortunately, from the documentation:
StructOf currently does not generate wrapper methods for embedded fields. This limitation may be lifted in a future version.
So your created struct won't inherit methods from the embedded types. Still, maybe it does what you need.
If you are just looking to convert your interface to struct, use this method.
type Customer struct {
Name string `json:"name"`
}
func main() {
// create a customer, add it to DTO object and marshal it
receivedData := somefunc() //returns interface
//Attempt to unmarshall our customer
receivedCustomer := getCustomerFromDTO(receivedData)
fmt.Println(receivedCustomer)
}
func getCustomerFromDTO(data interface{}) Customer {
m := data.(map[string]interface{})
customer := Customer{}
if name, ok := m["name"].(string); ok {
customer.Name = name
}
return customer
}