HiveBrain v1.2.0
Get Started
← Back to all entries
patterngoMinor

Unmarshaling dynamic JSON

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
jsonunmarshalingdynamic

Problem

I have JSON object response returned from the server which is similar to this one:

{
    "id": 123,
    "from_id": 234,
    "attachments": [
        {
            "type": "audio",
            "audio": {
                "artist": "Van Halen",
                "title": "Eruption"
            }
        },
        {
            "type": "photo",
            "photo": {
                "id": 123,
                "url": "http://someurl.com"
            }
        }
    ]
}


and I want to unmarshal it to the following structure:

type Response struct {
    Id          int `json:"id"`
    FromId      int `json:"from_id"`
    Attachments []Attachment
}


where Attachment is described like this

type Attachment struct {
    Type   string
    Fields map[string]interface{}
}


and Fields field is map that contains fields from "photo" or "audio" object of attachment in JSON.

Here is the code I've written to accomplish my task:

``
package main

import (
"encoding/json"
"fmt"
"log"
"reflect"
"strings"
"unicode"
)

type Response struct {
Id int
json:"id"
FromId int
json:"from_id"
Attachments []Attachment
}

type Attachment struct {
Type string
Fields map[string]interface{}
}

func (this *Attachment) UnmarshalJSON(b []byte) (err error) {
type innerAttachment struct {
Type string
Audio map[string]interface{}
Photo map[string]interface{}
}

inAt := innerAttachment{}
*this = Attachment{}

if err = json.Unmarshal(b, &inAt); err == nil {
this.Type = inAt.Type

a := []rune(inAt.Type)
a[0] = unicode.ToUpper(a[0])

v := reflect.ValueOf(inAt)

m := v.FieldByName(string(a)).Interface()
this.Fields = m.(map[string]interface{})
}

return
}

func main() {
encoded :=

{
"id": 123,
"from_id": 234,
"attachments": [
{
"type": "audio",
"audi

Solution

I would not implement method UnmarshalJSON as it is not really necessary. Instead, I would create Audio and Photo types and put them as fields into Attachment type. It would be easier to reference data as field than get it as a value from map.

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "strings"
)

type Response struct {
    Id          int `json:"id"`
    FromId      int `json:"from_id"`
    Attachments []Attachment
}

type Audio struct {
    Artist string `json:"artist"`
    Title  string `json:"title"`
}

type Photo struct {
    Id  int    `json:"id"`
    Url string `json:"url"`
}

type Attachment struct {
    Type  string
    Audio `json:"audio"`
    Photo `json:"photo"`
}

func main() {
    encoded := `
    {
        "id": 123,
        "from_id": 234,
        "attachments": [
            {
                "type": "audio",
                "audio": {
                    "artist": "Van Halen",
                    "title": "Eruption"
                }
            },
            {
                "type": "photo",
                "photo": {
                    "id": 123,
                    "url": "http://someurl.com"
                }
            }
        ]
    }
    `

    response := &Response{}
    reader := strings.NewReader(encoded)
    err := json.NewDecoder(reader).Decode(&response)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(response)
}

Code Snippets

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "strings"
)

type Response struct {
    Id          int `json:"id"`
    FromId      int `json:"from_id"`
    Attachments []Attachment
}

type Audio struct {
    Artist string `json:"artist"`
    Title  string `json:"title"`
}

type Photo struct {
    Id  int    `json:"id"`
    Url string `json:"url"`
}

type Attachment struct {
    Type  string
    Audio `json:"audio"`
    Photo `json:"photo"`
}

func main() {
    encoded := `
    {
        "id": 123,
        "from_id": 234,
        "attachments": [
            {
                "type": "audio",
                "audio": {
                    "artist": "Van Halen",
                    "title": "Eruption"
                }
            },
            {
                "type": "photo",
                "photo": {
                    "id": 123,
                    "url": "http://someurl.com"
                }
            }
        ]
    }
    `

    response := &Response{}
    reader := strings.NewReader(encoded)
    err := json.NewDecoder(reader).Decode(&response)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(response)
}

Context

StackExchange Code Review Q#68915, answer score: 6

Revisions (0)

No revisions yet.