sçuçu
sçuçu

Reputation: 3070

Json Decode cannot parse timestamp in json into Go struct

I am trying to obtain an HTTP request body which is a json object and decode it into a Go struct I have defined.

Two of the fields of the struct is of time.Time type. While having only one such typed field everything works correctly.

If I have more than one time.Time typed fields in the go struct I cannot decode it and get the error:

2014/11/01 01:07:04 parsing time "null" as ""2006-01-02T15:04:05Z07:00"": cannot parse "null" as """

The problem is in the decoding lines. Despite my debugging efforts I could have reached a meaningful result. That issue seems strange which is actually should not be.

What am I missing here?

func register(w http.ResponseWriter, r *http.Request){
//Read Request Body JSON Into Go Types

var requestBody = []byte(`{"username":"qwewwwqweqwe","password":"can","usertype":"student","firstname":"",‌​"midname":null,"surname":null,"signuptimestamp":null,"userstatus":null,"phone":nu‌​ll,"email":null,"address":null,"city":null,"country":null,"language":null,"lastlo‌​gintimestamp":null}`)

type RegisterStructure struct {
    Id int `json:"id"`
    Timestamp time.Time `json:"timestamp,omitemty"`
    SignupTimestamp time.Time `json:"signuptimestamp,omitempty"`
    Username string `json:"username"`
    Password string `json:"password"`
    UserType string `json:"usertype"`
    FirstName string `json:"firstname"`
    Midname string `json:"midname"`
    Surname string `json:"surname"`
    UserStatus string `json:"userstatus"`
    Phone string `json:"phone"`
    Email string `json:"email"`
    Address string `json:"address"`
    City string `json:"city"`
    Country string `json:"country"`
    Language string `json:"language"`
    //LastLoginTimestamp time.Time `json:"lastlogintimestamp,omitempty"`
}
var registerInstance RegisterStructure
var now = time.Now()
fmt.Printf("now is %v", now)
fmt.Println()
fmt.Printf("1 registerInstance after inited here is %v", registerInstance)
fmt.Println()

registerInstance = RegisterStructure{Timestamp: now, SignupTimestamp: now,}
fmt.Printf("registerInstance after set to var now here is %v", registerInstance)
fmt.Println()

dec := json.NewDecoder(bytes.NewReader(requestBody))
err = dec.Decode(&registerInstance)
if err != nil {
    fmt.Printf("error happens here.")
    log.Fatal(err)
}

Upvotes: 4

Views: 3779

Answers (1)

dyoo
dyoo

Reputation: 12013

Ok. Here is a reproducible example that demonstrates the error you're seeing.

package main

import (
    "encoding/json"
    "fmt"
    "bytes"
    "time"
)

type RegisterStructure struct {
    SignupTimestamp time.Time `json:"signuptimestamp,omitempty"`
}

func main() {
    requestBody := []byte(`{"signuptimestamp" : null}`)
    dec := json.NewDecoder(bytes.NewReader(requestBody))
    registerInstance := RegisterStructure{}
    err := dec.Decode(&registerInstance)
    if err != nil {
        fmt.Println(err)
    }
}

The error you're seeing has nothing to do with having multiple timestamps. This is why showing inputs is critical for debugging situations like this, and why you should go back and change your question to include a sample requestBody as part of the question content. Otherwise, it becomes very difficult to guess what you're doing.

What's happening is that null is not handled by the JSON unmarshaller for time.Time. The documentation for time.Time's unmarshaller says: UnmarshalJSON implements the json.Unmarshaler interface. The time is expected to be a quoted string in RFC 3339 format.

null is not such a value: the decoder in this case will not try to construct a "zero" value for the timestamp.

What you want to do is change the type of SignupTimestamp from time.Time to *time.Time, so that the null value can be explicitly represented as a nil pointer.

Here is an example to demonstrate:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "time"
)

type RegisterStructure struct {
    SignupTimestamp *time.Time `json:"signuptimestamp,omitempty"`
}

func main() {
    requestBodies := []string{
        `{"signuptimestamp" : "1985-04-12T23:20:50.52Z"}`,
        `{"signuptimestamp" : null}`,
    }
    for _, requestBody := range requestBodies {
        dec := json.NewDecoder(bytes.NewReader([]byte(requestBody)))
        registerInstance := RegisterStructure{}
        err := dec.Decode(&registerInstance)
        if err != nil {
            fmt.Println(err)
        }
        fmt.Printf("%#v\n", registerInstance)
    }
}

Upvotes: 7

Related Questions