Clojure 1.8.0中嵌套结构里defrecord的自定义pprint格式问题
为Clojure 1.8的defrecord实现带EDN标签的自定义pprint格式
我明白你的需求——既要给defrecord加上自定义的EDN标签化输出,又不想牺牲pprint的美观排版(比如自动换行、缩进)。之前把*print-pprint-dispatch*设为pr确实能显示自定义格式,但会丢掉pprint的核心排版能力,这肯定不是最优解。下面是针对Clojure 1.8.0的完整解决方案:
步骤1:定义defrecord并实现自定义print-method
首先我们要为你的junkrecord实现自定义的print-method,让它输出带EDN标签的格式,而不是默认的#junkrecord{...}:
(defrecord junkrecord [val]) ;; 自定义print-method,输出#junk/junkrecord标签+内部map结构 (defmethod print-method junkrecord [record writer] (.write writer "#junk/junkrecord ") (print-method (into {} record) writer))
这个方法会让普通的pr/println输出自定义标签,但此时pprint还不会用这个格式,因为pprint有自己的调度表。
步骤2:扩展pprint的dispatch方法
pprint通过clojure.pprint/pprint-dispatch来决定不同类型的排版逻辑,我们需要为junkrecord添加专门的dispatch方法,这样既能保留自定义标签,又能利用pprint的排版:
;; 为junkrecord注册pprint调度逻辑 (defmethod clojure.pprint/pprint-dispatch junkrecord [record] ;; 先输出自定义标签 (clojure.pprint/write-out "#junk/junkrecord ") ;; 把record转成map后交给默认的map dispatch处理,自动继承pprint的排版 (clojure.pprint/pprint-dispatch (into {} record)))
测试效果
现在你可以测试一下pprint的输出,完全保留排版:
(def test-record (->junkrecord {:name "foo" :details {:x 100 :y 200 :z [1 2 3]}})) (clojure.pprint/pprint test-record)
输出会是这样的(自动换行和缩进):
#junk/junkrecord {:name "foo", :details {:x 100, :y 200, :z [1 2 3]}}
(可选)支持EDN读取
如果需要能把自定义标签的EDN字符串读回成junkrecord实例,还需要注册数据读取器:
;; 定义读取函数,把map转成junkrecord (defn read-junkrecord [data-map] (map->junkrecord data-map)) ;; 注册到全局数据读取器 (alter-var-root #'clojure.core/*data-readers* assoc 'junk/junkrecord read-junkrecord) ;; 测试读取 (read-string "#junk/junkrecord {:val 42}") ;; => #junkrecord{:val 42}
这个方案的核心是既实现自定义的输出格式,又让pprint针对自定义类型做专门的调度,避免了直接替换*print-pprint-dispatch*带来的排版丢失问题,完全适配Clojure 1.8.0版本。
内容的提问来源于stack exchange,提问作者Sonicsmooth
相关产品推荐
相关产品推荐

