2016-12-07 2 views
1

Я извлекаю и декодирую большой ответ JSON, в котором есть ошибка. Теперь мне нужно найти , где ошибка есть! Я read about json.SyntaxError, но я изо всех сил пытаюсь выяснить, как его использовать.Отладка ошибки JSON от Golang

package main 

import (
    "encoding/json" 
    "fmt" 
    "net/http" 
    "os" 
    "text/template" 
    "time" 
) 

type Movie struct { 
    Title  string `json:"title"` 
    PublishedAt time.Time `json:"published_at"` 
} 

func main() { 
    req, _ := http.NewRequest("GET", "https://s.natalian.org/2016-12-07/debugme2.json", nil) 
    resp, err := http.DefaultClient.Do(req) 

    defer resp.Body.Close() 
    dec := json.NewDecoder(resp.Body) 

    _, err = dec.Token() 
    for dec.More() { 
     var m Movie 
     if err = dec.Decode(&m); err != nil { 
      fmt.Println(err) 
      fmt.Println("Bad", m) 

      // https://blog.golang.org/error-handling-and-go 
      if serr, ok := err.(*json.SyntaxError); ok { 
       fmt.Println("Syntax error", serr) 
      } 

     } else { 
      fmt.Println("Good", m) 
     } 

     tmpl := template.Must(template.New("test").Parse("OUTPUT: {{ if .Title }}{{.Title}}{{ if .PublishedAt }} was published at {{.PublishedAt}} {{ end }}{{end}}\n")) 
     tmpl.Execute(os.Stdout, m) 
    } 

} 

Что мне не хватает? Любые инструменты или стратегии или предложения будут высоко оценены. Мой выход в настоящее время выглядит следующим образом:

Good {foobar 2016-11-24 16:17:12 +0800 SGT} 
OUTPUT: foobar was published at 2016-11-24 16:17:12 +0800 SGT 
parsing time ""null"" as ""2006-01-02T15:04:05Z07:00"": cannot parse "null"" as "2006" 
Bad {barbar 0001-01-01 00:00:00 +0000 UTC} 
OUTPUT: barbar was published at 0001-01-01 00:00:00 +0000 UTC 
Good { 1999-12-24 16:11:12 +0200 +0200} 
OUTPUT: 
Good {Something else entirely 2000-01-24 16:11:12 +0200 +0200} 
OUTPUT: Something else entirely was published at 2000-01-24 16:11:12 +0200 +0200 

Но мне нужно что-то вроде этого в моем STDERR к лучше отладить выпуск:

Line 8: published_at is invalid 

И, возможно, некоторые контекст Заголовок, так что я могу сказать API Бэкэнд-команда у них есть ошибка в ответе JSON.

БОНУС вопрос: Кроме того, я не хочу, чтобы напечатать значение 0001-01-01 00:00:00 +0000 UTC, как это на самом деле очень пусто. На самом деле я не возражаю против того, чтобы его не хватало.

+0

Сообщение об ошибке указывает, где именно находится ошибка. * parsing time "" null "" as "" 2006-01-02T15: 04: 05Z07: 00 "": не удается разобрать «null» «как« 2006 »*. В вашем теле JSON вы набрали нуль как строку («null») .Попробуйте удалить кавычки вокруг него. – Nadh

+0

Я знаю, что там есть ошибка, потому что я его там помещал. Мой вопрос спрашивает, как печатать, где произошла ошибка. – hendry

+1

Вы пробовали 'json.unmarshal() 'function? Он возвращает ошибку, если есть какие-либо возможности, которые могут вам помочь, см. [https://play.golang.org/p/eQCG-RE5sK](https://play.golang.org/p/eQCG-RE5sK) – tgogos

ответ

2

Один из способов, как принимать нулевые значения, а не печатать ничего, если published_at имеет нулевое значение, чтобы установить PublishedAt поле со значением указателя:

type Movie struct { 
    Title  string `json:"title"` 
    PublishedAt *time.Time `json:"published_at"` 
} 

Строка ввода является действительным JSON , поэтому пакет json не поднимает SyntaxError.

json пакет имеет некоторые другие типы ошибок, такие как UnmarshalTypeError, которая поднимается, когда ошибка возникает, когда JSON не соответствует nuilt в тип (например: string, int, array ...).

К сожалению, при вызове пользовательской функции UnmarshalJSON(), это выглядит как json пакет возвращает необработанную ошибку:

package main 

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

// check the full type of an error raised when Unmarshaling a json string 
func main() { 
    var test struct { 
     Clock time.Time 
    } 
    buf := bytes.NewBufferString(`{"Clock":null}`) 
    dec := json.NewDecoder(buf) 

    // ask to decode an invalid null value into a flat time.Time field : 
    err := dec.Decode(&test) 

    // print the details of the returned error : 
    fmt.Printf("%#v\n", err) 
} 

// Output : 
&time.ParseError{Layout:"\"2006-01-02T15:04:05Z07:00\"", Value:"null", LayoutElem:"\"", ValueElem:"null", Message:""} 

https://play.golang.org/p/fhZxVpOflb

Конечная ошибка приходит прямо из time пакета, не какой-то UnmarshalError из пакета json, который мог бы хотя бы сказать вам «эта ошибка возникла при попытке значения Unmarshal на этом смещении», и сама ошибка не даст вам контекста.


Вы можете посмотреть специально для типа *time.ParseError в ошибке:

if terr, ok := err.(*time.ParseError); ok { 
    // in the example : Movie has one single time.Time field ; 
    // if a time.ParseError occured, it was while trying to read that field 
    fmt.Println("Error when trying to read 'published_at' value", terr) 

    // you can leave the field to its zero value, 
    // or if you switched to a pointer field : 
    m.PublishedAt = nil 
} 

Если вам посчастливилось иметь несколько полей времени (е.г: ProducedAt и PublishedAt), вы можете еще посмотреть, какое поле было оставлено с нулевым значением:

if terr, ok := err.(*time.ParseError); ok { 
    if m.ProducedAt.IsZero() { 
     fmt.Println("Error when trying to read 'produced_at' value", terr) 
    } 

    if m.PublishedAt == zero { 
     fmt.Println("Error when trying to read 'published_at' value", terr) 
    } 
} 

Кстати: как указано в the docs, «0001-01-01 00:00:00 UTC» является нулевое значение, которое команда go выбрала для нулевого значения go time.Time.

+0

Я быстро попробовал * time.Time в моей структуре, но у меня все еще есть «OUTPUT: barbar был опубликован в 0001-01-01 00:00:00 +0000 UTC» – hendry

+0

ах, похоже, что анализатор попытался декодировать * string * value ' null "вместо обычного значения * null * (" null "без кавычек). – LeGEC

+0

btw есть https://golang.org/pkg/time/#Time.IsZero ... тем не менее, он сосать, как нелегко отметить проблему в исходном JSON по номеру строки. – hendry

0

Ваши данные для publish_at являются «нулевыми», это строковый тип, поэтому я думаю, что вы можете определить строку PublishedAt как, и вы можете использовать код для его анализа во время. Время.

Это мой тестовый код:

package main 

import (
    "encoding/json" 

    "github.com/swanwish/go-common/logs" 
    "github.com/swanwish/go-common/utils" 
) 

func main() { 
    url := `https://s.natalian.org/2016-12-07/debugme2.json` 
    _, content, err := utils.GetUrlContent(url) 
    if err != nil { 
     logs.Errorf("Failed to get content from url %s, the error is %v", url, err) 
     return 
    } 

    movies := []struct { 
     Title  string `json:"title"` 
     PublishedAt string `json:"published_at"` 
    }{} 
    err = json.Unmarshal(content, &movies) 
    if err != nil { 
     logs.Errorf("Failed to unmarshal content %s, the error is %v", string(content), err) 
     return 
    } 
    logs.Debugf("The movies are %v", movies) 
} 

Результат:

The movies are [{foobar 2016-11-24T16:17:12.000+08:00} {barbar null} { 1999-12-24T16:11:12.000+02:00} {Something else entirely 2000-01-24T16:11:12.000+02:00}] 
+0

Я бы скорее попробовал разобрать его во времени (определить ошибку) и сделать с ним. Что с ненужными сторонними инструментами? Но это может быть не строка. Может быть просто null. – hendry

0

Похоже, безумие, но это должно работать:

rawBody := []byte(`{"title":"test", "published_at":"2017-08-05T15:04:05Z", "edited_at":"05.08.2017"}`) 

type Movie struct { 
    Title  string `json:"title"` 
    PublishedAt time.Time `json:"published_at"` 
    EditedAt time.Time `json:"edited_at"` 
} 

var msg Movie 

if err = json.Unmarshal(rawBody, &msg); err != nil { 
    if _, ok := err.(*time.ParseError); ok { 
     value := reflect.ValueOf(msg).Elem() 

     if value.Kind().String() != "struct" { 
      return err 
     } 

     for i := 0; i < value.NumField(); i++ { 
      field := value.Field(i) 

      if t, ok := field.Interface().(time.Time); ok { 
       if t.IsZero() { 
        name := value.Type().Field(i).Name 
        return fmt.Errorf("field: %s, message: %s", strings.ToLower(name), "time is not in RFC 3339 format.") 
       } 
      } 
     } 
    } 

    return err 
} 

Этот код будет возвращать первые произошла ошибка. Если PublisherAt недействителен, мы ничего не узнаем об EditedAt, даже если оно действительно.

Смежные вопросы