Go语言 将缺少的字段解组为空Map

siotufzp  于 2023-03-06  发布在  Go
关注(0)|答案(1)|浏览(107)
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
}

如果我在没有aspects字段的情况下解组{"description": "desc","name": "name"},则Aspects将为空。
可以通过添加UnmarshalJSON函数来解决。
但是有两个问题,一个是如果一个strcut嵌入了FieldDefinition,比如

type PropertyDefinition struct {
    FieldDefinition
    SourceName string     `json:"sourceName"`
}

json.Unmarshal将调用FieldDefinition.UnmarshalJSON,而不修改SourceName
我也可以添加PropertyDefinition.UnmarshalJson,但它具有传染性。
另一个是tmp结构体重复了FieldDefinition,这是次要的,我可以忍受。
https://github.com/golang/go/issues/27589相关。

yiytaume

yiytaume1#

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
    }

    // TODO: handle map merge if f.Aspects is already populated
    *f = (FieldDefinition)(target)

    return nil
}

关于FieldDefinition.UnmarshalJSON暴露在嵌入它的结构体中的另一个问题,不幸的是,如果从标准库中使用encoding/json是一个要求,那么没有一个好的方法来解决这个问题。如果将它作为一个命名字段,而不是嵌入一个选项,那么这将不再是一个问题。
但是我不认为您需要定制的UnmarshalJSON来确保map在开始时总是非空的。
如果AspectsMap对它的使用者来说是只读的,那么go对nilMap的处理应该已经很好地适用于这个用例了,因为从nilMap中阅读一个键并不会出现异常--它只会表现得好像这个键不存在一样:

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
}

此外,如果使用者需要消除是否设置了AspectsMap的歧义,他们现在可以检查该struct字段是否为nil
所以这是假设AspectsMap是只读的。
如果消费者在JSON解码后还要更新AspectsMap,那么您可以1)期望消费者负责检查nil并在需要时示例化Map,或者2)提供一个helper方法,例如FieldDefinition.SetAspectField(string, string),它可以为消费者处理Map的示例化。
编辑:提到如果AspectMap已经非空,则在FieldDefinition.UnmarshalJSON中处理Map合并

相关问题