patterngoMinor
Unmarshaling dynamic JSON
Viewed 0 times
jsonunmarshalingdynamic
Problem
I have JSON object response returned from the server which is similar to this one:
and I want to unmarshal it to the following structure:
where Attachment is described like this
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:
``
{
"id": 123,
"from_id": 234,
"attachments": [
{
"type": "audio",
"audi
{
"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.