Использовать объект Java как карту Clojure
У меня есть класс Java, который я хотел бы использовать в Clojure. Но я хочу использовать его как карту Clojure. Каковы шаги, необходимые для этого?
Я просмотрел код для IPersistentMap
- должен ли Java-класс реализовать это? Или должен быть какой-то Clojure код, который реализует протокол?
Я знаю, что могу просто написать код сопоставления, чтобы явно преобразовать код из объектов Java в карты, но это решение имеет большое соотношение усилий/вознаграждений. Кроме того, я мог бы столкнуться с такой же ситуацией много раз.
Конкретный пример: у меня есть парсер, написанный на Java. Я хотел бы использовать это для разбора некоторого текста, а затем получить доступ к содержимому анализируемой структуры данных, как если бы он был в Clojure maps:
(def parser (new MyParser))
(let [parse-tree (parser ... parse some text ...)]
((parse-tree :items) "itemid"))
Ответы
Ответ 1
Функция 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}
Ответ 2
Уверен, что (bean javaObject)
(см. bean ClojureDoc), но он не позволяет вам выбрать требуемое свойство и те, которых нет. Это влияет на ввод результирующей карты в функцию json-str
, в этом случае вы можете получить сообщение об ошибке: "Не знаю, как писать JSON of..."
И я нахожу, что это раздражает, когда я имею дело с NoSQL DB (mongoDB, neo4j), который принимает по существу JSON (например, лежащую в основе 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 [email protected] is here to unsplice the few properties
(map (fn [prop] [(keyword (str prop)) `(.. ~object [email protected](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
Clojure ключевые слова могут искать материал во всем, что реализует необходимые (только для чтения) части интерфейса java.lang.Map. Проблема, вероятно, состоит в том, что вы на самом деле не используете clojure ключевые слова в качестве ключей, чтобы это не помогло вам.
Что касается IPersistentMap; ваш синтаксический анализатор, по-видимому, не реализует ничего, что имеет отношение к этому интерфейсу.
Лично я бы написал прямое преобразование. clojure использует много таких (seq, например), и после конвертирования вы знаете, что имеете дело с реальной постоянной картой, а не с тем, что действует только некоторое время (так что вы действительно можете вызвать seq, keys, vals и т.д.).
В качестве альтернативы;
- просто выполните clojure.lang.ILookup и оставьте все остальное.
- конвертировать, используя какой-то сгенерированный/отраженный код, если вы хотите что-то более общее. См. https://github.com/joodie/clj-java-fields для примера.
Ответ 4
Как просто использовать 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"}
Ответ 5
user=> (defn parser [text]
"{ :items { \"itemid\" 55 }}");Mock
user=> (let [parse-tree (read-string (parser "Abracadabra"))]
((parse-tree :items) "itemid"))
55