解组 JSON 具有多种值类型和任意数量的键

Unmarshall JSON with multiple value types and arbitrary number of keys

我正在尝试阅读具有以下形式的 JSON

{
  string: int,
  string: string,
  string: MyStruct,
  string: MyStruct,
  ...
  string: MyStruct,
}

例如

{
  "status": 200,
  "message": "some cool text",
  "coolKeyA": {
    "name": "yoda",
    "age": 900
   },
  "CoolKeyB": {
    "name": "Mahalalel",
    "age": 895
   },
   "CoolKeyC": {
    "name": "Prince",
    "age": 57
   },
}

期望的结果是获得 map[string]MyStruct 的地图。有弹性或任意数量的“CoolKeyX”键,但其他键是静态的,例如状态和消息。

由于 JSON 中的值是不同的类型,我试图将它们放入空白 map[string]interface{}。然后目标是遍历键并取出它们感兴趣的键并将 map[string]inferface{string: string, string: int} 的键转换为 MyStruct.

scaryAcceptAll := map[string]interface{}{}
  if err = json.Unmarshal(byteArray, &scaryAcceptAll); err != nil { 
    log.Printf("error: %v", err)
    return err 
  } 
  
  for k,v := range scaryAcceptAll { 
    if (k == "val0" ) || (k == "val1") {
        continue
    }   
    desiredMap[k] = models.MyStruct{Name: v["name"], Age: v["age"]}
  }

这给了我以下错误:NonIndexableOperand: invalid operation: cannot index v (variable of type interface{})

我知道解组 JSONs 的基本思想是创建一个看起来像 json 的结构并使用它,但是因为我不知道键的确切数量或“CoolKey”键确实是(因为它是一个包含散列“000ab8f26d”的字符串)我不知道如何。我知道接口是一个包罗万象的东西,但我不确定如何从中提取我想要的数据。

一种方法是实施自定义 json.Unmarshaler:

type Obj struct {
    Status   int
    Message  string
    CoolKeys map[string]Person
}

type Person struct {
    Name string
    Age  int
}
func (o *Obj) UnmarshalJSON(data []byte) error {
    // first, unmarshal the object into a map of raw json
    var m map[string]json.RawMessage
    if err := json.Unmarshal(data, &m); err != nil {
        return err
    }

    // next, unmarshal the status and message fields, and any
    // other fields that don't belong to the "CoolKey" group
    if err := json.Unmarshal(m["status"], &o.Status); err != nil {
        return err
    }
    delete(m, "status") // make sure to delete it from the map
    if err := json.Unmarshal(m["message"], &o.Message); err != nil {
        return err
    }
    delete(m, "message") // make sure to delete it from the map

    // finally, unmarshal the rest of the map's content
    o.CoolKeys = make(map[string]Person)
    for k, v := range m {
        var p Person
        if err := json.Unmarshal(v, &p); err != nil {
            return err
        }
        o.CoolKeys[k] = p
    }
    return nil
}

https://go.dev/play/p/s4YCmve-pnz