Как разобрать нестандартный формат времени из json
говорит, что у меня есть следующий json
{
name: "John",
birth_date: "1996-10-07"
}
и я хочу его декодировать в следующую структуру
type Person struct {
Name string `json:"name"`
BirthDate time.Time `json:"birth_date"`
}
как это
person := Person{}
decoder := json.NewDecoder(req.Body);
if err := decoder.Decode(&person); err != nil {
log.Println(err)
}
который дает мне ошибку parsing time ""1996-10-07"" as ""2006-01-02T15:04:05Z07:00"": cannot parse """ as "T"
если бы я вручную разобрал его, я бы сделал это как
t, err := time.Parse("2006-01-02", "1996-10-07")
но когда значение времени из строки json , как я могу заставить декодер проанализировать его в вышеуказанном формате?
Ответы
Ответ 1
Это тот случай, когда вам нужно реализовать пользовательские функции маршала и демаршала.
UnmarshalJSON(b []byte) error { ... }
MarshalJSON() ([]byte, error) { ... }
Следуя примеру из документации по пакету json для Golang, вы получите что-то вроде:
// 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"'
}
// imeplement Marshaler und Unmarshalere 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(j)
}
// Maybe a Format function for printing your date
func (j JsonBirthDate) Format(s string) string {
t := time.Time(j)
return t.Format(s)
}
Ответ 2
Если есть много структур, и вы просто реализуете пользовательские функции маршала и демаршала, то для этого нужно много работать. Вместо этого вы можете использовать другую библиотеку ,, например, расширение json-итератора 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"'
}
Ответ 3
Я написал пакет для обработки дат yyyy-MM-dd
и yyyy-MM-ddThh:mm:ss
на https://github.com/ah/date
Он использует подход псевдонима типа в ответе выше, затем реализует функции MarshalJSON
и UnmarshalJSON
с некоторыми изменениями.
// 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
}
Важно разобрать в правильном часовом поясе. Мой код предполагает UTC, но вы можете по какой-то причине использовать часовой пояс компьютера.
Я также обнаружил, что в решениях, связанных с использованием функции time.Parse
внутренние механизмы Go пропускаются в виде сообщения об ошибке, которое клиенты не считают полезным, например: cannot parse "sdfdf-01-01" as "2006"
. Это полезно только в том случае, если вы знаете, что сервер написан на Go, а 2006
является примером формата даты, поэтому я добавляю более читаемые сообщения об ошибках.
Я также реализовал интерфейс Stringer
чтобы он был напечатан в сообщениях журнала или отладки.