Home > OS >  Unmarshalling nested JSON objects in GO
Unmarshalling nested JSON objects in GO

Time:11-08

I combined some properties common to all objects into a struct.

type Document struct {
    ID        string    `json:"_id,omitempty"`
    UpdatedAt time.Time `json:"updatedat"`
    CreatedAt time.Time `json:"createdat"`
}

I also have an address struct, which is not a document.

type Address struct {
    AddressLine string `json:"addressline,omitempty"`
    City        string `json:"city,omitempty"`
    Country     string `json:"country,omitempty"`
    CityCode    int    `json:"citycode,omitempty"`
}

My customer struct is a document. It also has an address property.

type Customer struct {
    Document `json:"document"`
    Address  Address `json:"address"`
    Name     string  `json:"name,omitempty"`
    Email    string  `json:"email,omitempty"`
    Valid    bool    `json:"valid,omitempty"`
}

The JSON object from MongoDB is as follows;

[
    {
        "_id": "6186b4556971a9dbae117333",
        "address": {
            "addressline": "Foo Address",
            "city": "Foo City",
            "citycode": 0,
            "country": "Foo Country"
        },
        "document": {
            "createdat": "0001-01-01T03:00:00 03:00",
            "updatedat": "0001-01-01T03:00:00 03:00"
        },
        "email": "[email protected]",
        "name": "Foo Fooster",
        "valid": false
    }
]

I am using the following code to unmarshal this.

    var customerEntity Entities.Customer
    json.Unmarshal(customerEntityBytes, &customerEntity)

But all I can get is the following line. Most fields are empty.

{{ 0001-01-01 00:00:00  0000 UTC 0001-01-01 00:00:00  0000 UTC} {   0}   false}

As I thought this was due to the mixed nested structure, I created another customer struct for testing purposes;

import "time"

type AutoGenerated []struct {
    ID      string `json:"_id"`
    Address struct {
        Addressline string `json:"addressline"`
        City        string `json:"city"`
        Citycode    int    `json:"citycode"`
        Country     string `json:"country"`
    } `json:"address"`
    Document struct {
        Createdat time.Time `json:"createdat"`
        Updatedat time.Time `json:"updatedat"`
    } `json:"document"`
    Email string `json:"email"`
    Name  string `json:"name"`
    Valid bool   `json:"valid"`
}

All of a sudden the whole problem was fixed and I was able to access it with all fields filled.

In summary, I cannot unmarshal the Custumer struct I want to use. Do I need to override the unmarshall method for this? I've also reviewed the override examples but the codes are very subjective. A change I will make in base classes will cause me to change the unmarshall method. What is the clean way to this?

CodePudding user response:

Always check errors.

err = json.Unmarshal(customerEntityBytes, &customerEntity)
if err != nil {
    // json: cannot unmarshal array into Go value of type Entities.Customer
}

and the reason is, as @mkopriva pointed out - your JSON is an array - and you are unmarshaling to a single struct. To fix:

 var customerEntity []Entities.Customer // use slice to capture JSON array
 err = json.Unmarshal(customerEntityBytes, &customerEntity)
 if err != nil { /* ... */ }

You can certainly use your custom types, but you are losing the _id tag by nesting it in your Document struct. To fix, promote it to Customer:

type Document struct {
    //ID        string    `json:"_id,omitempty"`
    UpdatedAt time.Time `json:"updatedat"`
    CreatedAt time.Time `json:"createdat"`
}

type Customer struct {
    ID       string `json:"_id,omitempty"`
    
    // ...
}

Working example: https://play.golang.org/p/EMcC0d1xOLf

  • Related