2017-08-30 51 views
1

我有一個應用程序(實際上有幾個)在使用Jackson的Map中解碼JSON數據。數據似乎在Map或ArrayList中(在JSON數組的情況下)。這些流上的數據是非結構化的,所以這不會改變。將Clojure的關聯抽象擴展爲Java庫類型

我擁有一些Clojure代碼,它們訪問這些對象中的嵌套屬性。理想情況下,我想將關聯抽象擴展爲這些Java類型,以便get-in能夠對它們起作用。類似以下內容:

(extend-protocol clojure.lang.Associative 
    java.util.Map 
    (containsKey [this k] (.containsKey this k)) 
    (entryAt [this k] (when (.containsKey this k) 
        (clojure.lang.MapEntry/create k (.get this k)))) 
java.util.ArrayList 
    (containsKey [this k] (< (.size this) k)) 
    (entryAt [this k] (when (.containsKey this k) 
        (clojure.lang.MapEntry/create k (.get this k))))) 

有兩個問題:第一個是Associative不是一個協議(如果它出現這樣會起作用)。第二個是類型已經定義,所以我不能添加與deftype聯合。

我對Clojure的JVM interop部分很新穎。有沒有我沒有看到的方法?還是有一個協議包裝Associative,並將與get-in,我錯過了?

謝謝!

回答

2

答案是你想要做的一半擴展已經完成了,另一半不能完成。 get-in函數調用get,它調用clojure.lang.RT/get,它調用clojure.lang.RT/getFrom,如果第一個參數是Map,則調用java.util.Map/get。所以,如果您有任何Java Map,然後get-in作品(我直接借用的doto文檔字符串本示例):

(let [m (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))] 
    [(get-in m ["b"]) 
    (get-in m ["a"])]) 
;;=> [2 1] 

然而,Clojure中沒有一個get實施支持RandomAccessList秒。您可以自己get,做:

(ns sandbox.core 
    (:refer-clojure :exclude [get]) 
    (:import (clojure.lang RT) 
      (java.util ArrayList List RandomAccess))) 

(defn get 
    ([m k] 
    (get m k nil)) 
    ([m k not-found] 
    (if (and (every? #(instance? % m) [List RandomAccess]) (integer? k)) 
    (let [^List m m 
      k (int k)] 
     (if (and (<= 0 k) (< k (.size m))) 
     (.get m k) 
     not-found)) 
    (RT/get map key not-found)))) 

例子:

(get (ArrayList. [:foo :bar :baz]) 2) 
;;=> :bar 

然後,你可以複製的get-in實施,因此會使用自定義get功能。

我敢肯定這是不是你想要的,但是,因爲那時你寫的代碼,每個位將不得不使用get-in而非Clojure的get-in,並已經使用Clojure的get任何其他代碼仍然不適用於ArrayList s。不幸的是,我不認爲你的問題真的有很好的解決方案。

+0

謝謝薩姆,聽說這個問題沒有一個很好的解決方案是有用的。我非常幸運,因爲它實際上都歸結爲少數呼入站點,並且目前採取了類似的方法。我可能會進一步細化它,並利用RT /獲取您的建議。 謝謝! – Cody