Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to map JSON objects with dynamic fields to Go structs

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?

like image 208
user1845791 Avatar asked Dec 15 '14 20:12

user1845791


People also ask

Can we create a dynamic struct in Golang?

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.

What is dynamic JSON?

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.

What is JSON RawMessage?

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.


2 Answers

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
        }
    }
}
like image 99
Vadim Sentiaev Avatar answered Nov 14 '22 05:11

Vadim Sentiaev


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

like image 4
pers3us Avatar answered Nov 14 '22 03:11

pers3us