Golang: Json from URL as map - json

What is the best way to extract json from a url i.e. Rest service from Go?
Also it seems most rest client libraries in go force a use of the json.marshall which needs a struct to be used with it.
This doesn't work in the case of unstructured data where you don't fully know what will be coming in. Is there a way to have it all simply come in as a map[string:string]?

Why not to parse it into map[string]string as this code have to do
var d map[string]interface{}
data, err := json.Unmarshal(apiResponse, &d)
You can traverse data in this struct too.
If you suspect, that api response can be not singular object, but the collection of objects, the interface{} also works for arrays.

If you don't know what's coming in a message, you can have several situations.
Message contents that depend on type
Type is usually indicated by some type field. In this case you can use a "union" struct that contains fields from all types:
type Foo struct {
A int
B string
}
type Bar struct {
C int
D string
}
type Message struct {
Type string
Foo
Bar
}
// or this, if you have common fields
type Message struct {
Type string
A int
B string
C int
D string
}
Unmarshal the message into the union struct, dispatch on type, and select the sub-struct.
var m Message
json.Unmarshal(data, &m)
switch m.Type {
case "foo":
...
case "bar":
...
}
Completely dynamic messages
In this case you have a collection of unrelated key-values and process them individually.
Parse into a map[string]interface{}. The downside, of course, is that you have to cast each value and check its type dynamically. Caveat: map[string]interface{} will convert all numbers to floats, even integers, so you have cast them to float64.
You can also use map[string]json.RawMessage, if you do not want to parse values, only keys (json.RawMessage is a []byte, and is preserved as is when unmarshaled).
"Envelope" message with dynamic payload
For example:
{
"type": "foo",
"payload": {
"key1": "value1"
}
}
{
"type": "bar",
"payload": {
"key2": "value2",
"key3": [1, 2]
}
}
Use a struct with json.RawMessage.
type Message struct {
Type string
Payload json.RawMessage
}
type Foo struct {
Key1 string
}
type Bar struct {
Key2 string
Key3 []int
}
Parse the envelope (payload will be preserved), then dispatch on type, and parse payload into a sub-struct.
var m Message
_ = json.Unmarshal(data, &m)
switch m.Type {
case "foo":
var payload Foo
_ = json.Unmarshal(m.Payload, &payload)
// do stuff
case "bar":
var payload Bar
_ = json.Unmarshal(m.Payload, &payload)
// do stuff
}

Related

How to set optional json in nest struct

I tried to set an optional json config in nest struct, when i need this json it will appear, otherwise it will not exist.
type Test struct {
Data NestTest `json:"data"`
}
type NestTest struct {
NestData1 string `json:"data1"`
NestData2 string `json:"data2,omitempty"`
}
test := Test{
Data: NestTest{
NestData1: "something",
},
}
b, err := json.Marshal(test)
fmt.Sprintf("the test struct json string is: %s", string(b))
output:
{"data":{"data1":"something","data2":""}}
expect:
{"data":{"data1":"something"}}
All fields are optional when unmarshalling (you won't get an error if a struct field doesn't have an associated value in the JSON). When marshaling, you can use omitempty to not output a field if it contains its type's zero value:
https://pkg.go.dev/encoding/json#Marshal
var Test struct {
Data string `json:"data,omitempty" validate:"option"`
}

Retrieve element from nested JSON string

This is the Go code that I have:
func main(){
s := string(`{"Id": "ABC123",
"Name": "Hello",
"RelatedItems":[
{"RId":"TEST123","RName":"TEST1","RChildren":"Ch1"},
{"RId":"TEST234","RName":"TEST2","RChildren":"Ch2"}]
}`)
var result map[string]interface{}
json.Unmarshal([]byte(s), &result)
fmt.Println("Id:", result["Id"])
Rlist := result["RelatedItems"].([]map[string]interface{})
for key, pist := range pist {
fmt.Println("Key: ", key)
fmt.Println("RID:", pist["RId"])
}
}
The struct is down below
type Model struct {
Id string `json:"Id"`
Name string `json:"ModelName"`
RelatedItems []RelatedItems `json:"RelatedItems"`
}
type RelatedItems struct {
RId string `json:"PCId"`
RName string `json:"PCName"`
RChildren string `json:"string"`
}
How would I get an output that would let me choose a particular field from the above?
eg:
Output
Id: ABC123
key:0
RID:TEST123
key:1
RID:TEST234
I am seeing this error
panic: interface conversion: interface {} is nil, not []map[string]interface {}
Based on the posted content,
I'm clear that you are facing issues retrieving data from the nested JSON string.
I've taken your piece of code and tried to compile and reproduce the issue.
After observing, I have a few suggestions based on the way the code has been written.
When the datatype present in the s is known to be similar to the type Model, the result could have been declared as type Model.
That results in var result Model instead of map[string]interface{}.
When the data that's gonna be decoded from the interface{} is not known, the usage of switch would come into rescue without crashing the code.
Something similar to:
switch dataType := result["RelatedItems"].(type){
case interface{}:
// Handle interface{}
case []map[string]interface{}:
// Handle []map[string]interface{}
default:
fmt.Println("Unexpected-Datatype", dataType)
// Handle Accordingly
When we try to Unmarshal, we make sure to look into the json tags that are provided for the fields of a structure. If the data encoded is not having the tags we provided, the data will not be decoded accordingly.
Hence, the result of decoding the data from s into result would result in {ABC123 [{ } { }]} as the tags of the fields Name, RId, RName, RChildren are given as ModelName, PCId, PCName, string respectively.
By the above suggestions and refining the tags given, the piece of code would be as following which would definitely retrieve data from nested JSON structures.
s := string(`{"Id": "ABC123",
"Name": "Hello",
"RelatedItems":[
{"RId":"TEST123","RName":"TEST1","RChildren":"Ch1"},
{"RId":"TEST234","RName":"TEST2","RChildren":"Ch2"}]
}`)
var result Model
json.Unmarshal([]byte(s), &result)
fmt.Println(result)
type Model struct {
Id string `json:"Id"`
Name string `json:"Name"`
RelatedItems []RelatedItems `json:"RelatedItems"`
}
type RelatedItems struct {
RId string `json:"RId"`
RName string `json:"RName"`
RChildren string `json:"RChildren"`
}
This results in the output: {ABC123 Hello [{TEST123 TEST1 Ch1} {TEST234 TEST2 Ch2}]}
Why would you unmarshal to a map anyway and go through type checks?
type Model struct {
Id string `json:"Id"`
Name string `json:"ModelName"`
RelatedItems []RelatedItems `json:"RelatedItems"`
}
type RelatedItems struct {
RId string `json:"PCId"`
RName string `json:"PCName"`
RChildren string `json:"string"`
}
s := `{"Id": "ABC123",
"Name": "Hello",
"RelatedItems":[
{"RId":"TEST123","RName":"TEST1","RChildren":"Ch1"},
{"RId":"TEST234","RName":"TEST2","RChildren":"Ch2"}]
}`
var result Model
if err := json.Unmarshal([]byte(s), &result); err != nil {
log.Fatal(err.Error())
}
fmt.Println("Id: ", result.Id)
for index, ri := range result.RelatedItems {
fmt.Printf("Key: %d\n", index)
fmt.Printf("RID: %s\n", ri.RId)
}

Golang json decoding fails on Array

I have an object like:
a = [{
"name": "rdj",
"place": "meh",
"meh" : ["bow", "blah"]
}]
I defined a struct like:
type first struct {
A []one
}
type one struct {
Place string `json: "place"`
Name string `json: "name"`
}
when I use the same in code like:
func main() {
res, _ := http.Get("http://127.0.0.1:8080/sample/")
defer res.Body.Close()
var some first
rd := json.NewDecoder(res.Body)
err := rd.Decode(&some)
errorme(err)
fmt.Printf("%+v\n", some)
}
I get the below error:
panic: json: cannot unmarshal array into Go value of type main.first
My understanding is:
type first defines the array and within that array is data structure defined in type one.
The JSON is an array of objects. Use these types:
type one struct { // Use struct for JSON object
Place string `json: "place"`
Name string `json: "name"`
}
...
var some []one // Use slice for JSON array
rd := json.NewDecoder(res.Body)
err := rd.Decode(&some)
The the types in the question match this JSON structure:
{"A": [{
"name": "rdj",
"place": "meh",
"meh" : ["bow", "blah"]
}]}
#rickydj,
If you need to have "first" as a separate type:
type first []one
If you do not care about having "first" as a separate type, just cut down to
var some []one
as #Mellow Marmot suggested above.

Accessing Nested Map of Type map[string]interface{} in Golang

So I'm trying to parse a JSON response. It can be multiple levels deep. This is what I did:
var result map[string]interface{}
json.Unmarshal(apiResponse, &result)
Firstly, is this the right way to do it?
Lets say the response was as follows:
{
"args": {
"foo": "bar"
}
}
To access key foo, I saw a playground doing this:
result["args"].(map[string]interface{})["foo"]
Here, what is the .() notation? Is this correct?
The notation x.(T) is called a Type Assertion.
For an expression x of interface type and a type T, the primary expression x.(T) asserts that x is not nil and that the value stored in x is of type T.
Your example:
result["args"].(map[string]interface{})["foo"]
It means that the value of your results map associated with key "args" is of type map[string]interface{} (another map with string keys and any values). And you want to access the element of that map associated with the key "foo".
If you know noting about the input JSON format, then yes, you have to use a generic map[string]interface{} type to process it. If you know the exact structure of the input JSON, you can create a struct to match the expected fields, and doing so you can unmarshal a JSON text into a value of your custom struct type, for example:
type Point struct {
Name string
X, Y int
}
func main() {
in := `{"Name":"center","X":2,"Y":3}`
pt := Point{}
json.Unmarshal([]byte(in), &pt)
fmt.Printf("Result: %+v", pt)
}
Output:
Result: {Name:center X:2 Y:3}
Try it on the Go Playground.
Modeling your input
Your current JSON input could be modelled with this type:
type Data struct {
Args struct {
Foo string
}
}
And accessing Foo (try it on the Go Playground):
d := Data{}
json.Unmarshal([]byte(in), &d)
fmt.Println("Foo:", d.Args.Foo)
struct is the best option, but if you insist, you can add a type declaration for a map, then you can add methods to help
with the type assertions:
package main
import "encoding/json"
type dict map[string]interface{}
func (d dict) d(k string) dict {
return d[k].(map[string]interface{})
}
func (d dict) s(k string) string {
return d[k].(string)
}
func main() {
apiResponse := []byte(`{"args": {"foo": "bar"}}`)
var result dict
json.Unmarshal(apiResponse, &result)
foo := result.d("args").s("foo")
println(foo == "bar")
}
https://golang.org/ref/spec#Type_declarations

Golang jsonable pointers to different structs in slice

My golang program have this structure of structs:
type JSONDoc struct {
Count int `json:"count"`
Objects []uintptr `json:"objects"`
}
type ObjectA struct {
FieldA string
}
type ObjectB struct {
FieldB string
}
I don't know what object types can be in JSONDoc.Objects, i'm need to store multiple structs in json array. Reflect returns pointers to structs, i'm appending them to struct, but encoding/json package in result json replace pointer with integer address.
Also unsafe.Pointer cannot be parsed by encoding/json too.
Just want result json to look as
{
"count":2,
"objects":
[
{"FieldA":"..."},
{"FieldB":"..."}
]
}
How can i store pointers to structs that will be correctly encoded to json?
You could use interface{}, for example :
type JSONDoc struct {
Count int `json:"count"`
Objects []interface{} `json:"objects"`
}
func main() {
doc := JSONDoc{Count: 2}
doc.Objects = append(doc.Objects, &ObjectA{"A"}, &ObjectB{"B"})
b, err := json.MarshalIndent(&doc, "", "\t")
fmt.Println(string(b), err)
}
playground