2017-08-30 2 views
1

J'ai une application (plusieurs actuellement) qui décode les données JSON dans une carte en utilisant Jackson. Les données semblent être dans une Map ou une ArrayList (dans le cas de tableaux JSON). Les données qui arrivent sur ces flux ne sont pas structurées, donc cela ne changera pas.Extension de l'abstraction associative de Clojure aux types de bibliothèques Java

Je possède du code Clojure qui accède aux propriétés imbriquées dans ces objets. Idéalement, je voudrais étendre l'abstraction associative à ces types Java afin que get-in fonctionne sur eux. Quelque chose comme le suivant:

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

Il ya deux problèmes avec ceci; Le premier étant qu'Associatif n'est pas un protocole (si c'était le cas, cela fonctionnerait). La seconde étant que les types sont déjà définis, je ne peux pas ajouter Associative avec deftype. Je suis assez nouveau dans la partie JOP interop de Clojure. Y a-t-il une méthode que je ne vois pas? Ou y at-il un protocole qui enveloppe Associative et fonctionnera avec get-in que j'ai raté?

Merci SO!

Répondre

2

La réponse est que la moitié de l'extension que vous voulez faire est déjà terminée, et l'autre moitié ne peut pas être faite. La fonction get-in appelle get, qui appelle clojure.lang.RT/get, qui appelle clojure.lang.RT/getFrom, qui appelle java.util.Map/get si le premier argument est Map. Donc, si vous avez une Java Map, puis get-in fonctionne (j'emprunte cet exemple directement à partir du doto docstring):

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

Cependant, Clojure ne dispose pas d'une mise en œuvre get pour List s qui prennent en charge RandomAccess. Vous pouvez écrire votre propre get qui fait:

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

Exemple:

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

Ensuite, vous pouvez copier la mise en œuvre de get-in il donc utiliser votre fonction personnalisée get.

Je suis sûr que ce n'est pas ce que vous voulez, mais, parce que tous les bits de code que vous écrivez devrait utiliser votreget-in plutôt que get-in de Clojure, et tout autre code qui utilise déjà get de Clojure serait ne fonctionne toujours pas avec ArrayList s. Je ne pense pas qu'il existe vraiment une bonne solution à votre problème, malheureusement.

+0

Merci Sam, entendre qu'il n'y a pas vraiment une bonne solution à ce problème est utile. Je suis très chanceux dans le fait que tout se résume à une poignée de sites d'appel pour obtenir, et ont adopté une approche similaire actuellement. Je vais probablement mincir davantage et utiliser RT/get sur votre suggestion. Merci! – Cody