如何让json.Marshal输出一致字节数组?针对map哈希需求
解决map[string]interface{}的稳定哈希问题
要生成map[string]interface{}的稳定哈希,核心是先将嵌套结构标准化为有序形式,再序列化为字节数组。直接用json.Marshal会因为map键顺序、数组元素顺序变化导致字节数组不一致,因此需要递归处理所有嵌套结构:
核心思路
- Map处理:提取所有键并排序,按排序后的键依次处理每个值(递归标准化)
- 切片/数组处理:先递归标准化每个元素,再将元素转换为可比较的形式(如JSON字符串)进行排序
- 基本类型:直接保留,无需额外处理
实现代码
标准化函数
package main import ( "encoding/json" "fmt" "sort" ) func normalize(v interface{}) interface{} { switch val := v.(type) { case map[string]interface{}: // 排序map的键 keys := make([]string, 0, len(val)) for k := range val { keys = append(keys, k) } sort.Strings(keys) // 按排序后的键构建有序结构 ordered := make([]interface{}, 0, len(val)*2) for _, k := range keys { ordered = append(ordered, k, normalize(val[k])) } return ordered case []interface{}: // 先递归标准化每个元素 normalizedElems := make([]interface{}, len(val)) for i, elem := range val { normalizedElems[i] = normalize(elem) } // 按元素的JSON字符串排序,保证顺序一致 sort.Slice(normalizedElems, func(i, j int) bool { iBytes, _ := json.Marshal(normalizedElems[i]) jBytes, _ := json.Marshal(normalizedElems[j]) return string(iBytes) < string(jBytes) }) return normalizedElems // 基本类型直接返回 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, string, bool: return val // 其他类型可按需扩展(如time.Time) default: return val } }
测试代码
func main() { data := map[string]interface{}{ "id": "124", "name": "name", "count": 123456, "sites": []map[string]interface{}{ { "name": "123445", "count": 234324, "id": "wersfs", }, { "id": "sadcacasca", "name": "sdvcscds", "count": 22, }, }, "list": []int{5, 324, 123, 123, 123, 14, 34, 52, 3}, } data1 := map[string]interface{}{ "name": "name", "id": "124", "sites": []map[string]interface{}{ { "id": "sadcacasca", "count": 22, "name": "sdvcscds", }, { "count": 234324, "name": "123445", "id": "wersfs", }, }, "count": 123456, "list": []int{123, 14, 34, 52, 3, 5, 324, 123, 123}, } // 标准化后再序列化 normalizedData := normalize(data) normalizedData1 := normalize(data1) jsonStr, _ := json.Marshal(normalizedData) jsonStr1, _ := json.Marshal(normalizedData1) fmt.Println("标准化后的JSON是否一致:", string(jsonStr) == string(jsonStr1)) // 输出true }
关键说明
- Map标准化:将无序的map转换为
[key1, value1, key2, value2,...]的有序切片,确保键顺序固定 - 切片排序:通过将元素转换为JSON字符串进行比较,兼容所有可序列化的嵌套结构,保证内容相同的元素排序后位置一致
- 性能优化:如果对性能要求极高,可以针对特定类型(如int、string)编写专门的比较逻辑,避免JSON序列化的开销
内容的提问来源于stack exchange,提问作者Sid P Tkf
相关产品推荐
相关产品推荐

