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的两种变体?
答案 0 :(得分:1)
将Foo.m
更改为map[string]Bar
而不是您拥有的内容,以便进行编译。要使其有效,您需要将Foo.m
更改为Foo.M
,将Bar.i
更改为Bar.I
。 Go JSON库不会对未导出的属性进行解组或编组。将大写属性映射到小写JSON元素需要使用标记。完整的工作示例here。
答案 1 :(得分:1)
答案 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断言的特定值时。
答案 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
可以处理多个子对象