解析嵌套的 JSON 数据而不知道其结构。

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

unmarshal nested json without knowing structure

问题

我正在使用键值存储作为我的Golang应用程序的后端,其中日期作为键(用于保持条目排序),JSON文档作为值。JSON文档中的顶级命名空间(foo)以及typedate在每个JSON文档中都存在,但其他方面存在一些差异(特别是关于一些嵌套的JSON数据),所以当我从数据库中提取数据时,我不知道在循环中每次提取的是什么。以下是JSON数据的示例:

{
  "foo": {
    "id": "124",
    "type": "baz",
    "rawdata": [
      123,
      345,
      345
    ],
    "epoch": "1433120656704"
  }
}

{
  "foo": {
    "id": "234",
    "type": "bar",
    "rawdata": [
      {
        "key": "dog",
        "values": [
          123,
          234
        ]
      },
      {
        "key": "cat",
        "values": [
          23,
          45
        ]
      }
    ],
    "epoch": "1433120656705"
  }
}

当我从数据库中提取数据时,我首先将每个条目解组为map[string]*json.RawMessage,以处理foo命名空间:

// 在循环中遍历数据库条目时
var objmap map[string]*json.RawMessage
if err := json.Unmarshal(dbvalue, &objmap); err != nil {
    return err
}

这是基于这个SO答案的方法。

然而,与该SO答案不同的是,当我必须再次解组foo命名空间下的内容时,我不知道要解组成哪个结构体:

if err := json.Unmarshal(*objmap["foo"], &bazorbar); err != nil {
    return err
}

type Baz struct {
    Id    string `json:"id"`
    Type  string `json:"type"`
    RawData []int  `json:"rawdata"`
    Epoch string `json:"epoch"`
}

type Bar struct {
    Id    string `json:"id"`
    Type  string `json:"type"`
    RawData []*Qux `json:"rawdata"`
    Epoch string `json:"epoch"`
}

// 嵌套在Bar中
type Qux struct {
    Key    string `json:"key"`
    Values []int  `json:"values"`
}

两个问题:

  1. 有没有办法避免重复解组(或者这是我不应该关心的事情)?
  2. 如何确定将json.RawMessage解组成哪个结构体(同时允许嵌套的JSON数据)?

更新: @chendesheng 提供的初始答案使我能够找出类型,但不能在确定类型后再次解组为结构体(这是我需要做的),所以根据对他/她答案评论中的对话,我对以下任一可能性感兴趣:

a) 复制json.RawMessage,按照您展示的方式解组为接口(通过chendesheng的答案),然后在确定类型后将副本解组为结构体?

b) 使用正则表达式确定类型,然后在确定类型后解组为该类型的结构体。

英文:

I am using a key value store as the backend for my golang application, with the date serving as the key (to keep entries sorted) and json documents as the values. The top level namespace of the json (foo) and the type and date are present in each json document that I'm storing but otherwise there are some differences (especially with respect to some nested json data), so when keyI'm pulling from the database, I don't really know what I'm pulling out at any time that I'm looping through . Here is a sample of the json data

{
  "foo": {
    "id": "124",
    "type": "baz",
    "rawdata": [
      123,
      345,
      345
    ],
    "epoch": "1433120656704"
  }
}

{
  "foo": {
    "id": "234",
    "type": "bar",
    "rawdata": [
      {
        "key": "dog",
        "values": [
          123,
          234
        ]
      },
      {
        "key": "cat",
        "values": [
          23,
          45
        ]
      }
    ],
    "epoch": "1433120656705"
  }
}


when I'm pulling from the database, the first thing I do is unmarshal each entry into a map[string]*json.RawMessage to deal with the foo namespace

//as I'm looping through the entries in the database
   var objmap map[string]*json.RawMessage
   if err := json.Unmarshal(dbvalue, &objmap); err !=nil{
       return err
   }

which I do thanks to this SO answer

However, unlike in that SO answer, when I have to unmarshal again whatever is contained under the foo namespace I don't know which struct to unmarshal into

   if err :=json.Unmarshal(*objmap["foo"], &bazorbar; err != nil{
         return err
   }

 type Baz struct{
  Id string `json:"id"`
  Type string `json:"type"`
  RawData []int `json:"rawdata"`
  Epoch string  `json:"epoch"`
}

type Bar struct{
  Id string `json:"id"`
  Type string `json:"type"`
  RawData []*Qux `json:"rawdata"`
  Epoch string  `json:"epoch"`
}
//nested inside Bar
type Qux struct{
  Key string `json:"key"`
  Values []int `json:"values`
}

Two part Question:

  1. Is there a way to avoid repeated unmarshals (or is that something I shouldn't even care about)
  2. how can I figure out which struct to unmarshal the json.RawMessage into (which also allows for nested json data)

Update: the initial answer provided by @chendesheng enables me to find out the type but not to unmarshal again into a struct once that type has been determined (which I need to do), so based on a conversation in the comments to his/her answer, I would be interested in either of these possibilities

a) make a copy of the json.RawMessage, unmarshal into the interface as you shown (by chendesheng's answer), and then unmarshal the copy itno the struct once you know the type (from having unmarshaled into the interface)?

b) use a regular expression to determine the type and then unmarshal into a struct of that type once it's known

答案1

得分: 11

两种检查结构类型的方法:

  1. 将json.RawMessage解组为map[string]interface{}。
  2. 使用正则表达式提取类型字符串。

http://play.golang.org/p/gfP6P4SmaC

英文:

Two ways to check struct type:

  1. Unmarshal json.RawMessage to a map[string]interface{}
  2. Use a regular expression to extract type string

http://play.golang.org/p/gfP6P4SmaC

huangapple
  • 本文由 发表于 2015年6月1日 20:47:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/30573899.html
匿名

发表评论

匿名网友

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

确定