老生常谈,如何给go的json key排序 在go中,解析json一直是一个让人痛苦的话题。尤其是对不特定的json对象,将它解析到`map[string]any` 对象时,key会发生乱序。 大部分场景中,这不会有什么问题,但有一些涉及签名的场景,则可能产生错误。有时需要对Marshal后产生的字符串json,按特定key顺序展示。 ## 方法一,使用jsonvalue库 这个库是腾讯的老哥开发的,功能非常强大。尤其对json排序有很丰富的接口。`MustMarshal`可以传入丰富的配置。 1. 简单通用,甚至允许自定义排序 2. 性能会略差一些,约是官方json.Marshal的1/10。 直接上示例: ``` // jsonvalue "github.com/Andrew-M-C/go.jsonvalue" func TestOrderedJsonvalue(t *testing.T) { jsonString := `{ "yolo": "covfefe", "stuff": { "b": "12", "d": "654", "a": "1" }, "yay": 5 }` var iterJSON any if err := json.Unmarshal([]byte(jsonString), &iterJSON); err != nil { t.Fatalf(err.Error()) } v := jsonvalue.New(iterJSON) t.Logf("v: %v", iterJSON) // OptDefaultStringSequence 字典序 s := v.MustMarshal(jsonvalue.OptDefaultStringSequence()) t.Logf("s: %s", s) gtest.Assert(s, `{"stuff":{"a":"1","b":"12","d":"654"},"yay":5,"yolo":"covfefe"}`) } ``` ## 方法二:实现`MarshalJSON`接口 这是自定义处理json的一般方法,定义一个新类型: ``` type JSONOrderedMap map[string]any ``` 并实现`MarshalJSON`即可。 先放用例: ```go func TestJSONOrderedMapStruct(t *testing.T) { jsonString := `{ "yolo": "covfefe", "stuff": { "b": "12", "d": "654", "a": "1" }, "yay": 5, "foo": ["b","c","a"] }` var ordered JSONOrderedMap if err := json.Unmarshal([]byte(jsonString), &ordered); err != nil { t.Error(err) return } jsonStr, err := json.Marshal(ordered) if err != nil { t.Error(err) return } t.Logf("JSON: %s", jsonStr) gtest.Assert(jsonStr, `{"foo":["b","c","a"],"stuff":{"a":"1","b":"12","d":"654"},"yay":5,"yolo":"covfefe"}`) } ``` 实现: ```go package jsontools import ( "bytes" "encoding/json" "sort" "strconv" ) // JSONOrderedMap When calling Marshal, the generated json will be sorted by key type JSONOrderedMap map[string]any func (j JSONOrderedMap) MarshalJSON() ([]byte, error) { buf := new(bytes.Buffer) if err := genJSONMap(buf, j); err != nil { return nil, err } return buf.Bytes(), nil } func genDefaultJSON(buf *bytes.Buffer, i any) error { switch v := i.(type) { case string: buf.WriteString(strconv.Quote(v)) case float64: buf.WriteString(strconv.FormatFloat(v, 'f', -1, 64)) case bool: buf.WriteString(strconv.FormatBool(v)) case nil: buf.WriteString("null") default: bts, err := json.Marshal(v) if err != nil { return err } buf.Write(bts) } return nil } func genJSONArr(buf *bytes.Buffer, vals []any) error { buf.WriteString("[") for i, val := range vals { if i > 0 { buf.WriteString(",") } switch v := val.(type) { case []any: err := genJSONArr(buf, v) if err != nil { return err } case map[string]any: err := genJSONMap(buf, v) if err != nil { return err } default: err := genDefaultJSON(buf, v) if err != nil { return err } } } buf.WriteString("]") return nil } func genJSONMap(buf *bytes.Buffer, j JSONOrderedMap) error { keys := make([]string, 0) for k := range j { keys = append(keys, k) } sort.Strings(keys) buf.WriteString("{") for i, k := range keys { if i > 0 { buf.WriteString(",") } buf.WriteString(strconv.Quote(k)) buf.WriteString(":") switch v := j[k].(type) { case []any: err := genJSONArr(buf, v) if err != nil { return err } case map[string]any: err := genJSONMap(buf, v) if err != nil { return err } default: err := genDefaultJSON(buf, v) if err != nil { return err } } } buf.WriteString("}") return nil } ``` 来自 大脸猪 写于 2024-06-05 10:48 -- 更新于2024-06-24 19:32 -- 0 条评论