【问题标题】:How to list unknown fields after json parsingjson解析后如何列出未知字段
【发布时间】:2019-07-28 11:40:16
【问题描述】:

假设我们有以下 Go 结构:

type Config struct {
    Name   string  `json:"name,omitempty"`
    Params []Param `json:"params,omitempty"`
}

type Param struct {
    Name  string `json:"name,omitempty"`
    Value string `json:"value,omitempty"`
}

和下面的json:

{
    "name": "parabolic",
    "subdir": "pb",
    "params": [{
        "name": "input",
        "value": "in.csv"
    }, {
        "name": "output",
        "value": "out.csv",
        "tune": "fine"
    }]
}

然后我们进行解组:

cfg := Config{}
if err := json.Unmarshal([]byte(cfgString), &cfg); err != nil {
    log.Fatalf("Error unmarshalling json: %v", err)
}
fmt.Println(cfg)

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

输出将是 {parabolic [{input in.csv} {output out.csv}]},这是有道理的 - 未知字段被忽略。

问题:如何找出哪些字段被忽略了?

getIgnoredFields(cfg, cfgString) 将返回 ["subdir", "params[1].tune"]

(有一个DisallowUnknownFields 选项,但它不同:此选项会导致Unmarshal 出错,而问题是如何仍然无错误地解析 json 并找出哪些字段被忽略)

【问题讨论】:

  • encoding/json 不提供此功能。
  • 可能还有另一种方式,如果你把结构体再编码成json,然后比较两个json,你就会发现不同。你可以使用https://github.com/yudai/gojsondiff
  • @Md.AlaminMahamud 对,我也想过这个问题,但是我们会得到像 {"one": "one", "two": "two"} 这样的 json,它会被反序列化为 struct,然后返回为 {"two":"two", "one": "one"}。会有差异,但没有帮助

标签: json go go-reflect


【解决方案1】:

不确定这是否是最好的方法,但我所做的是:

  1. 如果当前级别的类型是地图:

    1. 检查所有映射键是否已知。
      • 可能是键是结构字段名称或映射键。
      • 如果未知 - 添加到未知字段列表
    2. 递归地重复每个键对应的值
  2. 如果当前关卡类型是数组:

    1. 为每个元素递归运行

代码:

// ValidateUnknownFields checks that provided json
// matches provided struct. If that is not the case
// list of unknown fields is returned.
func ValidateUnknownFields(jsn []byte, strct interface{}) ([]string, error) {
    var obj interface{}
    err := json.Unmarshal(jsn, &obj)
    if err != nil {
        return nil, fmt.Errorf("error while unmarshaling json: %v", err)
    }
    return checkUnknownFields("", obj, reflect.ValueOf(strct)), nil
}

func checkUnknownFields(keyPref string, jsn interface{}, strct reflect.Value) []string {
    var uf []string
    switch concreteVal := jsn.(type) {
    case map[string]interface{}:
        // Iterate over map and check every value
        for field, val := range concreteVal {
            fullKey := fmt.Sprintf("%s.%s", keyPref, field)
            subStrct := getSubStruct(field, strct)
            if !subStrct.IsValid() {
                uf = append(uf, fullKey[1:])
            } else {
                subUf := checkUnknownFields(fullKey, val, subStrct)
                uf = append(uf, subUf...)
            }
        }
    case []interface{}:
        for i, val := range concreteVal {
            fullKey := fmt.Sprintf("%s[%v]", keyPref, i)
            subStrct := strct.Index(i)
            uf = append(uf, checkUnknownFields(fullKey, val, subStrct)...)
        }
    }
    return uf
}

完整版:https://github.com/yb172/json-unknown/blob/master/validator.go

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 1970-01-01
    • 1970-01-01
    • 2019-07-23
    • 2022-01-17
    • 1970-01-01
    • 2023-01-10
    相关资源
    最近更新 更多