解析具有多个值类型和任意数量键的 JSON

huangapple go评论119阅读模式
英文:

Unmarshall JSON with multiple value types and arbitrary number of keys

问题

我正在尝试读取以下形式的JSON:

  1. {
  2. string: int,
  3. string: string,
  4. string: MyStruct,
  5. string: MyStruct,
  6. ...
  7. string: MyStruct,
  8. }

例如:

  1. {
  2. "status": 200,
  3. "message": "一些很酷的文本",
  4. "coolKeyA": {
  5. "name": "尤达",
  6. "age": 900
  7. },
  8. "CoolKeyB": {
  9. "name": "马哈拉勒勒",
  10. "age": 895
  11. },
  12. "CoolKeyC": {
  13. "name": "王子",
  14. "age": 57
  15. },
  16. }

期望的结果是获得一个map[string]MyStruct的映射。其中"CoolKeyX"键的数量是弹性的或任意的,但其他键是静态的,例如status和message。

由于JSON中的值是不同类型的,我尝试将它们解析到一个空的map[string]interface{}中。然后目标是循环遍历键,并提取感兴趣的键,并将map[string]interface{string: string, string: int}的键转换为MyStruct。

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

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

我知道解组JSON的基本思想是创建一个与JSON类似的结构体并使用它,但由于我不知道确切的键的数量或"CoolKey"键实际上是什么(因为它是一个包含哈希值"000ab8f26d"的字符串),所以我不知道该怎么做。我知道接口有点像一个万能类型,但我不确定如何从中提取我想要的数据。

英文:

I'm attempting to read a JSON with the following form

  1. {
  2. string: int,
  3. string: string,
  4. string: MyStruct,
  5. string: MyStruct,
  6. ...
  7. string: MyStruct,
  8. }

For example

  1. {
  2. "status": 200,
  3. "message": "some cool text",
  4. "coolKeyA": {
  5. "name": "yoda",
  6. "age": 900
  7. },
  8. "CoolKeyB": {
  9. "name": "Mahalalel",
  10. "age": 895
  11. },
  12. "CoolKeyC": {
  13. "name": "Prince",
  14. "age": 57
  15. },
  16. }

The desired outcome is to get a map of map[string]MyStruct. There are an elastic or arbitrary number of "CoolKeyX" keys but the other keys are static, e.g., status and message.

Since the values in the JSON are different types I tried to reach them in to a blank map[string]interface{}. Then the goal is to loop through the keys and pluck out they keys of interest and convert the keys of map[string]inferface{string: string, string: int} to MyStruct.

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

Which gives me the following error: NonIndexableOperand: invalid operation: cannot index v (variable of type interface{})

I know the basic idea of unmarshalling JSONs is to create a struct that looks like the json and use that but since I don't know the exact number of keys or what the "CoolKey" key really is (because it's a string containing a hash "000ab8f26d") I didn't know how. I know interfaces are sort of a catch all but then I'm not sure how to pull my desired data out of it.

答案1

得分: 1

一种方法是实现一个自定义的json.Unmarshaler

  1. type Obj struct {
  2. Status int
  3. Message string
  4. CoolKeys map[string]Person
  5. }
  6. type Person struct {
  7. Name string
  8. Age int
  9. }
  1. func (o *Obj) UnmarshalJSON(data []byte) error {
  2. // 首先,将对象反序列化为原始 JSON 的映射
  3. var m map[string]json.RawMessage
  4. if err := json.Unmarshal(data, &m); err != nil {
  5. return err
  6. }
  7. // 接下来,反序列化状态和消息字段,以及不属于“CoolKey”组的任何其他字段
  8. if err := json.Unmarshal(m["status"], &o.Status); err != nil {
  9. return err
  10. }
  11. delete(m, "status") // 确保从映射中删除该字段
  12. if err := json.Unmarshal(m["message"], &o.Message); err != nil {
  13. return err
  14. }
  15. delete(m, "message") // 确保从映射中删除该字段
  16. // 最后,反序列化映射的其余内容
  17. o.CoolKeys = make(map[string]Person)
  18. for k, v := range m {
  19. var p Person
  20. if err := json.Unmarshal(v, &p); err != nil {
  21. return err
  22. }
  23. o.CoolKeys[k] = p
  24. }
  25. return nil
  26. }

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

英文:

One approach would be implement a custom json.Unmarshaler:

  1. type Obj struct {
  2. Status int
  3. Message string
  4. CoolKeys map[string]Person
  5. }
  6. type Person struct {
  7. Name string
  8. Age int
  9. }
  1. func (o *Obj) UnmarshalJSON(data []byte) error {
  2. // first, unmarshal the object into a map of raw json
  3. var m map[string]json.RawMessage
  4. if err := json.Unmarshal(data, &m); err != nil {
  5. return err
  6. }
  7. // next, unmarshal the status and message fields, and any
  8. // other fields that don't belong to the "CoolKey" group
  9. if err := json.Unmarshal(m["status"], &o.Status); err != nil {
  10. return err
  11. }
  12. delete(m, "status") // make sure to delete it from the map
  13. if err := json.Unmarshal(m["message"], &o.Message); err != nil {
  14. return err
  15. }
  16. delete(m, "message") // make sure to delete it from the map
  17. // finally, unmarshal the rest of the map's content
  18. o.CoolKeys = make(map[string]Person)
  19. for k, v := range m {
  20. var p Person
  21. if err := json.Unmarshal(v, &p); err != nil {
  22. return err
  23. }
  24. o.CoolKeys[k] = p
  25. }
  26. return nil
  27. }

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

huangapple
  • 本文由 发表于 2022年3月18日 01:31:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/71516691.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定