4

I need to specify a type for decoding JSON data in a flexible manner, meaning the type needs to be specified at runtime.

Consider this snippet: http://play.golang.org/p/F-Jy4ufMPz

s := `{"b":[{"x":9},{"x":4}]}`

var a struct {
  B []interface{}
}
err := json.Unmarshal([]byte(s), &a)
if err != nil {
  panic(err)
}

fmt.Println(a)

Which will produce {[map[x:9] map[x:4]]}. I want to decode to an array of a specific (struct) type instead of []interface{}, without specifying it at compile time.

Is that somehow possible without creating the array up front? (the number of returned items is unknown)

The only way I can think of right now is to encode the returned maps again later, and decode them to the specified type, which would create unnecessary processing overhead.

3
  • Why not just decode to JSON objects and assign as appropriate? Commented Oct 30, 2013 at 19:09
  • The object is pretty large, and I want to avoid code duplication when the object's structure is already defined in a struct. Commented Oct 30, 2013 at 19:13
  • When/how will you know which type you should decode them into? Commented Oct 30, 2013 at 20:32

1 Answer 1

5

If not specifying it at compile time, you still need to specify it somewhere.

If specified before the retrieval of the Json data, you can simply do a switch case, Unmarshalling it to your desired object.

If specified within the Json data, you can marshal the "flexible" part into a json.RawMessage to process it after you've decided what type of struct is suitable:

package main

import (
    "encoding/json"
    "fmt"
)

var s = `{"type":"structx", "data":{"x":9,"xstring":"This is structX"}}`

type JsonStruct struct {
    Type string
    Data json.RawMessage
}

type StructX struct {
    X       float64
    Xstring string
}

type StructY struct {
    Y bool
}

func main() {
    var a *JsonStruct
    err := json.Unmarshal([]byte(s), &a)
    if err != nil {
        panic(err)
    }

    switch a.Type {
    case "structx":
        // We Unmashal the RawMessage part into a StructX
        var s *StructX
        json.Unmarshal([]byte(a.Data), &s)
        if err != nil {
            panic(err)
        }
        fmt.Println(s)
    case "structy":
        // Do the same but for structY
    }
}

Playground

Sign up to request clarification or add additional context in comments.

3 Comments

The problem with json.RawMessage is, that I cannot determine how many rows it contains. I guess I need to create a new slice with reflect.SliceOf first, then unpack the RawMessage to it?
I am not sure why you need reflection. If the number of items is important to determine the struct, you van always unmarshal it to a []json.RawMessage. The json.Unmarshal will fill the slice for you.
Yes, I was a little confused. That's what I ended up using. It was because my real data structure was a little more complex than what I posted (the custom type was embedded in an array anonymous struct fields).