JSON Сериализация проблемы внешнего типа - преобразование элемента интерфейса map [string] {} в int - PullRequest
0 голосов
/ 01 апреля 2020

Я хочу сериализовать тип Dense пакета gonum.org/v1/gonum/mat. Из-за того, что я не могу реализовать методы для внешних типов, я создал тип

type DenseEx struct {
    Mtx *mat.Dense
}

и реализовал метод MarshalJSON следующим образом

func (d DenseEx) MarshalJSON() ([]byte, error) {
    js := map[string]interface{}{}
    rows, cols := d.Mtx.Dims()
    js["cols"] = cols
    js["rows"] = rows
    fltVals := make([]float64, cols*rows)
    for r := 0; r < rows; r++ {
       for c := 0; c < cols; c++ {
            i := r*cols + c
            fltVals[i] = d.Mtx.At(r, c)
        }
    }
  js["values"] = fltVals
  return json.Marshal(js)
}

Это работает, как и ожидалось. Теперь у меня есть проблемы с демонтажем структуры.

func (d DenseEx) UnmarshalJSON(data []byte) error {
    js := map[string]interface{}{}
    err := json.Unmarshal(data, &js)
    if err != nil {
        return err
    }
    intf, ok := js["cols"]
    if !ok {
        return fmt.Errorf("tag 'cols' missing in JSON data")
    }
    var cols, rows int
    cols, ok = intf.(int)
    if !ok {
        return fmt.Errorf("tag 'cols' cannot be converted to int")
    }
    ...
    return nil
}

Я не могу преобразовать значение тегов в его правильный тип. Мой тест json строка

var jsonStrs = []struct {
    str         string
    expected    DenseEx
    description string
}{
    {
        str: "{\"cols\":3,\"rows\":2,\"values\":[6,1,5,2,4,3]}",
        expected: DenseEx{
            Mtx: nil,
        },
        description: "deserialization of a 2x3 matrice",
    },
}

и мой код теста

...
for _, d := range jsonStrs {
    var m DenseEx
    err := m.UnmarshalJSON([]byte(d.str))
...

Я всегда получаю результат

matex_test.go:26: FAIL: deserialization of a 2x3 matrice: tag 'cols' cannot be converted to int

Есть идеи?

Заранее спасибо!

1 Ответ

1 голос
/ 01 апреля 2020

если вы отметите документы Unmarshal. Вы обнаружите, что известный тип чисел в Unmarshal равен float64

Чтобы демаршировать JSON в значение интерфейса, Unmarshal сохраняет одно из них в значении интерфейса:

bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null

Итак, когда вы попытаетесь Unmarshal int, вы получите его как float 64. Сначала нужно набрать assert его в float64 intf.(float64), а затем преобразовать в int

, например:

    intf, ok := js["cols"]
    if !ok {
        return fmt.Errorf("tag 'cols' missing in JSON data")
    }
    var cols, rows int

    ucols, ok := intf.(float64)
    if !ok {
        return fmt.Errorf("tag 'cols' cannot be converted to float64")
    } 

    cols = int(ucols)
...