2014-09-02 97 views
7

Java Bean'u Map'a dönüştürmede takılıyorum. İnternette birçok kaynak var, ama maalesef hepsi basit fasulyeleri Haritalar'a dönüştürüyor. Benimkiler biraz daha geniş.Java Bean'i bir Haritaya Düzleştirme

basitleştirilmiş bir örnek vardır:

public class MyBean { 

    private String firstName; 
    private String lastName; 
    private MyHomeAddress homeAddress; 
    private int age; 

    // getters & setters 

} 

Benim demek bu durumda, aşağıdaki koşullar için de geçerlidir, Map<String, Object> üretmektir:

map.containsKey("firstName") 
map.containsKey("lastName") 
map.containsKey("homeAddress.street") // street is String 
map.containsKey("homeAddress.number") // number is int 
map.containsKey("homeAddress.city") // city is String 
map.containsKey("homeAddress.zipcode") // zipcode is String 
map.containsKey("age") 

denedim Apache Commons BeanUtils kullanarak. Her iki yaklaşım da BeanUtils#describe(Object) ve BeanMap(Object), "derin düzey" 1 olan bir Harita üretir (yaninesnesini bir değer olarak tutan "homeAddress" anahtar vardır). Benim yöntemim, bir ilkel tür (veya Dize) karşılayana kadar nesneleri daha derin ve daha derin girmeli, o zaman kazmayı durdurmalı ve anahtar eklemek gerekir "order.customer.contactInfo.home".

Yani, sorum şu: Nasıl yapılmalı (ya da zaten bunu yapmamı sağlayacak mevcut bir proje var)? Burada basit yansıtıcı/özyinelemeli örnektir

private static boolean isValue(Object value) { 
    final Class<?> clazz = value.getClass(); 
    if (value == null || 
     valueClasses.contains(clazz) || 
     Collection.class.isAssignableFrom(clazz) || 
     Map.class.isAssignableFrom(clazz) || 
     value.getClass().isArray() || 
     value.getClass().isEnum()) { 
    return true; 
    } 
    return false; 
} 
+0

Büyük ihtimalle "ilkel" anlamına gelmez, çünkü "String" ilkel değildir ('Object' genişletir). Bu nedenle, hangi sınıfların geçiş yapacağı ve değerleri nasıl alacağı algoritmasını anlamanız için bir yönteme ihtiyacınız vardır, bu yüzden muhtemelen bir yapılandırma yapmadan (belki ek açıklamalarla) bunu yapmanın bir yolu olmayacaktır. – Tonio

+1

Bu yansıma ve yineleme ile yapılabilir, sadece kesinlikle kendiniz yazmanız gerekir. Şu anda kütüphane önerileri için soru sorulmadığı için sorunun kapanacağını unutmayın. – Radiodef

+0

Tonio, Radiodef - önerileriniz için teşekkürler, mesajımı düzenledim. –

cevap

5

:

güncelleme

Ben de Koleksiyonlar, Haritalar Dizileri ve numaralamalar içerecek şekilde Radiodef cevabını genişletmiştir.

  • Harita tuşları benzersiz olmalıdır:

    Bir dönüşüme Sorduğunuz şekilde yaparak bazı sorunlar olduğunu bilmelidir.

  • Java, sınıfların kendi özel alanlarına, devralınan bir sınıfa ait bir özel alanla aynı adı vermelerini sağlar.

Bu örnek, bunlara yönelik değil, çünkü bunları nasıl hesaplamak istediğinizden emin değilim (eğer yaparsanız). Fasulyeleriniz Object'dan başka bir şeyden miras kalıyorsa, fikrinizi biraz değiştirmelisiniz. Bu örnek sadece alt sınıfın alanlarını dikkate alır. Başka bir deyişle

, sen

public class SubBean extends Bean { 

varsa bu örnek sadece SubBean alanları dönecektir.

Java bize bu deli şeyi yapalım:

herkes bu yapıyor olmalı
package com.company.util; 
public class Bean { 
    private int value; 
} 

package com.company.misc; 
public class Bean extends com.company.util.Bean { 
    private int value; 
} 

O değil, ancak tuş olarak Dize kullanmak istiyorsanız bu bir problem.

import java.lang.reflect.*; 
import java.util.*; 

public final class BeanFlattener { 
    private BeanFlattener() {} 

    public static Map<String, Object> deepToMap(Object bean) 
    throws IllegalAccessException { 
     Map<String, Object> map = new LinkedHashMap<>(); 

     putValues(bean, map, null); 

     return map; 
    } 

    private static void putValues(
     Object bean, Map<String, Object> map, String prefix 
    ) throws IllegalAccessException { 
     Class<?> cls = bean.getClass(); 

     for(Field field : cls.getDeclaredFields()) { 
      field.setAccessible(true); 

      Object value = field.get(bean); 
      String key; 
      if(prefix == null) { 
       key = field.getName(); 
      } else { 
       key = prefix + "." + field.getName(); 
      } 

      if(isValue(value)) { 
       map.put(key, value); 
      } else { 
       putValues(value, map, key); 
      } 
     } 
    } 

    private static final Set<Class<?>> valueClasses = (
     Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
      Object.class, String.class, Boolean.class, 
      Character.class, Byte.class, Short.class, 
      Integer.class, Long.class, Float.class, 
      Double.class 
     ))) 
    ); 

    private static boolean isValue(Object value) { 
     return value == null || valueClasses.contains(value.getClass()); 
    } 
} 
+0

Teşekkür ederim, bu uygulama cazibe gibi çalışır. Projemde ihtiyaç duyulan başka bir dersi de dahil etmek için 'isValue (Object)' yöntemini genişlettim (yazıma bakın). –

+0

Büyük bir kod @Radiodef parçası için teşekkür ederiz. Bu kod için bazı birim testleri yazdım ve bunu bir Api'de kullanmayı planlıyorum, bu kodda kimsenin bir hatayla karşılaştığını merak ediyordum? – AngelThread

+1

@AngelThread Sadece burada yine benim kod üzerinde bakarak ve gördüğüm tek bariz sorun döngüsel bir başvuru içeren bir nesne geçirilen eğer class A {B b 'örneğin,' OutOfMemoryError' bir 'StackOverflowError' ya neden olmasıdır; } 've' B sınıfı {A a; } '. Böyle bir nesne bir fasülye olarak düşünülemez, ama eğer bu yöntemi tekrar tekrar yazsaydım, bunun gibi olayları ele alırdım. Ben bir "Set değerleri" olduğunu düşünüyorum ve bir değer türü olmayan bir alan her eklediğinizde, önce değeri önceden eklenip eklenmediğini görmek için ilk önce 'Set' i kontrol edersiniz. Eğer sette ise, tekrarlamıyorsunuz. – Radiodef

1

Hep Jackson Json Processor kullanabilirsiniz: Burada

teh codez olduğunu.Şunun gibi:

pojo bazı Java fasulye olduğunu
import com.fasterxml.jackson.databind.ObjectMapper; 
//... 
ObjectMapper objectMapper = new ObjectMapper(); 
//... 
@SuppressWarnings("unchecked") 
Map<String, Object> map = objectMapper.convertValue(pojo, Map.class); 

. Serileşmeyi kontrol etmek için fasülyede bazı güzel ek açıklamaları kullanabilirsiniz.

ObjectMapper'ı yeniden kullanabilirsiniz.

+0

convertValue() yöntemi iç nesneleri düzleştirmez, örneğin bir özniteliğe sahip olan bir Cat sınıfım var ve bu öznitelik ayrıca Cat tipidir. Cat sınıfının örneğini convertValue yöntemiyle dönüştürürsem, aşağıdaki gibi olacaktır. {friend = {friend = null, childCount = 2, name = sarı}, childCount = 1, ad = kırmızı} – AngelThread

+0

@AngelThread Düzleştirme gerçekleştirilmez, ancak iç nesneler türlerine bağlı olarak dönüştürülebilir (Örneğin, iç haritalar) . Ve Jackson'ı bir Harita hiyerarşisi oluşturmaya da yapılandırmak mümkündür, ancak bu sorunun kapsamı dışındadır. – dlaidlaw

+0

Daha fazla katkı yaptığınız için teşekkür ederim, bu kod parçasının bir yanını göstermeye çalışıyordum. – AngelThread