golang json decode with field name - json

For following JSON
{
"Jon": {
"Age": 15
},
"Mary": {
"Age": 17
}
}
how can i map it into golang struct, normally, the structure will be
type Person struct {
Name string `json:??,string`
Age int `json:Age, int`
}
as the json field name is the attribute of struct, thank you in advance.

You have to use custom JSON marshalling
package main
import (
"encoding/json"
"log"
"fmt"
)
type Person struct {
Name string `json:??,string`
Age int `json:Age, int`
}
type People map[string]*Person
func (p *People) UnmarshalJSON(data []byte) error {
var transient = make(map[string]*Person)
err := json.Unmarshal(data, &transient)
if err != nil {
return err
}
for k, v := range transient {
v.Name = k
(*p)[k] = v
}
return nil
}
func main() {
jsonInput := `
{
"Jon": {
"Age": 15
},
"Mary": {
"Age": 17
}
}
`
var people People = make(map[string]*Person)
err := people.UnmarshalJSON([]byte(jsonInput))
if err != nil {
log.Fatal(err)
}
for _, person := range people {
fmt.Printf("%v -> %v\n", person.Name, person.Age)
}
}

Related

Custom UnmarshalJSON: array object to map

I have json files like this:
{
"Systems":[
{
"ID":74,
"Data1":0.1,
"Data2":4
},
{
"ID":50,
"Data1":31,
"Data2":3
}
],
"Error":false
}
I would like to unmarshal in Go to something like this (note map):
type Info struct {
Systems map[int]System `json:"Systems"` // key should be ID json field
Error bool `json:"Error"`
}
type System struct {
Data1 float32 `json:"Data1"`
Data2 int `json:"Data2"`
}
Here is my (wrong) code:
package main
import (
"encoding/json"
"fmt"
)
type Info struct {
Systems map[int]System `json:"Systems"` // key should be ID json field
Error bool `json:"Error"`
}
type System struct {
ID int `json:"ID"`
Data1 float32 `json:"Data"`
Data2 int `json:"Data2"`
}
func main() {
file := "{\"Systems\":[{\"ID\":74,\"Data1\":0.1,\"Data2\":4},{\"ID\":50,\"Data1\":31,\"Data2\":3}],\"Error\":true}"
info := Info{}
bytes := []byte(file)
err := json.Unmarshal(bytes, &info)
if err != nil {
fmt.Printf("=> %v\n", err)
}
fmt.Printf("INFO: %+v\n", info)
}
func (d *Info) UnmarshalJSON(buf []byte) error {
var tmp interface{}
if err := json.Unmarshal(buf, &tmp); err != nil {
return err
}
d.Error = tmp.(map[string]interface{})["Error"].(bool)
d.Systems = make(map[int]System)
for _, v := range tmp.(map[string]interface{})["Systems"].([]interface{}) {
d.Systems[v.(map[string]interface{})["ID"].(int)] = v.(map[string]interface{}).(System)
}
return nil
}
https://play.golang.org/p/L_Gx-f9ycjW
You can try this
package main
import (
"encoding/json"
"fmt"
)
func main() {
var str = `{
"Systems":[
{
"ID":74,
"Data1":0.1,
"Data2":4
},
{
"ID":50,
"Data1":31,
"Data2":3
}
],
"Error":false
}`
var t Info
err := json.Unmarshal([]byte(str), &t)
if err != nil {
panic(err)
}
bts, err := json.MarshalIndent(t, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(bts))
}
type Info struct {
Systems map[int]System `json:"Systems"`
Error bool `json:"Error"`
}
type System struct {
ID int `json:"ID,omitempty"`
Data1 float32 `json:"Data1"`
Data2 int `json:"Data2"`
}
func (info *Info) UnmarshalJSON(data []byte) error {
var t struct {
Systems []System `json:"Systems"`
Error bool `json:"Error"`
}
err := json.Unmarshal(data, &t)
if err != nil {
return err
}
info.Systems = make(map[int]System, 0)
for _, v := range t.Systems {
info.Systems[v.ID] = v
}
return nil
}
https://play.golang.org/p/qB3vF08cmW8
Output:
{
"Systems": {
"50": {
"ID": 50,
"Data1": 31,
"Data2": 3
},
"74": {
"ID": 74,
"Data1": 0.1,
"Data2": 4
}
},
"Error": false
}
You can't decode json directly in the struct you proposed because it doesn't match the json structure.
What you can do is decoding the json into this:
type Info struct {
Systems []*System `json:"Systems"` // array here
Error bool `json:"Error"`
Index map[int]*System // not json mapped
}
type System struct {
ID int `json:"ID"`
Data1 float32 `json:"Data1"`
Data2 int `json:"Data2"`
}
and populate the Index field in postprocessing with something like this:
var info Info
json.Unmarshal(dataIn, &info)
info.Index = map[int]*System{} // initialize an empty map
for _, s := range info.Systems {
info.Index[s.ID] = s
}
fmt.Println(info.Index[50].Data1)
you can find a full example here https://play.golang.org/p/B8O6nfI258-

How do I json unmarshal slice inside a slice

I am trying to unmarshal some pretty ugly json but can't figure out how. I have:
package main
import "fmt"
import "encoding/json"
type PublicKey struct {
ID int `json:"id"`
Key string `json:"key"`
MyData []struct {
ID string `json:"id"`
Value int `json:"value"`
}
}
func main() {
b := `[
{
"id": 1,
"key": "my_key"
},
[
{
"id": "some_id",
"value": 12
},
{
"id": "anorther_id",
"value": 13
}
]
]`
var pk []PublicKey
err := json.Unmarshal([]byte(b), &pk)
if err != nil {
fmt.Println(err)
}
fmt.Println(pk)
}
For the result I am getting:
[{1 my_key []} {0 []}]
The second slice is empty when it shouldn't be.
EDIT:
The error I get is:
json: cannot unmarshal array into Go struct field PublicKey.key of type main.PublicKey
https://play.golang.org/p/cztXOchiiS5
That is some truly hideous JSON! I have two approaches to handling the mixed array elements and I like the 2nd one better. Here's the first approach using interface and a type switch:
package main
import (
"encoding/json"
"errors"
"fmt"
)
type PublicKey struct {
ID int `json:"id"`
Key string `json:"key"`
}
type MyData struct {
ID string `json:"id"`
Value int `json:"value"`
}
type MixedData struct {
Key []PublicKey
MyData [][]MyData
}
func (md *MixedData) UnmarshalJSON(b []byte) error {
md.Key = []PublicKey{}
md.MyData = [][]MyData{}
var obj []interface{}
err := json.Unmarshal([]byte(b), &obj)
if err != nil {
return err
}
for _, o := range obj {
switch o.(type) {
case map[string]interface{}:
m := o.(map[string]interface{})
id, ok := m["id"].(float64)
if !ok {
return errors.New("public key id must be an int")
}
pk := PublicKey{}
pk.ID = int(id)
pk.Key, ok = m["key"].(string)
if !ok {
return errors.New("public key key must be a string")
}
md.Key = append(md.Key, pk)
case []interface{}:
a := o.([]interface{})
myData := make([]MyData, len(a))
for i, x := range a {
m, ok := x.(map[string]interface{})
if !ok {
return errors.New("data array contains unexpected object")
}
val, ok := m["value"].(float64)
if !ok {
return errors.New("data value must be an int")
}
myData[i].Value = int(val)
myData[i].ID, ok = m["id"].(string)
if !ok {
return errors.New("data id must be a string")
}
md.MyData = append(md.MyData, myData)
}
default:
// got something unexpected, handle somehow
}
}
return nil
}
func main() {
b := `[
{
"id": 1,
"key": "my_key"
},
[
{
"id": "some_id",
"value": 12
},
{
"id": "another_id",
"value": 13
}
]
]`
m := MixedData{}
err := json.Unmarshal([]byte(b), &m)
if err != nil {
fmt.Println(err)
}
fmt.Println(m)
}
https://play.golang.org/p/g8d_AsH-pYY
Hopefully there aren't any unexpected other elements, but they can be handled similarly.
Here is the second that relies more on Go's internal JSON parsing with the help of json.RawMessage. It makes the same assumptions about the contents of the array. It assumes that any objects will Unmarshal into PublicKey instances and any arrays consist of only MyData instances. I also added how to marshal back into the target JSON for symmetry:
package main
import (
"encoding/json"
"fmt"
"os"
)
type PublicKey struct {
ID int `json:"id"`
Key string `json:"key"`
}
type MyData struct {
ID string `json:"id"`
Value int `json:"value"`
}
type MixedData struct {
Keys []PublicKey
MyData [][]MyData
}
func (md *MixedData) UnmarshalJSON(b []byte) error {
md.Keys = []PublicKey{}
md.MyData = [][]MyData{}
obj := []json.RawMessage{}
err := json.Unmarshal([]byte(b), &obj)
if err != nil {
return err
}
for _, o := range obj {
switch o[0] {
case '{':
pk := PublicKey{}
err := json.Unmarshal(o, &pk)
if err != nil {
return err
}
md.Keys = append(md.Keys, pk)
case '[':
myData := []MyData{}
err := json.Unmarshal(o, &myData)
if err != nil {
return err
}
md.MyData = append(md.MyData, myData)
default:
// got something unexpected, handle somehow
}
}
return nil
}
func (md *MixedData) MarshalJSON() ([]byte, error) {
out := make([]interface{}, len(md.Keys)+len(md.MyData))
i := 0
for _, x := range md.Keys {
out[i] = x
i++
}
for _, x := range md.MyData {
out[i] = x
i++
}
return json.Marshal(out)
}
func main() {
b := `[
{
"id": 1,
"key": "my_key"
},
[
{
"id": "some_id",
"value": 12
},
{
"id": "another_id",
"value": 13
}
]
]`
m := MixedData{}
err := json.Unmarshal([]byte(b), &m)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(m)
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if err := enc.Encode(m); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
https://play.golang.org/p/ryZzaWKNcN0
Here's an approach that combines json.RawMessage with the trick of using the default unmarshaler in a type that implements json.Unmarshaler by creating a new temporary type that aliases the target type.
The idea is that we unmarshal the incoming array into a raw message and ensure that the array length is what we expect. Then we unmarshal the individual array elements into the custom struct types using their JSON tag annotations. The end result is that we can unmarshal the PublicKey type in the usual way and the UnmarshalJSON code is not terribly difficult to follow once you understand the tricks.
For example (Go Playground):
type PublicKey struct {
ID int `json:"id"`
Key string `json:"key"`
Data []MyData
}
type MyData struct {
ID string `json:"id"`
Value int `json:"value"`
}
func (pk *PublicKey) UnmarshalJSON(bs []byte) error {
// Unmarshal into a RawMessage so we can inspect the array length.
var rawMessage []json.RawMessage
err := json.Unmarshal(bs, &rawMessage)
if err != nil {
return err
}
if len(rawMessage) != 2 {
return fmt.Errorf("expected array of length 2, got %d", len(rawMessage))
}
// Parse the first object as PublicKey using the default unmarshaler
// using a temporary type that is an alias for the target type.
type PublicKey2 PublicKey
var pk2 PublicKey2
err = json.Unmarshal(rawMessage[0], &pk2)
if err != nil {
return err
}
// Parse the second object as []MyData in the usual way.
err = json.Unmarshal(rawMessage[1], &pk2.Data)
if err != nil {
return err
}
// Finally, assign the aliased object to the target object.
*pk = PublicKey(pk2)
return nil
}
func main() {
var pk PublicKey
err := json.Unmarshal([]byte(jsonstr), &pk)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", pk)
// main.PublicKey{ID:1, Key:"my_key", Data:[]main.MyData{main.MyData{ID:"some_id", Value:12}, main.MyData{ID:"anorther_id", Value:13}}}
}

Parse JSON HTTP response using golang

I am trying to get the value of say "ip" from my following curl output:
{
"type":"example",
"data":{
"name":"abc",
"labels":{
"key":"value"
}
},
"subsets":[
{
"addresses":[
{
"ip":"192.168.103.178"
}
],
"ports":[
{
"port":80
}
]
}
]
}
I have found many examples in the internet to parse json output of curl requests and I have written the following code, but that doesn't seem to return me the value of say "ip"
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
type svc struct {
Ip string `json:"ip"`
}
func main() {
url := "http://myurl.com"
testClient := http.Client{
Timeout: time.Second * 2, // Maximum of 2 secs
}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}
res, getErr := testClient.Do(req)
if getErr != nil {
log.Fatal(getErr)
}
body, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
log.Fatal(readErr)
}
svc1 := svc{}
jsonErr := json.Unmarshal(body, &svc1)
if jsonErr != nil {
log.Fatal(jsonErr)
}
fmt.Println(svc1.Ip)
}
I would appreciate if anyone could provide me hints on what I need to add to my code to get the value of say "ip".
You can create structs which reflect your json structure and then decode your json.
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
)
type Example struct {
Type string `json:"type,omitempty"`
Subsets []Subset `json:"subsets,omitempty"`
}
type Subset struct {
Addresses []Address `json:"addresses,omitempty"`
}
type Address struct {
IP string `json:"IP,omitempty"`
}
func main() {
m := []byte(`{"type":"example","data": {"name": "abc","labels": {"key": "value"}},"subsets": [{"addresses": [{"ip": "192.168.103.178"}],"ports": [{"port": 80}]}]}`)
r := bytes.NewReader(m)
decoder := json.NewDecoder(r)
val := &Example{}
err := decoder.Decode(val)
if err != nil {
log.Fatal(err)
}
// If you want to read a response body
// decoder := json.NewDecoder(res.Body)
// err := decoder.Decode(val)
// Subsets is a slice so you must loop over it
for _, s := range val.Subsets {
// within Subsets, address is also a slice
// then you can access each IP from type Address
for _, a := range s.Addresses {
fmt.Println(a.IP)
}
}
}
The output would be:
192.168.103.178
By decoding this to a struct, you can loop over any slice and not limit yourself to one IP
Example here:
https://play.golang.org/p/sWA9qBWljA
One approach is to unmarshal the JSON to a map, e.g. (assumes jsData contains JSON string)
obj := map[string]interface{}{}
if err := json.Unmarshal([]byte(jsData), &obj); err != nil {
log.Fatal(err)
}
Next, implement a function for searching the value associated with a key from the map recursively, e.g.
func find(obj interface{}, key string) (interface{}, bool) {
//if the argument is not a map, ignore it
mobj, ok := obj.(map[string]interface{})
if !ok {
return nil, false
}
for k, v := range mobj {
//key match, return value
if k == key {
return v, true
}
//if the value is a map, search recursively
if m, ok := v.(map[string]interface{}); ok {
if res, ok := find(m, key); ok {
return res, true
}
}
//if the value is an array, search recursively
//from each element
if va, ok := v.([]interface{}); ok {
for _, a := range va {
if res, ok := find(a, key); ok {
return res,true
}
}
}
}
//element not found
return nil,false
}
Note, that the above function return an interface{}. You need to convert it to appropriate type, e.g. using type switch:
if ip, ok := find(obj, "ip"); ok {
switch v := ip.(type) {
case string:
fmt.Printf("IP is a string -> %s\n", v)
case fmt.Stringer:
fmt.Printf("IP implements stringer interface -> %s\n", v.String())
case int:
default:
fmt.Printf("IP = %v, ok = %v\n", ip, ok)
}
}
A working example can be found at https://play.golang.org/p/O5NUi4J0iR
Typically in these situations you will see people describe all of these sub struct types. If you don't actually need to reuse the definition of any sub structs (like as a type for a function argument), then you don't need to define them. You can just use one definition for the whole response. In addition, in some cases you don't need to define a type at all, you can just do it at the time of declaration:
package main
import "encoding/json"
const s = `
{
"subsets": [
{
"addresses": [
{"ip": "192.168.103.178"}
]
}
]
}
`
func main() {
var svc struct {
Subsets []struct {
Addresses []struct { Ip string }
}
}
json.Unmarshal([]byte(s), &svc)
ip := svc.Subsets[0].Addresses[0].Ip
println(ip == "192.168.103.178")
}
You can write your own decoder or use existing third-party decoders.
For instance, github.com/buger/jsonparser could solve your problem by iterating throw array (two times).
package main
import (
"github.com/buger/jsonparser"
"fmt"
)
var data =[]byte(`{
"type":"example",
"data":{
"name":"abc",
"labels":{
"key":"value"
}
},
"subsets":[
{
"addresses":[
{
"ip":"192.168.103.178"
}
],
"ports":[
{
"port":80
}
]
}
]
}`)
func main() {
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
jsonparser.ArrayEach(value, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
v, _, _, err := jsonparser.Get(value, "ip")
if err != nil {
return
}
fmt.Println("ip: ", string(v[:]))
}, "addresses")
}, "subsets")
}
Output: ip: 192.168.103.178

Parsing a JSON file in Go

I am having trouble parsing a JSON file in Go.
I am not getting any errors, but I am not getting an output.
I have tried a few different methods, but I can't seem to get any to work.
Any help would be greatly appreciated. Thanks in advance.
package simplefiles
import (
"encoding/json"
"fmt"
"os"
)
//PluginInfo - struct for plugins.json
var PluginInfo struct {
LatestVersion string `json:"latest_version"`
LastUpdated string `json:"last_updated"`
Popular bool `json:"popular"`
Info []string `json:"Info"`
}
//ParsePlugins - parses plugins.json
func ParsePlugins() {
pluginsFile, err := os.Open("test.json")
if err != nil {
fmt.Println("opening config file", err.Error())
}
jsonParser := json.NewDecoder(pluginsFile)
if err = jsonParser.Decode(&PluginInfo); err != nil {
fmt.Println("parsing config file", err.Error())
} else {
fmt.Printf(PluginInfo.LastUpdated)
}
return
}
JSON Sample:
{
"my-test-site":{
"latest_version":"6.4.5",
"last_updated":"2016-05-22T00:23:00.000Z",
"popular":true,
"infomation":[
{
"id":6043,
"title":"Test info 1",
"created_at":"2014-08-01T10:58:35.000Z",
"updated_at":"2015-05-15T13:47:24.000Z",
"published_date":null,
"references":{
"url":[
"http://samplesite1.com",
"http://samplesite2.com"
]
},
"site_type":"info",
"fixed_v":"1.10"
}
]
},
"another-test-site":{
"latest_version":"2.1.0",
"last_updated":"2016-06-12T08:36:00.000Z",
"popular":false,
"infomation":[
{
"id":6044,
"title":"Test site 2 info",
"created_at":"2014-08-01T10:58:35.000Z",
"updated_at":"2015-05-15T13:47:24.000Z",
"published_date":null,
"references":{
"otherinfo":[
"blah blah blah"
]
},
"site_type":"search",
"fixed_v":"1.2.0"
}
]
}
}
Your problem is that your JSON data is a map of string to the struct type you defined, not the struct type directly. If you modify your code slightly as below it works, but you need to index into the map to get each struct value:
package main
import (
"encoding/json"
"fmt"
"os"
)
//PluginInfo - struct for plugins.json
var PluginInfo map[string]struct { // NOTICE map of string to struct
LatestVersion string `json:"latest_version"`
LastUpdated string `json:"last_updated"`
Popular bool `json:"popular"`
Info []string `json:"Info"`
}
//ParsePlugins - parses plugins.json
func ParsePlugins() {
pluginsFile, err := os.Open("test.json")
if err != nil {
fmt.Println("opening config file", err.Error())
}
jsonParser := json.NewDecoder(pluginsFile)
if err = jsonParser.Decode(&PluginInfo); err != nil {
fmt.Println("parsing config file", err.Error())
} else {
for key, val := range PluginInfo {
fmt.Printf("Key %q, last updated %s\n", key, val.LastUpdated)
}
}
return
}
func main() {
ParsePlugins()
}

anonymous fields in JSON

I'm reverse engineering some JSON that seems to be using anonymous field names. For example:
{
"1": 123,
"2": 234,
"3": 345
}
BTW - it's not simply using "1" and "2" and "3" because they represent userids that are at a minimum int32's.
Is there some way such as using tags to properly Unmarshal the JSON?
I've tried:
package main
import (
"encoding/json"
"fmt"
)
type MyStruct struct {
string `json:",string"`
}
func main() {
jsonData := []byte("{\"1\":123,\"2\":234,\"3\":345}")
var decoded MyStruct
err := json.Unmarshal(jsonData, &decoded)
if err != nil {
panic(err)
}
fmt.Printf("decoded=%+v\n", decoded)
}
Just decode the data into a map (map[string]int):
jsonData := []byte("{\"1\":123,\"2\":234,\"3\":345}")
var decoded map[string]int
err := json.Unmarshal(jsonData, &decoded)
if err != nil {
panic(err)
}
You'll then be able to iterate over and access the elements by the user ID key:
for userID, _ := range decoded {
fmt.Printf("User ID: %s\n", userID)
}
https://play.golang.org/p/SJkpahGzJY