4

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)
}
6
  • There is insufficient information to reproduce or verify. Please revise the question to show an example request body. If it helps, do: fmt.Println("%#v", requestBody) so that we know what kind of thing is being parsed and can try to reproduce your problem. Also, your question does not convincingly show that parsing is the problem: how do you know that it's parsing that's causing the problem? At the moment, you've shown a log message, but maybe the log is not showing the right thing? What line in your program corresponds to which logging statement in your program? We can not tell. Commented Oct 31, 2014 at 23:54
  • my request body after I have outputted with your fmt code is something like: %#v [123 34 117 115 101 114 110 97 109 101 34........17 115 101 114 115 116 97 116 117 115 34 58 110 117 108 108 44 34 8 108 125] Commented Oct 31, 2014 at 23:58
  • About logging lines; I cannot see any of the log lines just after the ` fmt.Printf("error happens here.")`. So ı have guessed the encoding was the problem in addition to the lead of the error message relating to the parsing. Commented Oct 31, 2014 at 23:59
  • ... sure, but if you are mixing up log.Printfs alongside fmt.Printfs, the ordering guarantees are particularly weak. This is exactly why you probably should be using the log methods exclusively: mixing the two is not a good idea. Commented Nov 1, 2014 at 0:05
  • Please try: fmt.Println("%#v", string(requestBody)). Copy and paste the exact content somewhere. That way, someone here can use that to reproduce the parse error, or confirm that JSON parsing is not responsible. Commented Nov 1, 2014 at 0:07

1 Answer 1

7

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)
    }
}
Sign up to request clarification or add additional context in comments.

7 Comments

I have initialised the time.Time typed fields Timestamp and SignupTimestamp to time.Now() for this reason. It did not overcome it. Why?
The problem is that the UnmarshalJSON method that's defined for time.Time doesn't know how to deal with null. You might argue that it should, but that's a separate discussion, and probably worth sending a bug report. But as it stands for now, it doesn't, so you need to manage that. The answer above shows one way to manage it.
Yes. I have not argued about it. But now I can say it should. My point is that the fields are not null in my input to UnmarshalJSON, as the following line: registerInstance = RegisterStructure{Timestamp: now, SignupTimestamp: now,}
They are null in your requestBody. The problem isn't that they're null in the destination structure. The problem is that when the JSON parser is dealing with your input, your input has nulls in the values associated to those fields.
Yes now I have understood. I have confused the point that what I input to UnmarshallJSon is of course a byte slice of JSON from request body. Thanks. Now it seems I should make the string representation of the time.Now() which I need. And set the fields with that after decoding process.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.