简体   繁体   中英

json unmarshal embedded struct

I would like to unmarshal to struct Outer defined as:

type Outer struct {
    Inner
    Num int
}

type Inner struct {
    Data string
}
func (i *Inner) UnmarshalJSON(data []byte) error {
    i.Data = string(data)
    return nil
}

Using json.Unmarshal(data, &Outer{}) seems only to use Inner 's UnmarshalJSON and ignores the Num field: https://play.golang.org/p/WUBfzpheMl

I have an unwieldy solution where I set the Num field manually, but I was wondering if anybody had a cleaner or simpler way to do it.

Thanks!

This is happening because Inner is being embedded in Outer . That means when json library calls unmarshaler on Outer , it instead ends up calling it on Inner .

Therefore, inside func (i *Inner) UnmarshalJSON(data []byte) , the data argument contains the entire json string, which you are then processing for Inner only.

You can fix this by making Inner explicit field in Outer

Outer struct {
    I Inner // make Inner an explicit field
    Num int `json:"Num"`
}

Working example

You shouldn't need an unmarshalling function

https://play.golang.org/p/-HZwX5-rPD

Edit: Here is a more complete example

https://play.golang.org/p/oz5kiwy3_K

Just delete UnmarshalJSON in your example because it's used in unmarshaling of Outer since Inner is inlined. Otherwise, you need to override it if you want to do something custom.

https://play.golang.org/p/D6V6vKpx9J

One way to do this is to forgo the custom UnmarshalJSON function entirely and just use the basic JSON notation, ie:

type Outer struct {
    Inner
    Num int `json:"num"`
}

type Inner struct {
   Data string `json:"data"`
}

You lose some of the more granular capabilities you could have with a custom unmarshalling method, but when you're unmarshalling a struct with mostly primitive fields like strings, you really don't need to worry about that.

Example in go playground

If you really need custom unmarshalling, you could use composition, and give the struct a custom json encoding tag and have the struct contain the fields you want to play with. So if data is something that will contain multiple complex fields, you could just change Inner to reflect those fields, like this:

type Outer struct {
    Data Inner `json:"data"`
    Num int `json:"num"`
}

type Inner struct {
    Thing string `json:"thing"`
    OtherThing int `json:"otherThing"`
}

Example in go playground

Once again, this doesn't have a custom unmarshalling function, but that can be scraped together for Inner easily enough. (Personally, I'd forgo the use of custom unmarshal functions entirely in any given case and just use encoding tags unless I absolutely had to use the unmarshalling function.)

Faced same issue. One solution is to unmarshal twice for each sub structs. Eg

type Inner struct {
        Data string
}

type NumField struct {
        Num int
}

type Outer struct {
        Inner
        NumField
}

func (i *Inner) UnmarshalJSON(data []byte) error {
        i.Data = string(data)
        return nil
}

func (o *Outer) UnmarshalJSON(data []byte) error {
        if err := json.Unmarshal(data, &o.Inner); err != nil {
                return err
        }
        if err := json.Unmarshal(data, &o.NumField); err != nil {
                return err
        }
        return nil
}

func main() {
        x := Outer{}
        data := []byte(`{"Num": 4}`)
        _ = json.Unmarshal(data, &x)
        fmt.Printf("%#v\n", x)
}

This requires moving the extra fields into a separate struct.

Actually you not need explicit field, you need properly Marshal/UnMarshal

Example: https://play.golang.org/p/mWPM7m44wfK

package main

import (
    "encoding/json"
    "fmt"
)

type Outer struct {
    Inner
    Num int `json:"Num"`
}

type Inner struct{ Data string }

type InnerRaw struct {Data string}

func (i *Inner) UnmarshalJSON(data []byte) error {
    ir:=&InnerRaw{}
    json.Unmarshal(data, ir)
    i.Data = ir.Data
    return nil
}

func main() {
    x := Outer{}
    data := []byte(`{"Num": 4, "Data":"234"}`)
    _ = json.Unmarshal(data, &x)
    fmt.Printf("%+v\n", x)
    js, _:=json.Marshal(x)
    fmt.Printf("JSON:%s", string(js))
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM