英文:
Serialize a map using a specific order
问题
我有一个使用字符串作为键和值的映射表。我有一个键的数组,指定了映射表值的顺序。
我想将该映射表序列化为JSON格式,但要保持数组指定的顺序。
这里有一个示例代码:http://play.golang.org/p/A52GTDY6Wx
我想要序列化的结果是:
{
"name": "John",
"age": "20"
}
但是如果直接序列化映射表,键会按字母顺序排序:
{
"age": "20",
"name": "John"
}
我可以将其序列化为映射表的数组,以保持顺序,但会生成很多不必要的字符:
[
{
"name": "John"
},
{
"age": "20"
}
]
在我的实际代码中,我需要序列化数据库查询的结果,该查询在文本文件中指定,并且我需要保持列的顺序。由于列在编译时是未知的,所以我不能使用结构体。
编辑:我不需要按指定顺序读取JSON。生成的JSON是供人阅读的,所以我只希望它尽可能地易读。
我可以使用自定义格式,但JSON对我来说非常适用。
谢谢!
英文:
I have a map that uses string for both key and value. I have an array of keys that specifies the order of the values of the map.
I want to serialize that map to a JSON, but keeping the order defined on the array.
There is a sample code here: http://play.golang.org/p/A52GTDY6Wx
I want to serialize it as:
{
"name": "John",
"age": "20"
}
But if I serialize the map directly, the keys are ordered alphabetically:
{
"age": "20",
"name": "John"
}
I can serialize it as an array of maps, thus keeping the order, however that generates a lot of undesired characters:
[
{
"name": "John"
},
{
"age": "20"
}
]
In my real code I need to serialize the results of a database query which is specified in a text file, and I need to maintain the column order. I cannot use structs because the columns are not known at compile time.
EDIT: I don't need to read the JSON later in the specified order. The generated JSON is meant to be read by people, so I just want it to be as humanly readable as possible.
I could use a custom format but JSON suits me perfectly for this.
Thanks!
答案1
得分: 16
你需要在自定义类型上实现json.Marshaler
接口。这样做的好处是可以与其他结构类型很好地配合使用。
抱歉,你总是需要编写一小段JSON编码代码。
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
)
type KeyVal struct {
Key string
Val interface{}
}
// 定义一个有序映射
type OrderedMap []KeyVal
// 实现json.Marshaler接口
func (omap OrderedMap) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
buf.WriteString("{")
for i, kv := range omap {
if i != 0 {
buf.WriteString(",")
}
// 编码键
key, err := json.Marshal(kv.Key)
if err != nil {
return nil, err
}
buf.Write(key)
buf.WriteString(":")
// 编码值
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}}
英文:
You need to implement the json.Marshaler
interface on a custom type. This has the advantage of playing well within other struct types.
Sorry, you're always going to have to write a little bit of JSON encoding code.
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)
}
Outputs
{"orderedMap":{"name":"John","age":20}}
答案2
得分: 2
对于这个特定的需求,你实际上不需要使用json.Marshal
,你可以简单地实现自己的函数,就像这样:
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"))
}
你可以点击这里查看示例代码。
英文:
For that specific requirement you really don't need to use json.Marshal
at all, you can simply implement your own function like 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"))
}
答案3
得分: 2
可能最简单的解决方案是:https://github.com/iancoleman/orderedmap
尽管在这里提到它可能会比较慢。1
英文:
Probably the easiest solution: https://github.com/iancoleman/orderedmap
Although it might be slow as it's mentioned here
答案4
得分: 0
这是一个类似于YAML v2提供的MapSlice实现。它可以进行Marshal和Unmarshal操作。
https://github.com/mickep76/mapslice-json
英文:
Here is a MapSlice implementation similar to what YAML v2 offers. It can do both Marshal and Unmarshal.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论