我有一个地图,它使用字符串作为键和值。我有一组键,用于指定地图值的顺序。
我想将该映射序列化为JSON,但保持在数组上定义的顺序。
此处有一个示例代码:http://play.golang.org/p/A52GTDY6Wx
我想将其序列化为:
{
"name": "John",
"age": "20"
}
但如果我直接序列化地图,按字母顺序排列:
{
"age": "20",
"name": "John"
}
我可以将它序列化为一个地图数组,从而保持顺序,但这会产生许多不受欢迎的字符:
[
{
"name": "John"
},
{
"age": "20"
}
]
在我的实际代码中,我需要序列化在文本文件中指定的数据库查询的结果,并且我需要维护列顺序。我不能使用结构,因为在编译时不知道列。
编辑:我不需要按照指定的顺序阅读JSON。生成的JSON应该由人们阅读,所以我只希望它尽可能具有人性化的可读性。
我可以使用自定义格式,但JSON非常适合我。
谢谢!
答案 0 :(得分:10)
您需要在自定义类型上实现json.Marshaler
接口。这样做的好处是可以在其他结构类型中很好地运行。
抱歉,您总是需要编写一些JSON编码代码。
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
)
type KeyVal struct {
Key string
Val interface{}
}
// Define an ordered map
type OrderedMap []KeyVal
// Implement the json.Marshaler interface
func (omap OrderedMap) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
buf.WriteString("{")
for i, kv := range omap {
if i != 0 {
buf.WriteString(",")
}
// marshal key
key, err := json.Marshal(kv.Key)
if err != nil {
return nil, err
}
buf.Write(key)
buf.WriteString(":")
// marshal value
val, err := json.Marshal(kv.Val)
if err != nil {
return nil, err
}
buf.Write(val)
}
buf.WriteString("}")
return buf.Bytes(), nil
}
func main() {
dict := map[string]interface{}{
"orderedMap": OrderedMap{
{"name", "John"},
{"age", 20},
},
}
dump, err := json.Marshal(dict)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", dump)
}
输出
{"orderedMap":{"name":"John","age":20}}
答案 1 :(得分:4)
对于该特定要求,您根本不需要使用json.Marshal
,您可以简单地实现自己的功能,如this:
type OrderedMap map[string]string
func (om OrderedMap) ToJson(order ...string) string {
buf := &bytes.Buffer{}
buf.Write([]byte{'{', '\n'})
l := len(order)
for i, k := range order {
fmt.Fprintf(buf, "\t\"%s\": \"%v\"", k, om[k])
if i < l-1 {
buf.WriteByte(',')
}
buf.WriteByte('\n')
}
buf.Write([]byte{'}', '\n'})
return buf.String()
}
func main() {
om := OrderedMap{
"age": "20",
"name": "John",
}
fmt.Println(om.ToJson("name", "age"))
}
答案 2 :(得分:0)
可能是最简单的解决方案:https://github.com/iancoleman/orderedmap
尽管它可能会因为提到here
而变慢答案 3 :(得分:0)
这里是类似于YAML v2提供的MapSlice实现。它既可以做元帅也可以做元帅。