灵活类型的JSON解码

4
我需要以一种灵活的方式指定解析JSON数据的类型,这意味着类型需要在运行时指定。
考虑以下片段: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)

这将产生{[map[x:9] map[x:4]]}。我想解码为一个特定(结构)类型的数组,而不是[]interface{},而不需要在编译时指定它。

如果不事先创建数组,是否有可能实现这一点?(返回的项数是未知的)

我现在唯一能想到的方法是稍后再次对返回的映射进行编码,并将它们解码为指定的类型,这将创建不必要的处理开销。


为什么不直接解码为JSON对象并适当赋值呢? - Hot Licks
这个对象相当大,我想避免在已经定义了结构体的情况下重复编写代码。 - user187676
你将如何知道应该将它们解码成哪种类型? - ANisus
1个回答

5

如果在编译时没有指定它,你仍然需要在某个地方指定它。

如果在检索Json数据之前指定,您可以简单地使用switch case将其取消标记为所需的对象。

如果在Json数据中指定,则可以将“灵活”部分封送成json.RawMessage,以便在确定适当的结构类型后进行处理:

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


json.RawMessage 的问题在于,我无法确定它包含多少行。我猜我需要先用 reflect.SliceOf 创建一个新的 slice,然后将 RawMessage 解压到其中? - user187676
我不确定你为什么需要反射。如果项目数量对于确定结构很重要,你可以将其解组为[]json.RawMessage。json.Unmarshal会为您填充切片。 - ANisus
是的,我有点困惑。那就是我最终使用的方法。因为我的真实数据结构比我发布的要复杂一些(自定义类型嵌入在数组匿名结构字段中)。 - user187676

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接