简体   繁体   English

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

[英]How to map JSON objects with dynamic fields to Go structs

I am developing a website using Go and connecting it to Elastic Search. 我正在使用Go开发一个网站并将其连接到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). 当我从Elastic Search读取文档时,它将返回一个JSON对象作为结果,其中可以包含具有动态名称(或用户定义的字段)的字段。

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. 我可以获得JSON结果并将其解组为Go结构,但我不知道将这些动态字段保留为Go结构的一部分的最佳方法是什么。

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: 例如,如果我从Elastic Search获得联系人的文档,它可能看起来像这样:

{  
   "EmailAddress": "test@test.com",
   "Name": "Test Contact",
   "Phone": "17894785236",
   "City": "San Francisco",
   "State": "California"
}

And the Go struct for Contact is: 接触的Go结构是:

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. 我实现MarshalerUnmarshaler来覆盖对象的Marshaled和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? 你会推荐什么?

As Simon has pointed out in comment, using one big map[string]interface{} isn't ideal if the structure of the json is fixed. 正如Simon在评论中指出的那样,如果json的结构是固定的,那么使用一个大的map [string] interface {}并不理想。 Best way then is to use structure and unmarshall it using http://golang.org/pkg/encoding/json/#Unmarshal . 最好的方法是使用结构并使用http://golang.org/pkg/encoding/json/#Unmarshal解组它。 (refer the example: http://play.golang.org/p/cDTe8x4xLk ) (参考示例: http//play.golang.org/p/cDTe8x4xLk

But for large json blob, for which structure is not known beforehand, your implementation works perfectly. 但对于大型json blob,事先不知道哪种结构,您的实现工作完美。

edit: added link to example 编辑:添加了链接到示例

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
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM