2017-06-20 1 views
0

J'ai une classe Java générée par Protocol Buffers appelée TextLine. Quand j'instancier l'objet Java avec:java.lang.StackOverflowError dans clojure.java.data from-java

(def tb (-> (TextLine/newBuilder) (.setText "this is a text line") (.build))) 

Et puis appelez:

(from-java tb) 

Je reçois un StackOverflowError:

java.lang.StackOverflowError: null 
at java.lang.Class.getMethods (Class.java:1614) 
clojure.lang.Reflector.getMethods (Reflector.java:373) 
clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:311) 
clojure.java.data$add_getter_fn.invokeStatic (data.clj:38) 
clojure.java.data$add_getter_fn.invoke (data.clj:37) 
clojure.core.protocols$fn__6755.invokeStatic (protocols.clj:167) 
clojure.core.protocols/fn (protocols.clj:124) 
clojure.core.protocols$fn__6710$G__6705__6719.invoke (protocols.clj:19) 
clojure.core.protocols$seq_reduce.invokeStatic (protocols.clj:31) 
clojure.core.protocols$fn__6732.invokeStatic (protocols.clj:75) 
clojure.core.protocols/fn (protocols.clj:75) 
clojure.core.protocols$fn__6684$G__6679__6697.invoke (protocols.clj:13) 
clojure.core$reduce.invokeStatic (core.clj:6545) 
clojure.core$reduce.invoke (core.clj:6527) 
clojure.java.data$eval554$fn__555.invoke (data.clj:135) 
clojure.lang.MultiFn.invoke (MultiFn.java:229) 
clojure.java.data$make_getter_fn$fn__501.invoke (data.clj:35) 
clojure.java.data$eval554$fn__555$iter__556__560$fn__561.invoke (data.clj:136) 
clojure.lang.LazySeq.sval (LazySeq.java:40) 
clojure.lang.LazySeq.seq (LazySeq.java:49) 
clojure.lang.Cons.next (Cons.java:39) 
clojure.lang.RT.next (RT.java:688) 
clojure.core$next__4341.invokeStatic (core.clj:64) 
clojure.core.protocols$fn__6755.invokeStatic (protocols.clj:168) 
clojure.core.protocols/fn (protocols.clj:124) 
clojure.core.protocols$fn__6710$G__6705__6719.invoke (protocols.clj:19) 
clojure.core.protocols$seq_reduce.invokeStatic (protocols.clj:31) 
clojure.core.protocols$fn__6738.invokeStatic (protocols.clj:75) 
clojure.core.protocols/fn (protocols.clj:75) 
clojure.core.protocols$fn__6684$G__6679__6697.invoke (protocols.clj:13) 
clojure.core$reduce.invokeStatic (core.clj:6545) 
clojure.core$into.invokeStatic (core.clj:6610) 
clojure.core$into.invoke (core.clj:6604) 
clojure.java.data$eval554$fn__555.invoke (data.clj:136) 
clojure.lang.MultiFn.invoke (MultiFn.java:229) 
clojure.java.data$make_getter_fn$fn__501.invoke (data.clj:35) 
clojure.java.data$eval554$fn__555$iter__556__560$fn__561.invoke (data.clj:136) 
clojure.lang.LazySeq.sval (LazySeq.java:40) 
clojure.lang.LazySeq.seq (LazySeq.java:49) 
clojure.lang.Cons.next (Cons.java:39) 
clojure.lang.RT.next (RT.java:688) 
clojure.core$next__4341.invokeStatic (core.clj:64) 
clojure.core.protocols$fn__6755.invokeStatic (protocols.clj:168) 
clojure.core.protocols/fn (protocols.clj:124) 
clojure.core.protocols$fn__6710$G__6705__6719.invoke (protocols.clj:19) 
clojure.core.protocols$seq_reduce.invokeStatic (protocols.clj:31) 
clojure.core.protocols$fn__6738.invokeStatic (protocols.clj:75) 
clojure.core.protocols/fn (protocols.clj:75) 
clojure.core.protocols$fn__6684$G__6679__6697.invoke (protocols.clj:13) 
clojure.core$reduce.invokeStatic (core.clj:6545) 
clojure.core$into.invokeStatic (core.clj:6610) 
clojure.core$into.invoke (core.clj:6604) 
clojure.java.data$eval554$fn__555.invoke (data.clj:136) 
clojure.lang.MultiFn.invoke (MultiFn.java:229) 
clojure.java.data$make_getter_fn$fn__501.invoke (data.clj:35) 
clojure.java.data$eval554$fn__555$iter__556__560$fn__561.invoke (data.clj:136) 
clojure.lang.LazySeq.sval (LazySeq.java:40) 
clojure.lang.LazySeq.seq (LazySeq.java:49) 
clojure.lang.Cons.next (Cons.java:39) 
clojure.lang.RT.next (RT.java:688) 
clojure.core$next__4341.invokeStatic (core.clj:64) 
clojure.core.protocols$fn__6755.invokeStatic (protocols.clj:168) 
clojure.core.protocols/fn (protocols.clj:124) 
clojure.core.protocols$fn__6710$G__6705__6719.invoke (protocols.clj:19) 
.... 

Toutes les idées sur ce qui pourrait être la cause ou la meilleure façon pour résoudre le problème? J'aimerais vraiment m'interfacer avec l'objet Java en tant que carte Clojure.

+0

Vous pouvez poser cette question sur la liste de diffusion de Google Groupes [email protected] –

Répondre

3

Je ne recommanderais pas d'utiliser clojure.data.java/from-java pour beaucoup de choses. L'idée qu'une simple fonction peut traduire un objet Java arbitraire en une carte Clojure raisonnable sans aucune connaissance du domaine de l'objet source est un vœu pieux.

Je n'en avais pas entendu parler avant aujourd'hui, mais je suis allé regarder la source et comme prévu, il s'agit essentiellement d'une extension de clojure.core/bean, une autre tentative pleine d'espoir pour un problème impossible. Spécifiquement, il utilise l'introspection javabéenne pour essayer de deviner ce que les getters et les setters représentent des champs significatifs. Maintenant, cependant, comme de nombreuses classes Java qui n'ont pas été conçues pour être utilisées comme beans, les classes protobuf contiennent des références circulaires, ce qui signifie que les hacker récursivement est une tâche infinie, débouchant finalement sur un débordement de pile.

Que faire à la place? Je recommanderais simplement de travailler avec les classes Java protobuf générées via Java interop, ou peut-être essayer de trouver une bonne bibliothèque Protobuf Clojure. N'essayez pas de convertir les objets Java en données Clojure idiomatiques.

+0

Merci pour votre patience! Je suis sûr que tu m'as sauvé des jours de frustration. J'ai trouvé ce qui semble être une [bibliothèque Clojure protobuf] de bonne réputation (https://github.com/ninjudd/clojure-protobuf). Seriez-vous capable de savoir s'il supporte proto3? Aussi, d'après votre expérience, serait-il préférable de travailler avec les classes Java protobuf au lieu de quelque chose comme cette bibliothèque? – frank

+0

Il y a quelques années, j'ai écrit un morceau de code qui pouvait convertir des arborescences d'objets Apache Thrift plus ou moins génériques en structures de données Clojure. Au lieu d'énumérer les champs dans les classes générées, j'ai utilisé les métadonnées qui étaient disponibles dans certains objets générés par Thrift. Je pourrais obtenir les champs "business" et leurs types pour chaque classe. Ainsi j'ai évité des champs supplémentaires qui étaient là pour le besoin de Thrift seulement. Peut-être qu'une approche similaire pourrait aussi être appliquée à Protobuf. – ez121sl

+0

@ ez121sl Oui, c'est exactement ce que ferait une bonne bibliothèque de prototypes pour Clojure, et cela fonctionne parce que protobuf, comme l'épargne, a suffisamment de métadonnées pour décrire l'objet de manière significative. – amalloy