Как использовать тег go json для демонтажа как числовых c, так и строковых свойств в строковое значение - PullRequest
0 голосов
/ 10 января 2020

У меня есть следующие Go struct и JSON data:

type Entry struct {
    Timestamp string `json:"timestamp"`
    Value     string `json:"value"`
}
{
  "timestamp": "2020-01-01T00:00:00.000Z",
  "value": "a string" // but sometimes it's a number
}

В большинстве случаев value данных JSON имеет тип string, однако иногда он имеет тип number.

Если это число, метод json.Unmarshal возвращает ошибку, подобную этой:

json: cannot unmarshal number into Go struct field Entry.valueof type string

Есть идиоматика c и простой способ преодолеть такую ​​проблему в Go или мне следует реализовать собственный метод демаршаллинга для этого случая?

Ответы [ 2 ]

1 голос
/ 10 января 2020

Предоставление альтернативы ответу icza с использованием пользовательского демаршаллера.

type Entry struct {
    Timestamp string     `json:"timestamp"`
    Value     EntryValue `json:"value"`
}

type EntryValue struct {
    string
}

func (e *EntryValue) UnmarshalJSON(data []byte) error {
    // Simplified check 
    e.string = string(bytes.Trim(data, `"`))
    return nil
}

func main() {

    for _, s := range []string{
        `{  "timestamp": "2020-01-01T00:00:00.000Z",  "value": "a string" }`,
        `{  "timestamp": "2020-01-01T00:00:00.000Z",  "value": 12 }`,
    } {

        var e Entry
        if err := json.Unmarshal([]byte(s), &e); err != nil {
            panic(err)
        }
        fmt.Printf("%#v\n", e)
    }
}
1 голос
/ 10 января 2020

Вы можете использовать interface{} для Entry.Value, и пакет encoding/json выберет правильный тип во время выполнения: string для строки JSON и float64 для строки JSON number:

type Entry struct {
    Timestamp string      `json:"timestamp"`
    Value     interface{} `json:"value"`
}

for _, s := range []string{
    `{  "timestamp": "2020-01-01T00:00:00.000Z",  "value": "a string" }`,
    `{  "timestamp": "2020-01-01T00:00:00.000Z",  "value": 12 }`,
} {

    var e Entry
    if err := json.Unmarshal([]byte(s), &e); err != nil {
        panic(err)
    }
    fmt.Printf("%#v %T\n", e, e.Value)
}

Это выводит (попробуйте на Go Playground ):

main.Entry{Timestamp:"2020-01-01T00:00:00.000Z", Value:"a string"} string
main.Entry{Timestamp:"2020-01-01T00:00:00.000Z", Value:12} float64

Да, тогда вам понадобится введите утверждение , чтобы вывести набранное значение из Entry.Value.

Другой вариант - использовать json.Number, который может содержать как строки, так и числа JSON:

type Entry struct {
    Timestamp string      `json:"timestamp"`
    Value     json.Number `json:"value"`
}

Используя это, приведенный выше пример выводит (это на Go Playground ):

main.Entry{Timestamp:"2020-01-01T00:00:00.000Z", Value:"a string"}
main.Entry{Timestamp:"2020-01-01T00:00:00.000Z", Value:"12"}

При использовании json.Number, вы можете получить доступ к его значению, используя Number.String() или Number.Int64() или Number.Float64(), которые возвращают ошибку, если его значение не является числом.

Здесь следует отметить одну вещь: , если вход JSON является строкой, содержащей действительное число, например, "12", тогда Number.Int64() не сообщит об ошибке, но проанализирует ее и вернет 12 , Это разница по сравнению с использованием intefface{} (где Entry.Value останется string).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...