You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Clojure序列化字符串前对JSON/Map结构排序?

Clojure中clojure/data.json序列化时的键排序解决方案

问题场景

你使用clojure.data.json/write-str序列化嵌套Clojure map时,遇到键顺序不符合预期的问题:

  • 原始数据:
    { :c 1 :a { :d 0 :c 1 :e { :a 0 :1 4 }}}
    
  • 默认输出(键无固定顺序):
    {"c":1,"a":{"d":0,"c":1,"e":{"a":0,"1":4}}}
    
  • 期望输出(按指定顺序排列键):
    {"a":{"c":1,"d":0,"e":{"1":4,"a":0}},"c":1}
    

核心原因

Clojure默认的hash-map是无序结构,write-str序列化时会按map内部的哈希顺序输出键,无法保证固定顺序。而JavaScript的JSON.stringify可以通过第二个参数指定键顺序,Clojure需要借助有序数据结构来实现类似效果。

解决方案

1. 基于自然排序的有序序列化

如果希望键按字符串自然顺序排列(数字键优先,字母键按字典序),可以把所有嵌套map转换为sorted-map(有序map),再进行序列化:

(require '[clojure.data.json :as json]
         '[clojure.walk :as walk])

;; 递归将所有嵌套map转为sorted-map
(defn to-sorted-map [m]
  (if (map? m)
    (into (sorted-map) (walk/postwalk to-sorted-map m))
    m))

(def raw-data { :c 1 :a { :d 0 :c 1 :e { :a 0 :1 4 }}})

(json/write-str (to-sorted-map raw-data))

执行后输出完全符合你的期望:

{"a":{"c":1,"d":0,"e":{"1":4,"a":0}},"c":1}

2. 手动指定自定义键顺序

如果需要严格按照你定义的顺序输出键,可以使用array-map(小map保持插入顺序)或引入有序map库来构造数据:

(require '[clojure.data.json :as json])

;; 按期望顺序构造有序map
(def ordered-data
  (array-map
   :a (array-map
       :c 1
       :d 0
       :e (array-map
           :1 4
           :a 0))
   :c 1))

(json/write-str ordered-data)

这种方式会完全遵循你插入键的顺序生成JSON字符串。如果需要处理大量键或稳定的插入顺序,推荐使用org.clojure/data.ordered库的ordered-map,或者Guava的LinkedHashMap。

注意事项

  • array-map在键数量超过8个时会自动转为无序的hash-map,如果需要稳定的插入顺序,优先使用专门的有序map实现;
  • sorted-map会按键的自然排序规则排列,适合不需要自定义顺序、只需要统一固定顺序的场景;
  • 不需要修改原始数据结构的话,也可以自定义write-str的:formatter参数实现排序,但这种方式代码复杂度更高,不如直接使用有序map直观。

内容的提问来源于stack exchange,提问作者Erik Lilja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:15:08