Clojure读取大型嵌套JSON时如何保留键的原始顺序?
如何在Clojure中读取JSON时保留键的原始顺序?
用户的依赖配置 (deps.edn)
{ :deps { org.clojure/data.json {:mvn/version "2.4.0"} org.clojure/data.csv {:mvn/version "1.1.0"} org.flatland/ordered {:mvn/version "1.4.0"} metosin/jsonista {:mvn/version "0.3.8"} com.cognitect/transit-clj {:mvn/version "1.0.333"} }}
用户的代码
(ns ordering-json (:require [clojure.data.csv :as csv] [clojure.data.json :as json] [jsonista.core :as json2] [clojure.java.io :as io] [clojure.string :as string] [clojure.test :as t] [flatland.ordered.map :refer [ordered-map]] )) (defn save-as-json [of data] (with-open [writer (io/writer of)] (json/write data writer))) (defn read-f [f out] (let [data (json/read-str (slurp f) :key-fn (fn [k] (if (empty? k) "" k)) :map-fn ordered-map )] (prn data) (save-as-json out data))) (read-f "./data/standards.json" "./data/modified.json")
问题描述
使用多个JSON反序列化工具读取JSON时,小型文件能保留键顺序,但处理大型嵌套JSON文件时,键的原始顺序会被打乱,希望找到读取时保留键顺序的方法。
解决方案
1. 修复clojure.data.json的嵌套对象解析问题
你当前的代码仅给顶层JSON对象指定了:map-fn ordered-map,但嵌套的JSON对象仍会使用默认的无序map。需要通过:value-fn递归处理所有嵌套对象:
(defn read-f [f out] (let [parse-nested (fn [key value] (if (map? value) (into (ordered-map) value) value)) data (json/read-str (slurp f) :key-fn (fn [k] (if (empty? k) "" k)) :map-fn ordered-map :value-fn parse-nested)] (prn data) (save-as-json out data)))
parse-nested函数会检查每个解析值,若为map类型(即嵌套JSON对象),就转换为ordered-map,确保所有层级的键顺序都被保留。
2. 使用jsonista实现更简洁的嵌套有序解析
jsonista支持直接配置object-mapper,让所有JSON对象(包括嵌套的)都使用ordered-map:
(defn read-f-jsonista [f out] (let [mapper (json2/object-mapper {:decode-key-fn (fn [k] (if (empty? k) "" k)) :decode-object-fn (fn [m] (into (ordered-map) m))}) data (json2/read-value (slurp f) mapper)] (prn data) (json2/write-value out data)))
:decode-object-fn会作用于所有解析出的JSON对象,无需额外递归处理,就能实现全层级的键顺序保留。
验证注意事项
- 写入JSON时,clojure.data.json和jsonista都会按ordered-map的顺序输出键,无需额外配置。
- 处理超大型JSON文件时,建议使用流式解析(如jsonista的流式API),避免一次性加载整个文件到内存,同时流式解析也能通过配置保留键顺序。
内容的提问来源于stack exchange,提问作者TheChetan
相关产品推荐
相关产品推荐

