如何将具有动态字段的JSON对象映射到Go结构体中

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

How to map JSON objects with dynamic fields to Go structs

问题

我正在使用Go开发一个网站,并将其连接到Elastic Search。在Elastic Search中,我可以为索引类型设置动态字段。当我从Elastic Search中读取文档时,它将返回一个JSON对象作为结果,其中可能包含具有动态名称(或用户定义字段)的字段。

我可以获取JSON结果并将其解组为Go结构体,但我不知道将这些动态字段保留为Go结构体的最佳方法是什么。

这是我正在做的。例如,如果我从Elastic Search获取一个联系人文档,它可能如下所示:

  1. {
  2. "EmailAddress": "test@test.com",
  3. "Name": "Test Contact",
  4. "Phone": "17894785236",
  5. "City": "San Francisco",
  6. "State": "California"
  7. }

而Contact的Go结构体如下所示:

  1. type Contact struct {
  2. EmailAddress string
  3. Name string
  4. Phone string
  5. CustomFields map[string]interface{}
  6. }

我实现了MarshalerUnmarshaler来重写对象的编组和解组方式。

  1. func (c *Contact) MarshalJSON() ([]byte, error) {
  2. contactMap := make(map[string]interface{})
  3. contactMap["EmailAddress"] = c.EmailAddress
  4. contactMap["Name"] = c.Name
  5. contactMap["Phone"] = c.Phone
  6. for k, v := range c.CustomFields {
  7. contactMap[k] = v
  8. }
  9. return json.Marshal(contactMap)
  10. }
  11. func (c *Contact) UnmarshalJSON(data []byte) error {
  12. var contactMap map[string]interface{}
  13. if c == nil {
  14. return errors.New("RawString: UnmarshalJSON on nil pointer")
  15. }
  16. if err := json.Unmarshal(data, &contactMap); err != nil {
  17. return err
  18. }
  19. c.EmailAddress = contactMap["EmailAddress"].(string)
  20. c.Name = contactMap["Name"].(string)
  21. c.Phone = contactMap["Phone"].(string)
  22. for key, val := range contactMap {
  23. if key != "EmailAddress" && key != "Name" && key != "Phone" {
  24. c.CustomFields[key] = value
  25. }
  26. }
  27. return nil
  28. }

这是最佳的方法吗?你有什么建议?

英文:

I am developing a website using Go and connecting it to Elastic Search. In Elastic Search I can have dynamic fields for index types. When I read a document from Elastic Search it will return a JSON object as the result, which can include fields with dynamic names (or user defined fields).

I can get the JSON result and unmarshal it into a Go struct, but I do not know what is the best way to keep those dynamic fields as part of the Go struct.

This is what I am doing. For example, if I get a document for a Contact from Elastic Search it may look something like this:

  1. {
  2. "EmailAddress": "test@test.com",
  3. "Name": "Test Contact",
  4. "Phone": "17894785236",
  5. "City": "San Francisco",
  6. "State": "California"
  7. }

And the Go struct for Contact is:

  1. type Contact struct {
  2. EmailAddress string
  3. Name string
  4. Phone string
  5. CustomFields map[string]interface{}
  6. }

And I implement Marshaler and Unmarshaler to override how the object is Marshaled and Unmarshalled.

  1. func (c *Contact) MarshalJSON() ([]byte, error) {
  2. contactMap := make(map[string]interface{})
  3. contactMap["EmailAddress"] = c.EmailAddress
  4. contactMap["Name"] = c.Name
  5. contactMap["Phone"] = c.Phone
  6. for k, v := range c.CustomFields {
  7. contactMap[k] = v
  8. }
  9. return json.Marshal(contactMap)
  10. }
  11. func (c *Contact) UnmarshalJSON(data []byte) error {
  12. var contactMap map[string]interface{}
  13. if c == nil {
  14. return errors.New("RawString: UnmarshalJSON on nil pointer")
  15. }
  16. if err := json.Unmarshal(data, &contactMap); err != nil {
  17. return err
  18. }
  19. c.EmailAddress = contactMap["EmailAddress"].(string)
  20. c.Name = contactMap["Name"].(string)
  21. c.Phone = contactMap["Phone"].(string)
  22. for key, val := range contactMap {
  23. if key != "EmailAddress" && key != "Name" && Key != "Phone" {
  24. c.CustomFields[key] = value
  25. }
  26. }
  27. return nil
  28. }

Is this the best way to do this? What would you recommend?

答案1

得分: 5

只需进行一些小的清理

  1. var contactMap map[string]interface{}
  2. if c == nil {
  3. return errors.New("RawString: UnmarshalJSON on nil pointer")
  4. }
  5. if err := json.Unmarshal(data, &contactMap); err != nil {
  6. return err
  7. }
  8. for key, val := range contactMap {
  9. switch key {
  10. case "EmailAddress":
  11. c.EmailAddress = val.(string)
  12. case "Name":
  13. c.Name = val.(string)
  14. case "Phone":
  15. c.Phone = val.(string)
  16. default:
  17. c.CustomFields[key] = val
  18. }
  19. }
  20. }
英文:

Just add little cleanup

  1. var contactMap map[string]interface{}
  2. if c == nil {
  3. return errors.New("RawString: UnmarshalJSON on nil pointer")
  4. }
  5. if err := json.Unmarshal(data, &contactMap); err != nil {
  6. return err
  7. }
  8. for key, val := range contactMap {
  9. switch key {
  10. case "EmailAddress":
  11. c.EmailAddress = val.(string)
  12. case "Name":
  13. c.Name = val.(string)
  14. case "Phone":
  15. c.Phone = val.(string)
  16. default:
  17. c.CustomFields[key] = val
  18. }
  19. }
  20. }

答案2

得分: 4

正如Simon在评论中指出的那样,如果JSON的结构是固定的,使用一个大的**map[string]interface{}**并不理想。最好的方法是使用结构体并使用http://golang.org/pkg/encoding/json/#Unmarshal进行解组(参考示例:http://play.golang.org/p/cDTe8x4xLk)。

但是对于结构未知的大型JSON数据块,您的实现完全有效。

编辑:添加了示例链接

英文:

As Simon has pointed out in comment, using one big map[string]interface{} isn't ideal if the structure of the json is fixed. Best way then is to use structure and unmarshall it using http://golang.org/pkg/encoding/json/#Unmarshal. (refer the example: http://play.golang.org/p/cDTe8x4xLk)

But for large json blob, for which structure is not known beforehand, your implementation works perfectly.

edit: added link to example

huangapple
  • 本文由 发表于 2014年12月16日 04:49:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/27492888.html
匿名

发表评论

匿名网友

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

确定