go中如何在json序列化/反序列化时保持对象的顺序

文章介绍了在Go中遇到的JSON序列化顺序问题,由于Go的map是无序的,导致数据签名验证失败。作者通过实现自定义的OrderedMap和接口,解决了在反序列化和序列化过程中保持JSON对象顺序的问题,确保了序列化的结果与原始JSON串一致。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 背景

众所周知,go中的map是无序的,将json对象反序列化到map中,就丢失了原始的json对象的顺序信息。大多数场景下,这是没问题的。因为json标准中是不要求对象顺序的,但实践中却不一定。

最近在用go重写一个java应用时,遇到了这个问题:原来的java程序返回的数据包含json规格的业务数据,以及一个基于该json串的md5指纹,客户端收到go返回的json数据后,首先验证签名正确,再解析json。go重写的程序返回的数据,数据签名验证始终不通过。经过排查发现,go返回的json与原java应用返回的json中,对象内的key顺序不一致,进而导致了签名验证失败。

在前述的java应用中采用了有序map来存储数据,序列化时保持了顺序,golang中的map和encoding/json包是不保证顺序的。好在encoding/json包中提供了Marshaler/Unmarshaler接口,所以决定尝试自己造轮子,实现在json序列化/反序列化场景下的有序map。

自取地址:https://github.com/bournex/ordered_container

如果要将字符串反序列化到OrderedMap,期望序列化后与原始串一致的话,原始串中不能有空格、制表符等内容哈,json结构必须是紧凑的。原因在反序列化章节里会提到。

2. 实现

整体思路就是自定义个map和array对象,并实现Marshaler/Unmarshaler接口,在MarshalJSON/UnmarshalJSON中进行自定义的json对象顺序编排。

2.1 反序列化

json字符串 -> OrderedMap

json的解析过程符合典型的下推自动机模型,好在encoding/json中已经实现了scanner,并且decoder开放了json token的读取接口,而我们只要使用系统栈结构,稍加应用就可以实现这个PDA。

前面提到了在原始json串中不能有空格,否则OrderedMap序列化后是跟原始串对不上。这是因为encoding/json包的scanner实现是忽略掉这些indent信息的。如果要在序列时还保留这些无效的空格、制表符等内容,我就得自己实现scanner…幸好业务上签名用的json串已经是紧凑的了。

2.2 序列化

OrderedMap -> json字符串

这个就比较简单了,先写入起始token ‘{’,再for/range遍历OrderedMap.Values中的值,对于interface{}类型的值调用json.Marshal,将序列化后的byte数组连续拼接即可,最后拼上结束token ‘}’。OrderedArray的处理过程也是一样的。

3. 用法

package main

import (
	"encoding/json"
	"fmt"

	"github.com/bournex/ordered_container"
)

func main() {
	jsonstr := []byte(`{"name":"alice","age":5}`)
    
	var orderedMap ordered_container.OrderedMap
	json.Unmarshal(jsonstr, &orderedMap)
	b, _ := json.Marshal(orderedMap)
	fmt.Println(string(b)) // 输出`{"name":"alice","age":5}`

	unorderdMap := map[string]interface{}{}
	json.Unmarshal(jsonstr, &unorderdMap)
	b2, _ := json.Marshal(unorderdMap)
	fmt.Println(string(b2)) // 输出`{"age":5,"name":"alice"}`
}

祝你使用愉快

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值