【问题标题】:Use Java object as Clojure map使用 Java 对象作为 Clojure 映射
【发布时间】:2011-12-16 12:03:43
【问题描述】:

我有一个想在 Clojure 中使用的 Java 类。但是,我想将它用作 Clojure 映射。这样做需要哪些步骤?

我查看了IPersistentMap 的代码——Java 类应该实现它吗?还是应该有一些实现协议的 Clojure 代码?

我知道我可以编写一些映射代码,将代码从 Java 对象显式转换为映射,但该解决方案的工作量/回报率很高。另外,我可能会多次遇到同样的情况。


具体示例:我有一个用 Java 编写的解析器。我想用它来解析一些文本,然后像在 Clojure 映射中一样访问解析后的数据结构的内容:

(def parser (new MyParser))

(let [parse-tree (parser ... parse some text ...)]
  ((parse-tree :items) "itemid"))

【问题讨论】:

    标签: java clojure clojure-java-interop


    【解决方案1】:

    bean 工作正常,但它不能很好地处理一些 Java 对象。

    (import java.awt.Insets)
    (bean (Insets. 1 2 3 4))
    => {:class java.awt.Insets}
    

    但是这个java问题有java解决方案:

    (import (com.fasterxml.jackson.databind ObjectMapper))
    (import (java.util Map))
    (into {} (.. (ObjectMapper.) (convertValue (Insets. 1 2 3 4) Map)))
    => {"top" 1, "left" 2, "bottom" 3, "right" 4}
    

    【讨论】:

      【解决方案2】:

      当然(bean javaObject)(请参阅bean ClojureDoc)运行良好,但它不允许您选择您想要的属性和您不想要的属性。当您将结果映射输入json-str 函数时,它会产生影响,在这种情况下,您可能会收到一条错误消息:“不知道如何编写 JSON of ...”

      当我处理基本上接受 JSON(如 neocons 的底层)的 NoSQL DB(mongoDB、neo4j)时,我发现这很烦人。

      那么我的解决方案是什么?

      (defmacro get-map-from-object-props [object & props]
        ;->> will eval and reorder the next list starting from the end
        (->> (identity props) ;identity is here to return the 'props' seq
             ;map each property with their name as key and the java object invocation as the value
             ;the ~@ is here to unsplice the few properties
             (map (fn [prop] [(keyword (str prop)) `(.. ~object ~@(prop-symbol prop) )]))
             (into {})))
      
      ;getter is a simple function that transform a property name to its getter "name" -> "getName"
      (defn prop-symbol [prop]
        (map symbol (map getter (clojure.string/split (str prop) #"\\."))))
      

      你可以这样使用它(是的,如果有的话,这个函数会处理一个属性链)

      (get-map-from-object-props javaObject property1 property2 property3.property1)
      

      希望对某人有所帮助...

      【讨论】:

        【解决方案3】:

        如果只使用带有(内部)字符串作为键的 java.util.HashMap,并在几行 Clojure 中进行转换呢?:

        (into {} (java.util.HashMap. {"foo" "bar" "baz" "quux"})) ?
        
        {"foo" "bar" "baz" "quux"}
        

        或使用关键字:

        (into {}
          (map
            (juxt
              #(keyword (key %))
              #(val %))
            (java.util.HashMap. {"foo" "bar" "baz" "quux"})))
        
        {:baz "quux", :foo "bar"}
        

        【讨论】:

        • 我不知道如何使用它......我已经有一个 Java 类,并且想像使用 Clojure 对象一样使用它......你是否建议我转换我的java 对象到 HashMap?
        【解决方案4】:
        user=> (defn parser [text]
          "{ :items { \"itemid\" 55 }}");Mock
        user=> (let [parse-tree (read-string (parser "Abracadabra"))]
        ((parse-tree :items) "itemid"))
        55
        

        【讨论】:

        • 你能解释一下吗?我不明白这如何解决我的问题。
        • 如果你的 Parser 输出 clojure map 格式字符串,使用 read-string,你可以转换。
        • 这与问题无关。
        【解决方案5】:

        想到函数bean

        获取一个 Java 对象并根据其 JavaBean 属性返回映射抽象的只读实现。

        来自网站的示例:

        user=> (import java.util.Date)
        java.util.Date
        
        user=> (def *now* (Date.))
        #'user/*now*
        
        user=> (bean *now*)
        {:seconds 57, :date 13, :class java.util.Date,
         :minutes 55, :hours 17, :year 110, :timezoneOffset -330,
         :month 6, :day 2, :time 1279023957492}
        

        【讨论】:

        • 这看起来很有希望,我会试一试。我可能需要做一个递归的bean 应用程序。
        • 看看 clojure.java.data。有一个函数(from-java)递归地使用 java.beans.Introspector。此外,您可以通过将 ...BeanInfo 类添加到 java 库中来优化 from-java 的输出,以控制识别哪些 get... 方法,并重命名 clojure 映射中使用的关键字。
        • 现在你也可以试试 bean-dip 和类似的库:github.com/uwcpdx/bean-dip(我是前者的作者,但在 README 中描述了后者)
        【解决方案6】:

        Clojure 关键字可以在任何实现 java.lang.Map 接口的必需(只读)部分的内容中查找内容。问题可能是您实际上并没有使用 clojure 关键字作为键,因此这可能对您没有帮助。

        至于 IPersistentMap;您的解析器可能没有实现与该接口相关的任何内容。

        就个人而言,我会编写一个直接向上的转换函数。 Clojure 使用了很多这些(例如 seq),并且在转换之后,您知道您正在处理一个真正的持久映射,而不是仅在某些时候表现得像它的东西(所以您可以实际调用 seq、keys、vals 等)。

        或者;

        • 只需实现 clojure.lang.ILookup,其他所有内容都省略。
        • 如果您想要更通用的东西,请使用一些生成/反射代码进行转换。有关示例,请参阅 https://github.com/joodie/clj-java-fields

        【讨论】:

        • 我编辑了我的示例以表明我想将解析 result 视为 Clojure 映射。我在解析结果final 中生成所有字段都没有问题,如果有帮助的话。
        猜你喜欢
        • 2022-07-29
        • 2022-10-13
        • 1970-01-01
        • 2016-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-08
        相关资源
        最近更新 更多