16

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

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

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


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

(def parser (new MyParser))

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

6 回答 6

24

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}
于 2011-10-27T14:26:19.423 回答
5

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

希望这会帮助某人...

于 2012-08-26T15:28:11.933 回答
1

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

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

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

或者;

于 2011-10-27T13:34:04.563 回答
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"}
于 2011-10-27T18:55:28.400 回答
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}
于 2019-02-13T11:37:46.710 回答
-2
user=> (defn parser [text]
  "{ :items { \"itemid\" 55 }}");Mock
user=> (let [parse-tree (read-string (parser "Abracadabra"))]
((parse-tree :items) "itemid"))
55
于 2011-10-27T14:26:25.263 回答