【问题标题】:handle unstructured JSON data using the standard Go unmarshaller使用标准 Go unmarshaller 处理非结构化 JSON 数据
【发布时间】:2021-09-28 12:19:18
【问题描述】:

在某些情况下,我正在设计一个将接收 JSON 发布数据的后端,但数据的本质是它具有非结构化的字段。我的一般研究告诉我这是一个静态语言与非结构化数据的问题。

通常,如果数据是众所周知的并且只是解组到结构中,则可以为其创建结构。我为嵌套对象创建了自定义解组函数。

现在的问题是其中一个字段可能包含具有任意数量键的对象。提供一些代码上下文:

properties: {
    "k1": "v1",
    "k2": "v2",
    "k3": "v3",
    ...
}



type Device struct {
    id: string,
    name: string,
    status: int,
    properties: <what would i put here?>
}

所以很难为它编写一个显式的解组函数。应该放一个map[string]string{}的类型?如果这些值不是所有字符串,它将如何工作?如果那个对象本身也有嵌套的值/对象呢?

【问题讨论】:

  • 使用map[string]interface{}。或者甚至只是interface{},如果数据可以包含除对象之外的 JSON 值,如数组或字符串、数字等原语。
  • var d Device err := json.Unmarshal([]byte(), &d) 这样有意义吗?
  • 这是将 json 解组为结构的常用方法,但我不确定这与您的原始问题有什么关系。请记住,为了能够将 json 编组到结构体中,必须导出字段。
  • 你也可以使用json.RawMessage类型。

标签: json go unmarshalling


【解决方案1】:

您可以将Properties 字段设置为map[string]interface{},以便它可以容纳不同类型的值。我为您的场景创建了一个小代码,如下所示:

package main

import (
    "encoding/json"
    "fmt"
)

type Device struct {
    Id         string
    Name       string
    Status     int
    Properties map[string]interface{}
}

func main() {
    devObj := Device{}
    data := []byte(`{"Id":"101","Name":"Harold","Status":1,"properties":{"key1":"val1"}}`)
    if err := json.Unmarshal(data, &devObj); err != nil {
        panic(err)
    }

    fmt.Println(devObj)

    devObj2 := Device{}
    data2 := []byte(`{"Id":"102","Name":"Thanor","Status":1,"properties":{"k1":25,"k2":"someData"}}`)
    if err := json.Unmarshal(data2, &devObj2); err != nil {
        panic(err)
    }

    fmt.Println(devObj2)

    devObj3 := Device{}
    data3 := []byte(`{"Id":"101","Name":"GreyBeard","Status":1,"properties":{"k1":25,"k2":["data1","data2"]}}`)
    if err := json.Unmarshal(data3, &devObj3); err != nil {
        panic(err)
    }
    fmt.Println(devObj3)

}

输出:

{101 Harold 1 map[key1:val1]}
{102 Thanor 1 map[k1:25 k2:someData]}
{101 GreyBeard 1 map[k1:25 k2:[data1 data2]]}

【讨论】:

    【解决方案2】:

    我会使用一种流行的 Go JSON 解析器,它不需要解析为预定义的结构。另一个好处,也是创建它们的主要原因,是它们比 encoding/json 快​​得多,因为它们不使用反射、interface{} 或某些其他方法。

    这里有两个:

    使用github.com/buger/jsonparser,可以使用GetString 函数检索属性:

    func GetString(data []byte, keys ...string) (val string, err error)
    

    这是一个完整的例子:

    package main
    
    import (
        "fmt"
        "strconv"
    
        "github.com/buger/jsonparser"
    )
    
    func main() {
        jsondata := []byte(`
        {
            "properties": {
                "k1": "v1",
                "k2": "v2",
                "k3": "v3"
            }
        }`)
    
        for i := 1; i > 0; i++ {
            key := "k" + strconv.Itoa(i)
            val, err := jsonparser.GetString(jsondata, "properties", key)
            
            if err == jsonparser.KeyPathNotFoundError {
                break
            } else if err != nil {
                panic(err)
            }
    
            fmt.Printf("found: key [%s] val [%s]\n", key, val)
        }
    }
    

    看它在 Go Playground 上运行:https://play.golang.org/p/ykAM4gac8zT

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-09
      • 2021-06-19
      • 1970-01-01
      • 2020-10-17
      • 1970-01-01
      • 2021-12-06
      • 1970-01-01
      相关资源
      最近更新 更多