Is it bad practice in Go to json.Decode a json object to an empty interface?

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

Is it bad practice in Go to json.Decode a json object to an empty interface?

问题

我有一个相当大的嵌套JSON对象需要解码。我可以将其解码为一个定义良好的嵌套结构,但我看到的另一种解决方案是将其解码为空接口。

从功能上讲,这样做是可以的。但我想知道在幕后当我从JSON解码对象时,以及稍后将其编组为JSON时,是否会产生性能损耗(反射)。

你有什么想法?提前感谢。

代码:

CustomizationData    interface{} `json:"customizationData" datastore:"-"`

对比:

CustomizationData    struct {
	Items []struct {
		ID     string `json:"id"`
		Images []struct {
			CustomizationState struct {
				Areas []struct {
					Height float64 `json:"height"`
					ID     string  `json:"id"`
					Left   float64 `json:"left"`
					Parent struct {
						Height float64 `json:"height"`
						Left   float64 `json:"left"`
						Top    float64 `json:"top"`
						Width  float64 `json:"width"`
					} `json:"parent"`
					Rotation float64 `json:"rotation"`
					Text     string  `json:"text"`
					Top      float64 `json:"top"`
					URL      string  `json:"url"`
					Width    float64 `json:"width"`
				} `json:"areas"`
				BackgroundColor string  `json:"backgroundColor"`
				IsUserSet       bool    `json:"isUserSet"`
				Orientation     float64 `json:"orientation"`
			} `json:"customizationState"`
			SpaceId string `json:"spaceId"`
		} `json:"images"`
		ProductId    float64 `json:"productId"`
		Quantity     float64 `json:"quantity"`
		Sku          string  `json:"sku"`
		TemplateName string  `json:"templateName"`
	} `json:"items"`
	ShippingAddress struct {
		City        string `json:"city"`
		CountryCode string `json:"countryCode"`
		Email       string `json:"email"`
		FirstName   string `json:"firstName"`
		LastName    string `json:"lastName"`
		Line1       string `json:"line1"`
		Phone       string `json:"phone"`
		PostalCode  string `json:"postalCode"`
		State       string `json:"state"`
	} `json:"shippingAddress"`
	TimeStamp string `json:"timeStamp"`
} `json:"customizationData" datastore:"-"`

可能还有更多的代码。

英文:

I have a fairly large nested JSON object I want to decode. I could decode this to a well defined nested struct, but an alternate solution I've seen is to just decode it to an empty interface.

Functionally, this works fine. But I'm wondering if behind the scenes I'm incurring a performance penalty (reflecting) when I decode the object from JSON and again when I later marshal it to JSON.

Thoughts? Thanks in advance.

Code:

CustomizationData    interface{} `json:"customizationData" datastore:"-"`

vs.

CustomizationData    struct {
	Items []struct {
		ID     string `json:"id"`
		Images []struct {
			CustomizationState struct {
				Areas []struct {
					Height float64 `json:"height"`
					ID     string  `json:"id"`
					Left   float64 `json:"left"`
					Parent struct {
						Height float64 `json:"height"`
						Left   float64 `json:"left"`
						Top    float64 `json:"top"`
						Width  float64 `json:"width"`
					} `json:"parent"`
					Rotation float64 `json:"rotation"`
					Text     string  `json:"text"`
					Top      float64 `json:"top"`
					URL      string  `json:"url"`
					Width    float64 `json:"width"`
				} `json:"areas"`
				BackgroundColor string  `json:"backgroundColor"`
				IsUserSet       bool    `json:"isUserSet"`
				Orientation     float64 `json:"orientation"`
			} `json:"customizationState"`
			SpaceId string `json:"spaceId"`
		} `json:"images"`
		ProductId    float64 `json:"productId"`
		Quantity     float64 `json:"quantity"`
		Sku          string  `json:"sku"`
		TemplateName string  `json:"templateName"`
	} `json:"items"`
	ShippingAddress struct {
		City        string `json:"city"`
		CountryCode string `json:"countryCode"`
		Email       string `json:"email"`
		FirstName   string `json:"firstName"`
		LastName    string `json:"lastName"`
		Line1       string `json:"line1"`
		Phone       string `json:"phone"`
		PostalCode  string `json:"postalCode"`
		State       string `json:"state"`
	} `json:"shippingAddress"`
	TimeStamp string `json:"timeStamp"`
} `json:"customizationData" datastore:"-"

And potentially more.

答案1

得分: 3

这完全取决于你对非解组数据的处理意图。

如果你的 JSON 数据中有嵌套的对象/数组,那么你将得到嵌套的接口。这意味着你需要显式地将接口转换为正确的类型以访问它们的数据。在这种情况下,最好使用第二个示例中的结构体,因为你可以更轻松地访问你的数据,例如 myData.Items[0].CustomizationState.Areas[0].Height。使用嵌套接口转换来做这个将会很麻烦。

另一方面,如果你只是将这些数据输出,例如作为对 Web 服务调用的响应,那么你不需要知道结构,只需将其原样输出即可。

就个人而言,我总是使用后者。

我假设你正在使用 http://mervine.net/json2struct 这个很棒的服务将你的 JSON 转换为可用的 Go 结构体。

这里有一个链接展示了两种方法之间访问数据的便捷性的差异。
http://play.golang.org/p/OlJJPZcxT7

对于那些想要保持在页面内的人:

var dataz = `{"foo": ["bar", "baz"], "boff": {"foo": "bar", "baz": "boff"}}`

type Dataz struct {
	Foo  []string `json:"foo"`
	Boff struct {
		Foo string `json:"foo"`
		Baz string `json:"baz"`
	} `json:"boff"`
}

func main() {
	// 方法1
	var d interface{}
	json.Unmarshal([]byte(dataz), &d)
	fmt.Println(d.(map[string]interface{})["foo"].([]interface{})[0])

	// 方法2
	var D Dataz
	json.Unmarshal([]byte(dataz), &D)
	fmt.Println(D.Foo[0])
}

编辑
根据关于性能的评论进行编辑

幸运的是,我们可以使用内置的 Go 工具进行测试

> go test -bench .
testing: warning: no tests to run
PASS
BenchmarkInterface	  300000	      6208 ns/op
BenchmarkStruct	      500000	      3622 ns/op
ok  	parse	3.773s

这是处理每秒 276,000 次解组或 161,000 次解组的差异。所以这几乎肯定不会成为你的瓶颈。:)

英文:

It depends entirely on what you intend on doing with the Unmarshalled data.

If you have nested objects / arrays in your json data, then you will end up with nested interfaces. That means you need to explicitly convert your interfaces to the correct type to access their data. In that case you are far better off using the struct in the second example as you will have your data more easily accessible as in myData.Items[0].CustomizationState.Areas[0].Height. Doing that with nested interface conversion is going to be a pain.

On the other hand, if you are just outputting this data, for example as a response to a webservice call, then you don't need to know the structure and can just spit it back out.

Personally, I always use the latter.

I assume you are using the awesome service at http://mervine.net/json2struct to convert your json into usable Go structs.

Here is a link showing the difference in ease of access between the two methods.
http://play.golang.org/p/OlJJPZcxT7

And for those who want to stay in-page:

var dataz = `{"foo": ["bar", "baz"], "boff": {"foo": "bar", "baz": "boff"}}`

type Dataz struct {
	Foo  []string `json:"foo"`
	Boff struct {
		Foo string `json:"foo"`
		Baz string `json:"baz"`
	} `json:"boff"`
}

func main() {
	// Method 1
	var d interface{}
	json.Unmarshal([]byte(dataz), &d)
	fmt.Println(d.(map[string]interface{})["foo"].([]interface{})[0])

	// Method 2
	var D Dataz
	json.Unmarshal([]byte(dataz), &D)
	fmt.Println(D.Foo[0])
}

EDIT
Edit based on comment about performance

Thankfully we can test it with built-in Go tools

> go test -bench .
testing: warning: no tests to run
PASS
BenchmarkInterface	  300000	      6208 ns/op
BenchmarkStruct	      500000	      3622 ns/op
ok  	parse	3.773s

It's the difference between handling unmarshalling of 276,000/sec or 161,000/sec. So this will almost certainly not be your bottleneck. Is it bad practice in Go to json.Decode a json object to an empty interface?

huangapple
  • 本文由 发表于 2015年2月19日 01:57:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/28590538.html
匿名

发表评论

匿名网友

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

确定