前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >老生常谈,如何给go的json key排序

老生常谈,如何给go的json key排序

作者头像
超级大猪
发布2024-06-06 09:42:57
830
发布2024-06-06 09:42:57
举报
文章被收录于专栏:大猪的笔记大猪的笔记

在go中,解析json一直是一个让人痛苦的话题。尤其是对不特定的json对象,将它解析到map[string]any 对象时,key会发生乱序。

大部分场景中,这不会有什么问题,但有一些涉及签名的场景,则可能产生错误。有时需要对Marshal后产生的字符串json,按特定key顺序展示。

方法一,使用jsonvalue库

这个库是腾讯的老哥开发的,功能非常强大。尤其对json排序有很丰富的接口。MustMarshal可以传入丰富的配置。

  1. 简单通用,甚至允许自定义排序
  2. 性能会略差一些,约是官方json.Marshal的1/10。

直接上示例:

代码语言:javascript
复制
// 	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的一般方法,定义一个新类型:

代码语言:javascript
复制
type JSONOrderedMap map[string]any

并实现MarshalJSON即可。

先放用例:

代码语言:javascript
复制
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"}`)
}

实现:

代码语言:javascript
复制
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("\"")
        buf.WriteString(v)
        buf.WriteString("\"")
    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("\"")
        buf.WriteString(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 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 方法一,使用jsonvalue库
  • 方法二:实现MarshalJSON接口
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档