lets say i have the following json
{
name: "John",
birth_date: "1996-10-07"
}
and i want to decode it into the following structure
type Person struct {
Name string `json:"name"`
BirthDate time.Time `json:"birth_date"`
}
like this
person := Person{}
decoder := json.NewDecoder(req.Body);
if err := decoder.Decode(&person); err != nil {
log.Println(err)
}
which gives me the error parsing time ""1996-10-07"" as ""2006-01-02T15:04:05Z07:00"": cannot parse """ as "T"
if i were to parse it manually i would do it like this
t, err := time.Parse("2006-01-02", "1996-10-07")
but when the time value is from a json string how do i get the decoder to parse it in the above format?
In any case, to answer my own question, it seems that parsing JSON should take about 8 cycles per input byte on a recent Intel processor. Maybe less if you are clever. So you should expect to spend 2 or 3 seconds parsing one gigabyte of JSON data.
JSON does not have a built-in type for date/time values. The general consensus is to store the date/time value as a string in ISO 8601 format. Example { "myDateTime": "2018-12-10T13:45:00.000Z" }
JSON does not directly support the date format and it stores it as String. However, as you have learned by now that mongo shell is a JavaScript interpreter and so in order to represent dates in JavaScript, JSON uses a specific string format ISODate to encode dates as string.
Use the JavaScript function JSON. parse() to convert text into a JavaScript object: const obj = JSON.
The JSON.parse function accepts an optional DateTime reviver function. You can use a function like this: dateTimeReviver = function (key, value) { var a; if (typeof value === 'string') { a = /\/Date\ ( (\d*)\)\//.exec (value); if (a) { return new Date (+a [1]); } } return value; } And your dates will come out right.
When the format is set to date-time, the auto-generated SDK will actually produce an error when a non-RFC-3339 date is encountered. The following error will be encountered when attempting to parse a JSON response: When I run to spec written this way, I need to pre-process the spec to remove the incorrect format properties.
When the type=string with no format information, the developer will need to manually parse the time. When the format is set to date-time, the auto-generated SDK will actually produce an error when a non-RFC-3339 date is encountered. The following error will be encountered when attempting to parse a JSON response:
Or, you can use the second parameter, of the JSON.parse () function, called reviver. The reviver parameter is a function that checks each property, before returning the value. Functions are not allowed in JSON.
That's a case when you need to implement custom marshal and unmarshal functions.
UnmarshalJSON(b []byte) error { ... }
MarshalJSON() ([]byte, error) { ... }
By following the example in the Golang documentation of json package you get something like:
// First create a type alias
type JsonBirthDate time.Time
// Add that to your struct
type Person struct {
Name string `json:"name"`
BirthDate JsonBirthDate `json:"birth_date"`
}
// Implement Marshaler and Unmarshaler interface
func (j *JsonBirthDate) UnmarshalJSON(b []byte) error {
s := strings.Trim(string(b), "\"")
t, err := time.Parse("2006-01-02", s)
if err != nil {
return err
}
*j = JsonBirthDate(t)
return nil
}
func (j JsonBirthDate) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(j))
}
// Maybe a Format function for printing your date
func (j JsonBirthDate) Format(s string) string {
t := time.Time(j)
return t.Format(s)
}
If there are lots of struct and you just implement custom marshal und unmarshal functions, that's a lot of work to do so. You can use another lib instead,such as a json-iterator extension jsontime:
import "github.com/liamylian/jsontime"
var json = jsontime.ConfigWithCustomTimeFormat
type Book struct {
Id int `json:"id"`
UpdatedAt *time.Time `json:"updated_at" time_format:"sql_date" time_utc:"true"`
CreatedAt time.Time `json:"created_at" time_format:"sql_datetime" time_location:"UTC"`
}
I wrote a package for handling yyyy-MM-dd
and yyyy-MM-ddThh:mm:ss
dates at https://github.com/a-h/date
It uses the type alias approach in the answer above, then implements the MarshalJSON
and UnmarshalJSON
functions with a few alterations.
// MarshalJSON outputs JSON.
func (d YYYYMMDD) MarshalJSON() ([]byte, error) {
return []byte("\"" + time.Time(d).Format(formatStringYYYYMMDD) + "\""), nil
}
// UnmarshalJSON handles incoming JSON.
func (d *YYYYMMDD) UnmarshalJSON(b []byte) (err error) {
if err = checkJSONYYYYMMDD(string(b)); err != nil {
return
}
t, err := time.ParseInLocation(parseJSONYYYYMMDD, string(b), time.UTC)
if err != nil {
return
}
*d = YYYYMMDD(t)
return
}
It's important to parse in the correct timezone. My code assumes UTC, but you may wish to use the computer's timezone for some reason.
I also found that solutions which involved using the time.Parse
function leaked Go's internal mechanisms as an error message which clients didn't find helpful, for example: cannot parse "sdfdf-01-01" as "2006"
. That's only useful if you know that the server is written in Go, and that 2006
is the example date format, so I put in more readable error messages.
I also implemented the Stringer
interface so that it gets pretty printed in log or debug messages.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With