Перетворити Go map на json


94

Я намагався перетворити свою карту Go на рядок json разом з encoding/jsonМаршалом, але це призвело до порожнього рядка.

Ось мій код:

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}]

[]

Я справді не знаю, де я помиляюся. Дякую за твою допомогу.


30
Будь ласка, не голосуйте проти, не даючи коментар. Я думаю, що питання є гарним питанням (+1): він містить весь код, він містить точне запитання, результати, ... Це повністю на тему, і ОП доклала багато зусиль, щоб задати гарне питання. Насправді соромно мати тут голосів проти!
topskip

4
Проблема справді пов'язана з тим, що ОП явно ігнорує помилку, яка негайно відповіла б на запитання.
JimB

3
Я явно сумлінний, помилився. Дві помилки в одному питанні. Ви можете бути впевнені, що я не буду їх повторювати.
Cronos87,

Відповіді:


110

Якби ви виявили помилку, то побачили б таке:

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 для "Тип ключа мапи повинен бути або рядком, цілим числом, або реалізовувати кодування. TextMarshaler ."
Ашхар Хасан,

@AshharHasan Очевидно, це змінилося в Go 1.7 ( golang.org/doc/go1.7#encoding_json ), але все ще не робить того, що ви очікуєте: play.golang.org/p/0aFaQ_ByOk
julienc

чи є спосіб зробити це за допомогою sync.Map?
Шахрух Мохаммед

@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


4
Ти маєш рацію. Це ганебна помилка ... Я насправді не знаю, чому я використав int для ключа json ... Дякую за ваші приклади.
Cronos87,

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}]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.