skip to Main Content

I want to unmarshal a JSON object where one field contains a JSON string into one coherent object. How do I do that in Go?

Example:

Input:

{
  "foo":1,
  "bar":"{\"a\":\"Hello\"}"
}

Go type:

type Child struct {
    A string `json:"a"`
}

type Main struct {
    Foo int   `json:"foo"`
    Bar Child `json:"bar"`
}

I guess I’d need to implement a custom UnmarshalJSON implementation on one of the types, but its twisting my head to figure out on which one and how.

2

Answers


  1. I guess you want to treat this as if the JSON String were just part of the surrounding JSON object? If so, then yes, as you suggest, a custom UnmarshalJSON method on Child should accomplish this.

    func (c *Child) UnmarshalJSON(p []byte) error {
        var jsonString string
        if err := json.Unmarshal(p, &jsonString); err != nil {
            return err // Means the string was invalid
        }
        type C Child // A new type that doesn't have UnmarshalJSON method
        return json.Unmarshal([]byte(jsonString), (*C)(c))
    }
    

    See it in the playground

    Login or Signup to reply.
  2. if i were to create a custom UnmarshalJson for that data, I would create an auxiliary struct auxMain that has the same fields as the main struct but with Bar field as string. Then it unmarshals the JSON data into this auxiliary struct, extracting the Foo field and the Bar field as a string. After that, it unmarshals the Bar field as string into the Child struct, and assigns the extracted Foo field and the Child struct to the Main struct.

    It’s a round about way but seems to work in the playground.

    func (m *Main) UnmarshalJSON(b []byte) error {
        type auxMain struct {
            Foo int    `json:"foo"`
            Bar string `json:"bar"`
        }
        var a auxMain
        if err := json.Unmarshal(b, &a); err != nil {
            return err
        }
        var child Child
        if err := json.Unmarshal([]byte(a.Bar), &child); err != nil {
            return err
        }
        m.Foo = a.Foo
        m.Bar = child
        return nil
    }
    

    try it out in the PlayGround and see: https://go.dev/play/p/wWIceUxu1tj

    Don’t know if this is what you are looking for.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search