使用特定订单序列化地图

时间:2014-08-07 12:45:50

标签: json serialization go

我有一个地图,它使用字符串作为键和值。我有一组键,用于指定地图值的顺序。

我想将该映射序列化为JSON,但保持在数组上定义的顺序。

此处有一个示例代码:http://play.golang.org/p/A52GTDY6Wx

我想将其序列化为:

{
  "name": "John",
  "age": "20"
}

但如果我直接序列化地图,按字母顺序排列:

{      
  "age": "20",
  "name": "John"
}

我可以将它序列化为一个地图数组,从而保持顺序,但这会产生许多不受欢迎的字符:

[
  {
    "name": "John"
  },
  {
    "age": "20"
  }
]

在我的实际代码中,我需要序列化在文本文件中指定的数据库查询的结果,并且我需要维护列顺序。我不能使用结构,因为在编译时不知道列。

编辑:我不需要按照指定的顺序阅读JSON。生成的JSON应该由人们阅读,所以我只希望它尽可能具有人性化的可读性。

我可以使用自定义格式,但JSON非常适合我。

谢谢!

4 个答案:

答案 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实现。它既可以做元帅也可以做元帅。

https://github.com/mickep76/mapslice-json