преобразование объектов Java в типы Clojure

#clojure #javabeans #clojure-java-interop #edn

#clojure #javabeans #clojure-java-interop #edn

Вопрос:

Привет, в настоящее время я использую java.data (https://github.com/clojure/java.data ) для преобразования java pojos в типы, совместимые с clojure. Это не работает для вложенных объектов.

Например:

 class Abc {
   Map<String, Def> someMap;
}

Class Def {
   String b;
}
  

Если я передам образец экземпляра Abc в java.data, я получу вывод в виде:

    {
      :someMap { 
         "keyString" #object[com.sample.Def 0xb33584d "com.sample.Def@b33584d"]
      }
   }
  

Но я хочу, чтобы результат был:

    {
       :someMap { 
            "keyString" {
                 "b" "value" 
            } 
       }
   }
  

Как я могу это исправить?

Я попробовал компонент clojure.core (https://clojuredocs.org/clojure.core/bean ) и это, похоже, тоже работает.

Заранее благодарю вас.

Ответ №1:

Для того, чтобы это работало, ваши объекты Java должны соответствовать спецификации JavaBean. Это означает, что им нужны методы .getXXX() для чтения свойств объекта (по крайней мере), а также .setXXX() для создания нового объекта. Пример:

Класс Inner :

 package demo;
public class Inner {
    public String secret;
    public String getSecret() {
        return secret;
    }    
    public Inner(String arg) {
        this.secret = arg;
    }
}
  

Класс Outer :

 package demo;
import java.util.HashMap;
import demo.Inner;

public class Outer {
  public HashMap<String, Inner> someMap;

  public Outer() {
    HashMap<String,Inner> hm = new HashMap<String, Inner>();
    hm.put("stuff", new Inner( "happens"));
    hm.put("another", new Inner( "thing"));
    this.someMap = hm;
  }

  public HashMap getSomeMap() { return someMap; }
}
  

и код Clojure для декодирования вложенных объектов JavaBean:

 (ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test)
  (:require
    [clojure.java.data :as jd])
  (:import [demo Calc]))

(dotest
  (let [java-obj    (Outer.)
        obj-shallow (jd/from-java java-obj)
        obj-deep    (jd/from-java-deep java-obj {})]
    (spyx         java-obj)
    (spyx         obj-shallow)
    (spyx-pretty  obj-deep)
    ))
  

Результаты показывают, что происходит:

 --------------------------------------
   Clojure 1.10.2-alpha1    Java 14
--------------------------------------

lein test tst.demo.core
java-obj     => #object[demo.Outer 0x138d8219 "demo.Outer@138d8219"]
obj-shallow  => {:someMap {"another" #object[demo.Inner 0x8d86c4d "demo.Inner@8d86c4d"], "stuff" #object[demo.Inner 0x28c92c51 "demo.Inner@28c92c51"]}}
obj-deep     => {:someMap {"another" {:secret "thing"}, 
                           "stuff"   {:secret "happens"}}}

  

Raw непрозрачен java-obj для Clojure. Использование jd/from-java только распаковывает внешний слой с использованием JavaBean-геттеров. Использование jd/from-java-deep (обратите внимание на карту требуемых параметров, оставленную здесь пустой) рекурсивно распакует JavaBean, используя соответствующие геттеры для каждого объекта на основе его класса java.

Весь приведенный выше код основан на этом проекте шаблона. Наслаждайтесь!

Ответ №2:

Встроенная bean функция Clojure выполняет только поверхностное преобразование — она не рекурсивно преобразует какую-либо вложенную информацию. Проверьте https://github.com/clojure/java.data для рекурсивного преобразования объектов Java в структуры данных Clojure (и обратно).

java.data также поддерживаются Java API в стиле «builder», чтобы вам было проще создавать объекты Java из Clojure.