英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论