【问题标题】:How to map JSON objects with dynamic fields to Go structs如何将带有动态字段的 JSON 对象映射到 Go 结构
【发布时间】:2015-02-14 01:42:17
【问题描述】:

我正在使用 Go 开发一个网站并将其连接到 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{}
}

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

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
}

这是最好的方法吗?你会推荐什么?

【问题讨论】:

  • 这些字段的随机性如何?可能完全随机?如果是这样,你上面的就是你最好的选择。但是,如果您可以将动态部分限制为特定结构,那么您可以围绕该部分建模更多结构,并在确定它们是什么后分别解组它们。
  • 是的,它们是随机的。它们不会遵循任何结构,它们将是用户定义的字段。我会保持原样。

标签: json go elasticsearch


【解决方案1】:

只需添加一点清理

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】:

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

但是对于事先不知道结构的大型 json blob,您的实现可以完美运行。

编辑:添加到示例的链接

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-27
    • 2016-06-24
    • 2020-04-14
    相关资源
    最近更新 更多