type FieldDefinition struct {
Name string `json:"name"`
Description string `json:"description"`
Aspects map[string]string `json:"aspects"`
}
func (f *FieldDefinition) UnmarshalJSON(b []byte) error {
var tmp struct {
Name string `json:"name"`
Description string `json:"description"`
Aspects map[string]string `json:"aspects"`
}
err := json.Unmarshal(b, &tmp)
if err != nil {
return err
}
if tmp.Aspects == nil {
tmp.Aspects = make(Aspects)
}
*f = tmp
return nil
}
如果我在没有
{"description": "desc","name": "name"}
字段的情况下解组aspects
,则 Aspects
将为零。
添加
UnmarshalJSON
功能即可解决
但是有两个问题。 一个是如果一个strcut嵌入
FieldDefinition
例如
type PropertyDefinition struct {
FieldDefinition
SourceName string `json:"sourceName"`
}
json.Unmarshal
会打电话给 FieldDefinition.UnmarshalJSON
并让 SourceName
保持不变。
我也可以加
PropertyDefinition.UnmarshalJson
,但是很有感染力
另一个是
tmp
结构体在重复FieldDefinition
,这是轻微的,我可以忍受。
tmp 结构重复 FieldDefinition,这是次要的,我可以忍受。
你不需要这样做。您可以在
FieldDefinition
上创建另一个类型,新类型将没有 UnmarshalJSON
方法,例如:
func (f *FieldDefinition) UnmarshalJSON(b []byte) error {
type newType FieldDefinition
var target newType
target.Aspects = make(map[string]string) // ensures map always non-nil
err := json.Unmarshal(b, &target)
if err != nil {
return err
}
*f = (FieldDefinition)(target)
return nil
}
对于您关于
FieldDefinition.UnmarshalJSON
暴露给嵌入它的结构的其他问题,不幸的是,如果使用来自 std lib 的 encoding/json
要求,则没有一个好的方法来解决这个问题。如果将它作为一个命名字段而不是嵌入一个选项,那么这将不再是一个问题。
但我认为您不需要自定义
UnmarshalJSON
来确保地图始终是非零的。
如果
Aspects
地图预计对它的消费者是只读的,那么 go 对 nil 地图的处理应该已经很好地适用于这个用例,因为从 nil 地图读取一个键不会恐慌 - 它只会表现得好像钥匙不在那里:
package main
import "fmt"
func main() {
var m map[string]string = nil
value, exists := m["foo"]
fmt.Println("value", value)
fmt.Println("exists", exists)
// prints
// value
// exists false
}
此外,如果消费者需要消除是否设置了
Aspects
映射的歧义,他们现在可以检查该结构字段是否为nil
。
所以这是假设
Aspects
地图只是只读的。
如果消费者也将在 JSON 解码后更新
Aspects
地图,那么您可以 1)期望消费者负责检查 nil 并在需要时实例化地图,或者 2)提供辅助方法,例如FieldDefinition.SetAspectField(string, string)
可以处理为消费者实例化地图。