Есть ли способ декодировать JSON в Go с преобразованием типов? - PullRequest
0 голосов
/ 08 декабря 2018

Я работаю с API, который может возвращать одно значение как int, если оно равно нулю, и как строку, если оно не равно нулю, и я хочу декодер, который будет правильно декодировать эти два JSON в struct

{
  "id": 1,
  "rating": 0
}
{
  "id": 2,
  "rating": "2"
}
type User struct {
  Id int64 `json:"id"`
  Rating int64 `json:"rating,string"`
}

Поэтому он должен попытаться преобразовать любой тип JSON (int, float, string) в тип, указанный в struct, и вызвать ошибку, только если это невозможно.Стандартный json.Decoder не делает этого.

Или, может быть, есть более настраиваемая библиотека для разбора json?

Ответы [ 3 ]

0 голосов
/ 08 декабря 2018

Вы пытаетесь проанализировать int64 из строки JSON.Вы можете сделать это с помощью пользовательских типов, которые реализуют интерфейс JSON Unmarshaler.

например

type User struct {
    Id     int64       `json:"id"`
    Rating Int64String `json:"rating"`
}

type Int64String int64

func (i Int64String) MarshalJSON() ([]byte, error) {
    return json.Marshal(strconv.FormatInt(int64(i), 10))
}

func (i *Int64String) UnmarshalJSON(data []byte) error {
    var jstring string
    err := json.Unmarshal(data, &jstring)
    if err != nil {
        return err
    }
    *(*int64)(i), err = strconv.ParseInt(jstring, 0, 64)
    return err
}

Детская площадка

0 голосов
/ 08 декабря 2018

Решено так:

type Int64 struct {
        Value int64
}

func (this *Int64) UnmarshalJSON(bytesValue []byte) error {
        stringValue := string(bytesValue)

        if len(stringValue) > 2 {
                if stringValue[0] == '"' && stringValue[len(stringValue)-1] == '"' {
                        stringValue = stringValue[1 : len(stringValue)-1]
                }
        }

        var err error
        this.Value, err = strconv.ParseInt(stringValue, 10, 64)
        return err
}

func (this *Int64) MarshalJSON() ([]byte, error) {
        return []byte(fmt.Sprintf("%v", this.Value)), nil
}
0 голосов
/ 08 декабря 2018

Я рекомендую вам изменить API, если это невозможно, вы можете использовать тип интерфейса для оценки и вручную проверять тип во время синтаксического анализа (целые числа будут возвращаться как float64):

package main

import (
    "fmt"
    "encoding/json"
)

type User struct {
    Id int `json:"id"`
    Rating interface{} `json:"rating"`
}

func main() {
    usersJson := `[{"id": 1, "rating": 0}, {"id": 2,"rating": "2"}]`

    var users []User

    err := json.Unmarshal([]byte(usersJson), &users)
    if err != nil {
        fmt.Println("err: ",err)
        return
    }

    for _, u := range users {
        switch u.Rating.(type) {
        case float64:
            fmt.Println("its an float64", u.Rating.(float64))
        case string:
            fmt.Println("its an string", u.Rating.(string))
        }
    }
}
...