Конвертировать карту Go в json

100

Я попытался преобразовать свою карту Go в строку json с помощью encoding/jsonMarshal, но в результате получилась пустая строка.

Вот мой код:

package main

import (
    "encoding/json"
    "fmt"
)

type Foo struct {
    Number int    `json:"number"`
    Title  string `json:"title"`
}

func main() {
    datas := make(map[int]Foo)

    for i := 0; i < 10; i++ {
        datas[i] = Foo{Number: 1, Title: "test"}
    }

    jsonString, _ := json.Marshal(datas)

    fmt.Println(datas)
    fmt.Println(jsonString)
}

Мой вывод:

map[9:{1 test} 2:{1 test} 7:{1 test} 3:{1 test} 4:{1 test} 5:{1 test} 6:{1 test} 8:{1 test} 0:{1 test} 1:{1 test}]

[]

Я действительно не знаю, в чем я ошибаюсь. Спасибо за помощь.

Cronos87
источник
32
Пожалуйста, не голосуйте против без комментария. Я думаю, что вопрос - хороший вопрос (+1): он содержит весь код, он содержит точный вопрос, вывод ... Это полностью по теме, и OP приложил много усилий, чтобы задать хороший вопрос. Очень жаль, что здесь есть голоса против!
topskip 09
4
Проблема действительно связана с тем, что OP явно игнорирует ошибку, которая немедленно ответила бы на вопрос.
JimB 09
3
Я явно сознательный, я был неправ. Две ошибки в одном вопросе. Будьте уверены, я не буду их повторять.
Cronos87 09

Ответы:

116

Если бы вы поймали ошибку, вы бы увидели это:

jsonString, err := json.Marshal(datas)
fmt.Println(err)

// [] json: unsupported type: map[int]main.Foo

Дело в том, что вы не можете использовать целые числа в качестве ключей в JSON; запрещено. Вместо этого вы можете заранее преобразовать эти значения в строки, например, используя strconv.Itoa.

Подробнее см. В этом сообщении: https://stackoverflow.com/a/24284721/2679935

жульен
источник
3
Здесь вы можете увидеть, как сопоставляются типы: golang.org/pkg/encoding/json/#Unmarshal Вместо этого вы можете использовать срез, который будет отображаться в массив JSON. Также: всегда проверяйте на наличие ошибок;)
seong
2
Думаю, поведение изменилось. См. Golang.org/pkg/encoding/json/#Unmarshal, чтобы узнать «Тип ключа карты должен быть либо строкой, либо целочисленным типом, либо реализовывать encoding.TextMarshaler».
Ашхар Хасан
@AshharHasan По-видимому, он изменился в Go 1.7 ( golang.org/doc/go1.7#encoding_json ), но он по-прежнему не делает того, что вы ожидаете: play.golang.org/p/0aFaQ_ByOk
julienc
есть ли способ сделать это с помощью sync.Map?
Shahrukh Mohammad
@ShahrukhMohammad Я не использовал Go много лет, я не смогу ответить на ваш вопрос ... Может быть, попробуйте создать новый вопрос на SO!
julienc
27

На самом деле он говорит вам, что не так, но вы проигнорировали его, потому что не проверяли ошибку, возвращенную из json.Marshal.

json: unsupported type: map[int]main.Foo

Спецификация JSON не поддерживает ничего, кроме строк для ключей объекта, в то время как javascript не будет привередничать с этим, это все еще незаконно.

У вас есть два варианта:

1 Используйте map[string]Fooи преобразуйте индекс в строку (например, используя fmt.Sprint):

datas := make(map[string]Foo, N)

for i := 0; i < 10; i++ {
    datas[fmt.Sprint(i)] = Foo{Number: 1, Title: "test"}
}
j, err := json.Marshal(datas)
fmt.Println(string(j), err)

2 Просто используйте срез (массив javascript):

datas2 := make([]Foo, N)
for i := 0; i < 10; i++ {
    datas2[i] = Foo{Number: 1, Title: "test"}
}
j, err = json.Marshal(datas2)
fmt.Println(string(j), err)

playground

OneOfOne
источник
4
Ты прав. Это досадная ошибка ... Я действительно не знаю, почему я использовал int для ключа json ... Спасибо за ваши примеры.
Cronos87 09
2

Поскольку этот вопрос был задан вопрос / последний ответил, поддержка для не строковых типов ключевых для карт для JSON маршалом / распаковать было добавлено за счет использования TextMarshaler и TextUnmarshaler интерфейсов здесь . Вы можете просто реализовать эти интерфейсы для своих ключевых типов, и тогда все json.Marshalбудет работать должным образом.

package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

// Num wraps the int value so that we can implement the TextMarshaler and TextUnmarshaler 
type Num int

func (n *Num) UnmarshalText(text []byte) error {
    i, err := strconv.Atoi(string(text))
    if err != nil {
        return err
    }
    *n = Num(i)
    return nil
}

func (n Num) MarshalText() (text []byte, err error) {
    return []byte(strconv.Itoa(int(n))), nil
}

type Foo struct {
    Number Num    `json:"number"`
    Title  string `json:"title"`
}

func main() {
    datas := make(map[Num]Foo)

    for i := 0; i < 10; i++ {
        datas[Num(i)] = Foo{Number: 1, Title: "test"}
    }

    jsonString, err := json.Marshal(datas)
    if err != nil {
        panic(err)
    }

    fmt.Println(datas)
    fmt.Println(jsonString)

    m := make(map[Num]Foo)
    err = json.Unmarshal(jsonString, &m)
    if err != nil {
        panic(err)
    }

    fmt.Println(m)
}

Выход:

map[1:{1 test} 2:{1 test} 4:{1 test} 7:{1 test} 8:{1 test} 9:{1 test} 0:{1 test} 3:{1 test} 5:{1 test} 6:{1 test}]
[123 34 48 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 49 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 50 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 51 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 52 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 53 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 54 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 55 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 56 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 44 34 57 34 58 123 34 110 117 109 98 101 114 34 58 34 49 34 44 34 116 105 116 108 101 34 58 34 116 101 115 116 34 125 125]
map[4:{1 test} 5:{1 test} 6:{1 test} 7:{1 test} 0:{1 test} 2:{1 test} 3:{1 test} 1:{1 test} 8:{1 test} 9:{1 test}]
vim
источник