使用标准的Go解组器处理非结构化的JSON数据。

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

handle unstructured JSON data using the standard Go unmarshaller

问题

一些背景信息,我正在设计一个后端,用于接收JSON的POST数据,但数据的特点是其中有一些字段是非结构化的。根据我的一般研究,这是一个静态语言与非结构化数据的问题。

通常,如果数据是已知的,你可以为其创建一个结构体,并将其解组为该结构体。我已经为嵌套对象创建了自定义的解组函数。

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

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

type Device struct {
    id: string,
    name: string,
    status: int,
    properties: <在这里应该放什么?>
}

所以很难为其编写一个显式的解组函数。应该使用map[string]string{}类型吗?如果值不全是字符串,它将如何工作?如果该对象本身也有嵌套值/对象,又该怎么办?

英文:

Some context, I'm designing a backend that will receive JSON post data, but the nature of the data is that it has fields that are unstructured. My general research tells me this is a static language vs unstructured data problem.

Normally if you can create a struct for it if the data is well known and just unmarshal into the struct. I have create custom unmarshaling functions for nested objects.

The issue now is that one of the fields could contain an object with an arbitrary number of keys. To provide some code context:

properties: {
    &quot;k1&quot;: &quot;v1&quot;,
    &quot;k2&quot;: &quot;v2&quot;,
    &quot;k3&quot;: &quot;v3&quot;,
    ...
}



type Device struct {
    id: string,
    name: string,
    status: int,
    properties: &lt;what would i put here?&gt;
}

So its hard to code an explicit unmarshaling function for it. Should put a type of map[string]string{}? How would it work if the values were not all strings then? And what if that object itself had nested values/objects as well?

答案1

得分: 3

你可以将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]]}
英文:

You can make the Properties field as map[string]interface{} so that it can accommodate different types of values.I created a small code for your scenario as follows:

package main

import (
	&quot;encoding/json&quot;
	&quot;fmt&quot;
)

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

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

	fmt.Println(devObj)

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

	fmt.Println(devObj2)

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

}

Output:

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

答案2

得分: 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

英文:

I would use one of the popular Go JSON parsers that don't require parsing to a pre-defined struct. An added benefit, and the primary reason they were created, is that they are much faster than encoding/json because they don't use reflection, interface{} or certain other approaches.

Here are two:

Using github.com/buger/jsonparser, a property could be retrieved using the GetString function:

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

Here's a full example:

package main

import (
	&quot;fmt&quot;
	&quot;strconv&quot;

	&quot;github.com/buger/jsonparser&quot;
)

func main() {
	jsondata := []byte(`
	{
		&quot;properties&quot;: {
			&quot;k1&quot;: &quot;v1&quot;,
			&quot;k2&quot;: &quot;v2&quot;,
			&quot;k3&quot;: &quot;v3&quot;
		}
	}`)

	for i := 1; i &gt; 0; i++ {
		key := &quot;k&quot; + strconv.Itoa(i)
		val, err := jsonparser.GetString(jsondata, &quot;properties&quot;, key)
		
        if err == jsonparser.KeyPathNotFoundError {
			break
		} else if err != nil {
			panic(err)
		}

		fmt.Printf(&quot;found: key [%s] val [%s]\n&quot;, key, val)
	}
}

See it run on Go Playground: https://play.golang.org/p/ykAM4gac8zT

huangapple
  • 本文由 发表于 2021年7月21日 12:52:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/68464147.html
匿名

发表评论

匿名网友

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

确定