Katakanlah saya memiliki objek java yang sangat sederhana yang hanya memiliki beberapa properti getXXX dan setXXX. Objek ini digunakan hanya untuk menangani nilai, pada dasarnya adalah catatan atau peta tipe-aman (dan performant). Saya sering perlu menyembunyikan objek ini menjadi pasangan nilai kunci (baik string atau tipe aman) atau mengonversi dari pasangan nilai kunci ke objek ini.
Selain refleksi atau menulis kode secara manual untuk melakukan konversi ini, apa cara terbaik untuk mencapai ini?
Contoh mungkin mengirim objek ini melalui jms, tanpa menggunakan tipe ObjectMessage (atau mengonversi pesan masuk ke objek yang tepat).
java
reflection
collections
javabeans
Shahbaz
sumber
sumber
java.beans.Introspector
.getBeanInfo()
. Itu dibangun langsung ke JDK.Jawaban:
Selalu ada kacang apache commons tapi tentu saja itu menggunakan refleksi di bawah tenda
sumber
java.beans
dependensi yang sangat terbatas pada platform. Lihat pertanyaan terkait untuk detailnya.Banyak solusi potensial, tapi mari tambahkan satu lagi. Gunakan Jackson (lib pemrosesan JSON) untuk melakukan konversi "tanpa json", seperti:
ObjectMapper m = new ObjectMapper(); Map<String,Object> props = m.convertValue(myBean, Map.class); MyBean anotherBean = m.convertValue(props, MyBean.class);
( entri blog ini memiliki beberapa contoh lagi)
Anda pada dasarnya dapat mengonversi semua jenis yang kompatibel: artinya jika Anda memang mengonversi dari jenis ke JSON, dan dari JSON itu ke jenis hasil, entri akan cocok (jika dikonfigurasi dengan benar juga dapat mengabaikan yang tidak dikenal).
Berfungsi dengan baik untuk kasus yang diharapkan, termasuk Peta, Daftar, array, primitif, POJO seperti kacang.
sumber
BeanUtils
bagus, tetapi tidak dapat menangani array dan enumPembuatan kode akan menjadi satu-satunya cara lain yang dapat saya pikirkan. Secara pribadi, saya mendapatkan solusi refleksi yang umumnya dapat digunakan kembali (kecuali bagian kode itu benar-benar kritis terhadap kinerja). Menggunakan JMS terdengar seperti berlebihan (ketergantungan tambahan, dan itu bukan maksudnya). Selain itu, mungkin juga menggunakan refleksi di bawah kapnya.
sumber
Ini adalah metode untuk mengubah objek Java menjadi Peta
public static Map<String, Object> ConvertObjectToMap(Object obj) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class<?> pomclass = obj.getClass(); pomclass = obj.getClass(); Method[] methods = obj.getClass().getMethods(); Map<String, Object> map = new HashMap<String, Object>(); for (Method m : methods) { if (m.getName().startsWith("get") && !m.getName().startsWith("getClass")) { Object value = (Object) m.invoke(obj); map.put(m.getName().substring(3), (Object) value); } } return map; }
Ini adalah bagaimana menyebutnya
Test test = new Test() Map<String, Object> map = ConvertObjectToMap(test);
sumber
Mungkin terlambat ke pesta. Anda dapat menggunakan Jackson dan mengubahnya menjadi objek Properties. Ini cocok untuk kelas bersarang dan jika Anda menginginkan kunci di for abc = value.
JavaPropsMapper mapper = new JavaPropsMapper(); Properties properties = mapper.writeValueAsProperties(sct); Map<Object, Object> map = properties;
jika Anda menginginkan sufiks, lakukan saja
SerializationConfig config = mapper.getSerializationConfig() .withRootName("suffix"); mapper.setConfig(config);
perlu menambahkan ketergantungan ini
sumber
Dengan Java 8 Anda dapat mencoba ini:
public Map<String, Object> toKeyValuePairs(Object instance) { return Arrays.stream(Bean.class.getDeclaredMethods()) .collect(Collectors.toMap( Method::getName, m -> { try { Object result = m.invoke(instance); return result != null ? result : ""; } catch (Exception e) { return ""; } })); }
sumber
JSON , misalnya menggunakan XStream + Jettison, adalah format teks sederhana dengan pasangan nilai kunci. Ini didukung misalnya oleh broker pesan Apache ActiveMQ JMS untuk pertukaran objek Java dengan platform / bahasa lain.
sumber
Cukup menggunakan refleksi dan Groovy:
def Map toMap(object) { return object?.properties.findAll{ (it.key != 'class') }.collectEntries { it.value == null || it.value instanceof Serializable ? [it.key, it.value] : [it.key, toMap(it.value)] } } def toObject(map, obj) { map.each { def field = obj.class.getDeclaredField(it.key) if (it.value != null) { if (field.getType().equals(it.value.class)){ obj."$it.key" = it.value }else if (it.value instanceof Map){ def objectFieldValue = obj."$it.key" def fieldValue = (objectFieldValue == null) ? field.getType().newInstance() : objectFieldValue obj."$it.key" = toObject(it.value,fieldValue) } } } return obj; }
sumber
Gunakan BeanWrapper juffrou-reflect . Ini sangat bagus.
Berikut adalah cara mengubah kacang menjadi peta:
public static Map<String, Object> getBeanMap(Object bean) { Map<String, Object> beanMap = new HashMap<String, Object>(); BeanWrapper beanWrapper = new BeanWrapper(BeanWrapperContext.create(bean.getClass())); for(String propertyName : beanWrapper.getPropertyNames()) beanMap.put(propertyName, beanWrapper.getValue(propertyName)); return beanMap; }
Saya mengembangkan Juffrou sendiri. Ini open source, jadi Anda bebas menggunakannya dan memodifikasi. Dan jika Anda memiliki pertanyaan tentang itu, saya akan dengan senang hati menjawabnya.
Bersulang
Carlos
sumber
Saat menggunakan Spring, seseorang juga dapat menggunakan trafo objek-ke-peta-integrasi Spring. Mungkin tidak ada gunanya menambahkan Spring sebagai dependensi hanya untuk ini.
Untuk dokumentasi, cari "Object-to-Map Transformer" di http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html
Pada dasarnya, ini melintasi seluruh grafik objek yang dapat dijangkau dari objek yang diberikan sebagai input, dan menghasilkan peta dari semua bidang tipe / String primitif pada objek. Ini dapat dikonfigurasi untuk menghasilkan:
Berikut contoh dari halaman mereka:
public class Parent{ private Child child; private String name; // setters and getters are omitted } public class Child{ private String name; private List<String> nickNames; // setters and getters are omitted }
Outputnya adalah:
Trafo balik juga tersedia.
sumber
Anda bisa menggunakan kerangka Joda:
http://joda.sourceforge.net/
dan memanfaatkan JodaProperties. Ini memang menetapkan bahwa Anda membuat kacang dengan cara tertentu, dan menerapkan antarmuka tertentu. Namun, itu memungkinkan Anda untuk mengembalikan peta properti dari kelas tertentu, tanpa refleksi. Kode sampel ada di sini:
http://pbin.oogly.co.uk/listings/viewlistingdetail/0e78eb6c76d071b4e22bbcac748c57
sumber
Jika Anda tidak ingin melakukan panggilan hardcode ke setiap pengambil dan penyetel, refleksi adalah satu-satunya cara untuk memanggil metode ini (tetapi tidak sulit).
Dapatkah Anda merefaktor kelas yang dimaksud untuk menggunakan objek Properties untuk menyimpan data aktual, dan membiarkan setiap pengambil dan penyetel hanya memanggil get / set di atasnya? Kemudian Anda memiliki struktur yang sesuai untuk apa yang ingin Anda lakukan. Bahkan ada metode untuk menyimpan dan memuatnya dalam bentuk nilai kunci.
sumber
Solusi terbaik adalah dengan menggunakan Dozer. Anda hanya membutuhkan sesuatu seperti ini di file mapper:
<mapping map-id="myTestMapping"> <class-a>org.dozer.vo.map.SomeComplexType</class-a> <class-b>java.util.Map</class-b> </mapping>
Dan hanya itu, Dozer akan mengurus sisanya !!!
URL Dokumentasi Dozer
sumber
Tentu saja ada cara konversi paling sederhana yang mungkin - tidak ada konversi sama sekali!
daripada menggunakan variabel privat yang ditentukan di kelas, buat kelas hanya berisi HashMap yang menyimpan nilai untuk instance.
Kemudian pengambil dan penyetel Anda kembali dan menetapkan nilai ke dalam dan ke luar HashMap, dan ketika saatnya untuk mengubahnya menjadi peta, voila! - itu sudah menjadi peta.
Dengan sedikit sihir AOP, Anda bahkan dapat mempertahankan ketidakfleksibelan yang melekat pada kacang dengan memungkinkan Anda untuk tetap menggunakan getter dan setter yang spesifik untuk setiap nama nilai, tanpa harus benar-benar menulis getter dan setter individual.
sumber
Anda dapat menggunakan properti pengumpul filter aliran java 8,
public Map<String, Object> objectToMap(Object obj) { return Arrays.stream(YourBean.class.getDeclaredMethods()) .filter(p -> !p.getName().startsWith("set")) .filter(p -> !p.getName().startsWith("getClass")) .filter(p -> !p.getName().startsWith("setClass")) .collect(Collectors.toMap( d -> d.getName().substring(3), m -> { try { Object result = m.invoke(obj); return result; } catch (Exception e) { return ""; } }, (p1, p2) -> p1) ); }
sumber
Prosesor Anotasi Kacang JavaDude saya menghasilkan kode untuk melakukan ini.
http://javadude.googlecode.com
Sebagai contoh:
@Bean( createPropertyMap=true, properties={ @Property(name="name"), @Property(name="phone", bound=true), @Property(name="friend", type=Person.class, kind=PropertyKind.LIST) } ) public class Person extends PersonGen {}
Di atas menghasilkan superclass PersonGen yang menyertakan metode createPropertyMap () yang menghasilkan Peta untuk semua properti yang ditentukan menggunakan @Bean.
(Perhatikan bahwa saya mengubah API sedikit untuk versi berikutnya - atribut anotasi akan menjadi defineCreatePropertyMap = true)
sumber
Anda harus menulis Layanan transformasi generik! Gunakan obat generik untuk membuatnya tetap bebas tipe (sehingga Anda dapat mengonversi setiap objek menjadi key => value dan back).
Bidang apa yang harus menjadi kuncinya? Dapatkan bidang itu dari kacang dan tambahkan nilai non-sementara lainnya di peta nilai.
Jalan kembali cukup mudah. Baca kunci (x) dan tulis terlebih dahulu kunci tersebut dan kemudian setiap entri daftar kembali ke objek baru.
Anda bisa mendapatkan nama properti kacang dengan apache commons beanutils !
sumber
Jika Anda benar-benar menginginkan kinerja, Anda dapat pergi ke jalur pembuatan kode.
Anda dapat melakukan ini sendiri dengan melakukan refleksi Anda sendiri dan membuat mixin AspectJ ITD.
Atau Anda dapat menggunakan Spring Roo dan membuat Spring Roo Addon . Addon Roo Anda akan melakukan hal serupa di atas, tetapi akan tersedia untuk semua orang yang menggunakan Spring Roo dan Anda tidak perlu menggunakan Anotasi Waktu Proses.
Saya telah melakukan keduanya. Orang-orang tidak suka Spring Roo tetapi itu benar-benar pembuatan kode paling komprehensif untuk Java.
sumber
Satu cara lain yang mungkin ada di sini.
BeanWrapper menawarkan fungsionalitas untuk mengatur dan mendapatkan nilai properti (secara individual atau massal), mendapatkan deskriptor properti, dan meminta properti untuk menentukan apakah mereka dapat dibaca atau ditulis.
Company c = new Company(); BeanWrapper bwComp = BeanWrapperImpl(c); bwComp.setPropertyValue("name", "your Company");
sumber
Jika berkaitan dengan pohon objek sederhana untuk pemetaan daftar nilai kunci, di mana kuncinya mungkin berupa deskripsi jalur bertitik dari elemen akar objek ke daun yang sedang diperiksa, agak jelas bahwa konversi pohon ke daftar nilai kunci sebanding dengan objek untuk pemetaan xml. Setiap elemen dalam dokumen XML memiliki posisi yang ditentukan dan dapat diubah menjadi jalur. Oleh karena itu saya mengambil XStream sebagai alat konversi dasar dan stabil dan mengganti driver hierarkis dan bagian penulis dengan implementasi sendiri. XStream juga dilengkapi dengan mekanisme pelacakan jalur dasar yang - digabungkan dengan dua lainnya - secara ketat mengarah pada solusi yang sesuai untuk tugas tersebut.
sumber
Dengan bantuan perpustakaan Jackson, saya dapat menemukan semua properti kelas tipe String / integer / double, dan nilai masing-masing dalam kelas Peta. ( tanpa menggunakan api refleksi! )
TestClass testObject = new TestClass(); com.fasterxml.jackson.databind.ObjectMapper m = new com.fasterxml.jackson.databind.ObjectMapper(); Map<String,Object> props = m.convertValue(testObject, Map.class); for(Map.Entry<String, Object> entry : props.entrySet()){ if(entry.getValue() instanceof String || entry.getValue() instanceof Integer || entry.getValue() instanceof Double){ System.out.println(entry.getKey() + "-->" + entry.getValue()); } }
sumber
Dengan menggunakan Gson,
object
ke JsonUbah Json ke Map
retMap = new Gson().fromJson(new Gson().toJson(object), new TypeToken<HashMap<String, Object>>() {}.getType() );
sumber
Kita dapat menggunakan perpustakaan Jackson untuk mengubah objek Java menjadi Peta dengan mudah.
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.6.3</version> </dependency>
Jika menggunakan dalam proyek Android, Anda dapat menambahkan jackson di build.gradle aplikasi Anda sebagai berikut:
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
Implementasi Sampel
public class Employee { private String name; private int id; private List<String> skillSet; // getters setters } public class ObjectToMap { public static void main(String[] args) { ObjectMapper objectMapper = new ObjectMapper(); Employee emp = new Employee(); emp.setName("XYZ"); emp.setId(1011); emp.setSkillSet(Arrays.asList("python","java")); // object -> Map Map<String, Object> map = objectMapper.convertValue(emp, Map.class); System.out.println(map); } }
Keluaran:
{name = XYZ, id = 1011, skill = [python, java]}
sumber