将map [string] interface {}反序列化为具体的地图类型

时间:2017-11-21 04:06:29

标签: json go

type Foo struct {
    M map[string]interface{} `json:"m"`
}

type Bar struct {
    I int `json:"i"`
}

type Bar2 struct {
    S string `json:"s"`
}


func do() {
    concreteFoo1 := Foo {
        M: make(map[string]Bar),
    }
    json.Unmarshal([]byte(`{"m": {"a": { "i": 1 }}}`), &concreteFoo1)

    concreteFoo2 := Foo {
        M: make(map[string]Bar2),
    }

    json.Unmarshal([]byte(`{"m": {"a": { "s": "hello" }}}`), &concreteFoo2)

}

无法编译:

  

不能使用make(map [string] Bar)(类型map [string] Bar)作为类型   在字段值

中映射[string] interface {}      

不能使用make(map [string] Bar2)(类型map [string] Bar2)作为类型   在字段值

中映射[string] interface {}

如何编译并支持Foo的两种变体?

5 个答案:

答案 0 :(得分:1)

Foo.m更改为map[string]Bar而不是您拥有的内容,以便进行编译。要使其有效,您需要将Foo.m更改为Foo.M,将Bar.i更改为Bar.I。 Go JSON库不会对未导出的属性进行解组或编组。将大写属性映射到小写JSON元素需要使用标记。完整的工作示例here

答案 1 :(得分:1)

更改此行: M map [string] interface {} json:"m"

进入: M界面{} json:"m"

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

答案 2 :(得分:1)

如果可能的Bar类型集合无限,则应使用Stud's answer中的解决方案。在解组之后,您只需要执行一个类型断言来获取map,这比对map[string]interface{}类型的每个值进行类型断言要好得多。

但是,如果该集仅限于几种类型,则可以创建一个父Bar类型,其中嵌入了所有可能的Bar变体的列表。如果嵌入类型的字段可能会发生冲突,您仍然可以使用嵌入类型实现json.Unmarshaler接口,并对要解组的内容执行一些自定义逻辑。

E.g。像这样的东西:

type BarSet struct {
    *Bar
    *Bar2
}

type Bar struct {
    I int `json:"i"`
}

type Bar2 struct {
    S string `json:"s"`
}

https://play.golang.org/p/tJfqtnP-CX

如果你想尽可能严格,你应该创建单独的Foo类型,每个类型都带有distincitve M map[string]...字段。

答案 3 :(得分:0)

您可以制作通用界面

type Foo struct {
    M map[string]Ibar `json:"m"`
}

type Ibar interface{}

然后打开包装。

当你需要变量make type断言的特定值时。

https://play.golang.org/p/fcs-Yp-ck2

答案 4 :(得分:0)

我再次看了一遍,认为自定义解组器可能是不错的选择。

func (f *Foo) UnmarshalJSON(b []byte) error {
    var tmp struct {
        M map[string]json.RawMessage `json:"m"`
    }
    var foo = Foo{
        M: make(map[string]interface{}),
    }
    var err error
    //for key, rawValue := range
    err = json.Unmarshal(b, &tmp)
    if err != nil {
        return err
    }
    for key, rawValue := range tmp.M {
        var bar Bar
        if json.Unmarshal(rawValue, &bar) == nil && bar.I != 0 { // custom check
            foo.M[key] = bar
            continue
        }
        var bar2 Bar2
        if json.Unmarshal(rawValue, &bar2) == nil && bar2.S != "" { // custom check
            foo.M[key] = bar2
            continue
        }
    }
    *f = foo
    return nil
}

优点:

  • 逻辑分析

  • 标准通话UnmarshalJSON

  • 可以处理多个子对象

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