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": "[email protected]",
"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?
Golang dynamic struct. Package dynamic struct provides possibility to dynamically, in runtime, extend or merge existing defined structs or to provide completely new struct. Main features: Building completely new struct in runtime.
A dynamic JSON file will be created to store the array of JSON objects. Consider, we have a database named gfg, a table named userdata. Now, here is the PHP code to fetch data from database and store them into JSON file named gfgfuserdetails. json by converting them into an array of JSON objects.
RawMessage is a raw encoded JSON value. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding. Example (Marshal) This example uses RawMessage to use a precomputed JSON during marshal.
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
}
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With