英文:
How to map JSON objects with dynamic fields to Go structs
问题
我正在使用Go开发一个网站,并将其连接到Elastic Search。在Elastic Search中,我可以为索引类型设置动态字段。当我从Elastic Search中读取文档时,它将返回一个JSON对象作为结果,其中可能包含具有动态名称(或用户定义字段)的字段。
我可以获取JSON结果并将其解组为Go结构体,但我不知道将这些动态字段保留为Go结构体的最佳方法是什么。
这是我正在做的。例如,如果我从Elastic Search获取一个联系人文档,它可能如下所示:
{
"EmailAddress": "test@test.com",
"Name": "Test Contact",
"Phone": "17894785236",
"City": "San Francisco",
"State": "California"
}
而Contact的Go结构体如下所示:
type Contact struct {
EmailAddress string
Name string
Phone string
CustomFields map[string]interface{}
}
我实现了Marshaler和Unmarshaler来重写对象的编组和解组方式。
func (c *Contact) MarshalJSON() ([]byte, error) {
contactMap := make(map[string]interface{})
contactMap["EmailAddress"] = c.EmailAddress
contactMap["Name"] = c.Name
contactMap["Phone"] = c.Phone
for k, v := range c.CustomFields {
contactMap[k] = v
}
return json.Marshal(contactMap)
}
func (c *Contact) UnmarshalJSON(data []byte) error {
var contactMap map[string]interface{}
if c == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
if err := json.Unmarshal(data, &contactMap); err != nil {
return err
}
c.EmailAddress = contactMap["EmailAddress"].(string)
c.Name = contactMap["Name"].(string)
c.Phone = contactMap["Phone"].(string)
for key, val := range contactMap {
if key != "EmailAddress" && key != "Name" && key != "Phone" {
c.CustomFields[key] = value
}
}
return nil
}
这是最佳的方法吗?你有什么建议?
英文:
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:
{
"EmailAddress": "test@test.com",
"Name": "Test Contact",
"Phone": "17894785236",
"City": "San Francisco",
"State": "California"
}
And the Go struct for Contact is:
type Contact struct {
EmailAddress string
Name string
Phone string
CustomFields map[string]interface{}
}
And I implement Marshaler and Unmarshaler to override how the object is Marshaled and Unmarshalled.
func (c *Contact) MarshalJSON() ([]byte, error) {
contactMap := make(map[string]interface{})
contactMap["EmailAddress"] = c.EmailAddress
contactMap["Name"] = c.Name
contactMap["Phone"] = c.Phone
for k, v := range c.CustomFields {
contactMap[k] = v
}
return json.Marshal(contactMap)
}
func (c *Contact) UnmarshalJSON(data []byte) error {
var contactMap map[string]interface{}
if c == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
if err := json.Unmarshal(data, &contactMap); err != nil {
return err
}
c.EmailAddress = contactMap["EmailAddress"].(string)
c.Name = contactMap["Name"].(string)
c.Phone = contactMap["Phone"].(string)
for key, val := range contactMap {
if key != "EmailAddress" && key != "Name" && Key != "Phone" {
c.CustomFields[key] = value
}
}
return nil
}
Is this the best way to do this? What would you recommend?
答案1
得分: 5
只需进行一些小的清理
var contactMap map[string]interface{}
if c == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
if err := json.Unmarshal(data, &contactMap); err != nil {
return err
}
for key, val := range contactMap {
switch key {
case "EmailAddress":
c.EmailAddress = val.(string)
case "Name":
c.Name = val.(string)
case "Phone":
c.Phone = val.(string)
default:
c.CustomFields[key] = val
}
}
}
英文:
Just add little cleanup
var contactMap map[string]interface{}
if c == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
if err := json.Unmarshal(data, &contactMap); err != nil {
return err
}
for key, val := range contactMap {
switch key {
case "EmailAddress":
c.EmailAddress = val.(string)
case "Name":
c.Name = val.(string)
case "Phone":
c.Phone = val.(string)
default:
c.CustomFields[key] = val
}
}
}
答案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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论