使用Java对象作为Clojure映射

时间:2011-10-27 13:01:34

标签: java clojure clojure-java-interop

我有一个我想在Clojure中使用的Java类。但是,我想将它用作Clojure地图。这样做的步骤是什么?

我查看了IPersistentMap的代码 - Java类应该实现吗?或者是否应该有一些实现协议的Clojure代码?

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


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

(def parser (new MyParser))

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

6 个答案:

答案 0 :(得分:22)

我想到了函数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}

答案 1 :(得分:5)

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

当我处理基本接受JSON的NoSQL DB(mongoDB,neo4j)时,我觉得很烦人。(就像neocons的基础)。

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

(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)

希望能帮到某人......

答案 2 :(得分:1)

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

IPersistentMap;你的解析器可能没有实现与该接口相关的任何东西。

就个人而言,我会写一个直接转换函数。 Clojure使用了很多这些(例如seq),转换后,你知道你正在处理一个真正的持久性地图,而不是某些时候只能表现得像的东西(所以你可以实际上在它上面调用seq,keys,vals等。)

可替换地;

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

答案 3 :(得分:1)

如果只使用带有(实习)字符串的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"}

答案 4 :(得分:0)

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}

答案 5 :(得分:-2)

user=> (defn parser [text]
  "{ :items { \"itemid\" 55 }}");Mock
user=> (let [parse-tree (read-string (parser "Abracadabra"))]
((parse-tree :items) "itemid"))
55