前言
在go中,map中的元素迭代的时候是无序的,使用encoding/json
进行序列化的时候默认是按照字典序排序的。
在一些业务场景中,我们需要使用key有序的map。关于有序map,很自然的就能够想到需要额外使用一个链表或者数组来记录key写入的顺序。go的标准库中没有提供,这里推荐一个用起来感觉不错的第三方的有序map:
github.com/iancoleman/orderedmap
go get
go get github.com/iancoleman/orderedmap
代码样例
下面是代码样例,使用orderedmap.New()
创建一个有序数组对象,通过Set
方法往有序map中添加key和value,可以通过Get
方法获取元素的值。key的顺序与元素添加顺序相关,也可以通过SortKeys
方法对key进行排序,或者通过Sort
方法通过value来对value进行排序。
对有序map进行json序列化的时候,如果未对key或者value进行排序的话,默认会按照元素添加的顺序进行序列化。
package main
import (
"encoding/json"
"fmt"
"sort"
"github.com/iancoleman/orderedmap"
)
func start() {
o := orderedmap.New()
o.Set("name", "tom")
o.Set("age", "10")
o.Set("height", "170")
o.Set("hobby", "ball")
x, _ := json.Marshal(o)
var m map[string]interface{}
_ = json.Unmarshal(x, &m)
y, _ := json.Marshal(m)
fmt.Println("ordered map: ", string(x))
fmt.Println("map: ", string(y))
//ordered map: {"name":"tom","age":"10","height":"170","hobby":"ball"}
//map: {"age":"10","height":"170","hobby":"ball","name":"tom"} map序列化后默认按照key的字典序排序
//按照key的字典序升序排序
o.SortKeys(sort.Strings)
x, _ = json.Marshal(o)
fmt.Println("asc sort key: ", string(x))
//asc sort key: {"age":"10","height":"170","hobby":"ball","name":"tom"} 这是就是标准库中的map序列化后结果一致
//按照key的字典序降序排序
o.SortKeys(func(keys []string) {
sort.Slice(keys, func(i, j int) bool {
return keys[i] > keys[j]
})
})
x, _ = json.Marshal(o)
fmt.Println("desc sort key: ", string(x))
//desc sort key: {"name":"tom","hobby":"ball","height":"170","age":"10"}
//按照value的字典序升序排序
o.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
return a.Value().(string) < b.Value().(string)
})
x, _ = json.Marshal(o)
fmt.Println("sort value: ", string(x))
//sort value: {"age":"10","height":"170","hobby":"ball","name":"tom"}
}
func main() {
start()
}
源码说明
1. orderedmap数据结构
有序map包含三个元素。其中escapeHTML
表示是否转义HTML字符,默认为true表示转义。可以调用SetEscapeHTML(false)
方法来设置不转义HTML字符。
type OrderedMap struct {
keys []string // 存放所有的key,用来记录元素顺序
values map[string]interface{} //存放key和value
escapeHTML bool //是否转义HTML字符。默认为true表示转义。
}
2. 排序函数
//对key进行排序
// SortKeys Sort the map keys using your sort func
func (o *OrderedMap) SortKeys(sortFunc func(keys []string)) {
sortFunc(o.keys)
}
//对value进行排序。其中结构体Pair实现了排序接口的Len()、Less()、Swap()这三个方法。可以直接使用标准库中的排序方法
// Sort Sort the map using your sort func
func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) {
pairs := make([]*Pair, len(o.keys))
for i, key := range o.keys {
pairs[i] = &Pair{key, o.values[key]}
}
sort.Sort(ByPair{pairs, lessFunc})
for i, pair := range pairs {
o.keys[i] = pair.key
}
}
3. 序列化函数
// 主要是分别序列化key和value,然后组装带一起
func (o OrderedMap) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
buf.WriteByte('{')
encoder := json.NewEncoder(&buf)
encoder.SetEscapeHTML(o.escapeHTML)
for i, k := range o.keys {
if i > 0 {
buf.WriteByte(',')
}
// add key
if err := encoder.Encode(k); err != nil {
return nil, err
}
buf.WriteByte(':')
// add value
if err := encoder.Encode(o.values[k]); err != nil {
return nil, err
}
}
buf.WriteByte('}')
return buf.Bytes(), nil
}
4. 反序列化函数
func (o *OrderedMap) UnmarshalJSON(b []byte) error {
if o.values == nil {
o.values = map[string]interface{}{}
}
// 1. 先直接对整体进行反序列化
err := json.Unmarshal(b, &o.values)
if err != nil {
return err
}
dec := json.NewDecoder(bytes.NewReader(b))
if _, err = dec.Token(); err != nil { // skip '{'
return err
}
o.keys = make([]string, 0, len(o.values))
// 2. 解析出源数据中key,主要分为了map和slice两种情况分别解析
return decodeOrderedMap(dec, o)
}
5. 参考自:
最后
大家好,如果觉得有用,可以悄悄给我点个赞[😄],也可以关注微信公众号「那只猴子」。