在Clojure/EDN中序列化已排序的映射?

在Clojure/EDN中序列化已排序的映射?,clojure,sortedmap,edn,Clojure,Sortedmap,Edn,如何在Clojure中序列化和反序列化排序映射 例如: (sorted-map :a 1 :b 2 :c 3 :d 4 :e 5) {:a 1, :b 2, :c 3, :d 4, :e 5} 我注意到: 在REPL中,已排序的映射以与未排序的映射相同的方式显示。这有时似乎很方便,但有时却不方便 不支持 Clojure确实支持 额外资源: 同一个问题有两个可用的答案: 第三个答案是设置自定义读取器文本。您可以像这样打印排序后的地图 ;; non-namespaced tags are m

如何在Clojure中序列化和反序列化排序映射

例如:

(sorted-map :a 1 :b 2 :c 3 :d 4 :e 5)
{:a 1, :b 2, :c 3, :d 4, :e 5}
我注意到:

  • 在REPL中,已排序的映射以与未排序的映射相同的方式显示。这有时似乎很方便,但有时却不方便
  • 不支持
  • Clojure确实支持
  • 额外资源:


    同一个问题有两个可用的答案:

    第三个答案是设置自定义读取器文本。您可以像这样打印排序后的地图

    ;; non-namespaced tags are meant to be reserved
    #my.ns/sorted-map {:foo 1 :bar 2}
    
    然后在读取时使用适当的数据函数(从哈希映射转换为排序映射)。您可以选择是否处理定制比较器(这是一个一般情况下无法解决的问题,但您当然可以选择处理特殊情况)

    clojure.edn/read
    接受可选的
    opts
    映射,该映射可能包含
    :reader
    键;然后,该键处的值将被视为一个映射,指定哪些数据读取器用于哪些标记。有关详细信息,请参见
    (doc clojure.edn/read)

    至于打印,您可以为
    print method
    安装自定义方法,或使用自定义功能打印已排序的地图。我可能会选择后一种解决方案——为内置类型实现内置协议/多方法通常不是一个好主意,因此即使在特定情况下看起来合理,也需要额外的注意等等。;使用自己的功能更简单

    更新:

    演示如何重新使用
    IPersistentMap
    打印方法
    impl clean,正如在对David回答的评论中承诺的那样:

    (def ^:private ipm-print-method
      (get (methods print-method) clojure.lang.IPersistentMap))
    
    (defmethod print-method clojure.lang.PersistentTreeMap
      [o ^java.io.Writer w]
      (.write w "#sorted/map ")
      (ipm-print-method o w))
    
    有了这一点:

    user=> (sorted-map :foo 1 :bar 2)
    #sorted/map {:bar 2, :foo 1}
    

    数据读取器.clj
    中:

    {sorted/map my-app.core/create-sorted-map}
    
    注:我希望这能起作用,但没有(不确定原因):

    现在,在我的app.core中:

    (defn create-sorted-map
      [x]
      (clojure.lang.PersistentTreeMap/create x))
    
    (defmethod print-method clojure.lang.PersistentTreeMap
      [o ^java.io.Writer w]
      (.write w "#sorted/map ")
      (print-method (into {} o) w))
    
    作为另一种选择--低级别,您可以使用:

    (defn create-sorted-map [x] (into (sorted-map) x))
    
    测试:

    (deftest reader-literal-test
      (testing "#sorted/map"
        (is (= (sorted-map :v 4 :w 5 :x 6 :y 7 :z 8)
               #sorted/map {:v 4 :w 5 :x 6 :y 7 :z 8}))))
    
    (deftest str-test
      (testing "str"
        (is (= "#sorted/map {:v 4, :w 5, :x 6, :y 7, :z 8}"
               (str (sorted-map :v 4 :w 5 :x 6 :y 7 :z 8))))))
    
    大部分内容都是根据我在上面找到的资源改编的


    注:我很惊讶上面的
    打印方法
    有效。在我看来,
    (into{}o)
    将失去顺序,从而导致打印失败,但它在我的测试中起作用。我不知道为什么。

    NB。clojure 1.5中引入了
    clojure.edn
    名称空间。测试通过是因为它们很小,所以基本上是“偶然的”(clojure的实现细节)。(测试的大小很重要,因为对于小映射
    (into{}…
    创建数组映射,保留插入顺序。当它们足够大时,这些映射将转换为哈希映射。)
    clojure.lang.PersistentTreeMap/create
    是一个命名Java方法的符号;这些在Clojure不是一流的。您可以将其封装在一个函数中--
    #(clojure.lang.PersistentTreeMap/create%)
    --这个函数可以作为一个值使用,但实际上您当前的解决方案在我看来更好。如果您想对
    clojure.lang.IPersistentMap
    重新使用现有的
    打印方法实现,可以(1)使用
    (get(methods print method)clojure.lang.IPersistentMap)
    (它只是一个函数),(2)将其存储在方便的地方,(3)从自己的
    print method实现中调用它。我将编辑我的答案以演示。
    
    (deftest reader-literal-test
      (testing "#sorted/map"
        (is (= (sorted-map :v 4 :w 5 :x 6 :y 7 :z 8)
               #sorted/map {:v 4 :w 5 :x 6 :y 7 :z 8}))))
    
    (deftest str-test
      (testing "str"
        (is (= "#sorted/map {:v 4, :w 5, :x 6, :y 7, :z 8}"
               (str (sorted-map :v 4 :w 5 :x 6 :y 7 :z 8))))))