Converter Go map para json

94

Tentei converter meu mapa Go em uma string json com encoding/jsonMarshal, mas resultou em uma string vazia.

Este é meu código:

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

Minha saída é:

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

[]

Realmente não sei onde estou errado. Obrigado pela ajuda.

Cronos87
fonte
30
Por favor, não downvote sem dar um comentário. Eu acho que a pergunta é uma boa pergunta (+1): contém todo o código, contém uma pergunta precisa, a saída, ... Está totalmente no assunto e o OP se esforçou muito para fazer uma boa pergunta. É realmente uma pena ter os votos negativos aqui!
topskip de
4
O problema decorre do fato de o OP ignorar explicitamente o erro que teria respondido a questão imediatamente.
JimB de
3
Estou claramente consciente de que estava errado. Dois erros em uma pergunta. Você pode ter certeza de que não os repetirei.
Cronos87

Respostas:

110

Se você tivesse detectado o erro, teria visto o seguinte:

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

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

O fato é que você não pode usar inteiros como chaves em JSON; é proibido. Em vez disso, você pode converter esses valores em strings de antemão, por exemplo, usando strconv.Itoa.

Veja esta postagem para mais detalhes: https://stackoverflow.com/a/24284721/2679935

julienc
fonte
3
Aqui você pode ver como os tipos são mapeados: golang.org/pkg/encoding/json/#Unmarshal Em vez disso, você pode usar uma fatia, que será mapeada para uma matriz JSON. Além disso: sempre verifique se há erros;)
seong
2
Acho que o comportamento mudou. Consulte golang.org/pkg/encoding/json/#Unmarshal para "O tipo de chave do mapa deve ser uma string, um tipo inteiro ou implementar a codificação.TextMarshaler."
Ashhar Hasan
@AshharHasan Aparentemente mudou no Go 1.7 ( golang.org/doc/go1.7#encoding_json ), mas ainda não faz o que você esperaria: play.golang.org/p/0aFaQ_ByOk
julienc
existe uma maneira de fazer isso com um sync.Map?
Shahrukh Mohammad
@ShahrukhMohammad Não uso o Go há anos, não poderei responder à sua pergunta ... Talvez tente criar uma nova pergunta no SO!
julienc
27

Na verdade, ele informa o que está errado, mas você o ignorou porque não verificou o erro retornado de json.Marshal.

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

A especificação JSON não oferece suporte a nada, exceto strings para chaves de objeto, embora o javascript não seja muito exigente quanto a isso, ainda é ilegal.

Você tem duas opções:

1 Use map[string]Fooe converta o índice em string (usando fmt.Sprint, por exemplo):

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 Basta usar um slice (array 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
fonte
4
Você está certo. É um erro vergonhoso ... Eu realmente não sei por que usei um int para uma chave json ... Obrigado por seus exemplos.
Cronos87
2

Uma vez que esta pergunta foi feita / último respondeu: suporte para tipos não-chave corda para mapas para json Marshal / Unmarshal foi adicionado através do uso de TextMarshaler e TextUnmarshaler interfaces de aqui . Você poderia apenas implementar essas interfaces para seus tipos de chave e, a seguir json.Marshal, funcionaria conforme o esperado.

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

Resultado:

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
fonte