0

I'm trying to unmarshal JSON with time value. I have this JSON structure.

{
        "Nick": "cub",
        "Email": "cub875@rambler.ru",
        "Created_at": "2017-10-09",
        "Subscribers": [
            {
                "Email": "rat1011@rambler.ru",
                "Created_at": "2017-11-30"
            },
            {
                "Email": "hound939@rambler.ru",
                "Created_at": "2016-07-15"
            },
            {
                "Email": "worm542@rambler.ru",
                "Created_at": "2017-02-16"
            },
            {
                "Email": "molly1122@rambler.ru",
                "Created_at": "2016-11-30"
            },
            {
                "Email": "goat1900@yandex.ru",
                "Created_at": "2018-07-10"
            },
            {
                "Email": "duck1146@rambler.ru",
                "Created_at": "2017-09-04"
            },
            {
                "Email": "eagle1550@mail.ru",
                "Created_at": "2018-01-03"
            },
            {
                "Email": "prawn1755@rambler.ru",
                "Created_at": "2018-04-20"
            },
            {
                "Email": "platypus64@yandex.ru",
                "Created_at": "2018-02-17"
            }
        ]
    }

And a function that implements reading from JSON file to struct User. Everything if fine but when I set CreatedAt field in User struct to time.Time type I get 0001-01-01 00:00:00 +0000 UTC value for each field with type format in JSON file.

type Subscriber struct {
    Email     string    `json: "Email"`
    CreatedAt time.Time `json: "Created_at"`
}

type User struct {
    Nick        string       `json: "Nick"`
    Email       string       `json: "Email"`
    CreatedAt   string       `json: "Created_at"`
    Subscribers []Subscriber `json: "Subscribers"`
}

func main() {

    // Slice where objects from users.json will be pushed
    var jsonData []User

    // ReadJSON and push User struct to jsonData slice
    readJSON("users.json", &jsonData)

    for _, user := range jsonData {
        fmt.Println(user.Nick, user.Email, user.CreatedAt) // 0001-01-01 00:00:00 +0000 UTC
        fmt.Println(user.Subscribers)
    }

}

func readJSON(fileName string, userSlice *[]User) {
    file, err := os.Open(fileName)
    if err != nil {
        log.Fatal(err)
    }

    bytes, err := ioutil.ReadAll(file)
    if err != nil {
        log.Fatal(err)
    }

    err1 := json.Unmarshal(bytes, &userSlice)

    if err1 != nil {
        log.Fatal(err1)
    }

}

What is an appropriate way to read time from JSON file to User in RFC3339 format?

dlsniper
  • 7,188
  • 1
  • 35
  • 44

1 Answers1

2

You can use a custom time type that implements the json.Unmarshaler interface.

You can start with this struct:

type CustomTime struct {
    time.Time // Embed time.Time to allow calling of normal time.Time methods
}

Then add the required UnmarshalJSON([]byte) error function. It could look like this:

func (c *CustomTime) UnmarshalJSON(b []byte) error {
    if len(b) < 3 {
        // Empty string: ""
        return fmt.Errorf("Empty time value")
    }

    t, err := time.Parse("2006-01-02", string(b[1:len(b)-1])) // b[1:len(b)-1] removes the first and last character, as they are quotes
    if err != nil {
        return err
    }

    c.Time = t

    return nil
}

You can try the example on the Go Playground

xarantolus
  • 1,961
  • 1
  • 11
  • 19