Bir Java nesnesini (bean) anahtar-değer çiftlerine (ve tersi) nasıl dönüştürebilirim?


93

Sadece bazı getXXX ve setXXX özelliklerine sahip çok basit bir java nesnem olduğunu varsayalım. Bu nesne, temelde bir kayıt veya tür açısından güvenli (ve performans gösteren) bir harita gibi yalnızca değerleri işlemek için kullanılır. Sık sık bu nesneyi anahtar değer çiftlerine (dizeler veya kasa tipi) dönüştürmem veya anahtar değer çiftlerinden bu nesneye dönüştürmem gerekir.

Bu dönüşümü yapmak için yansıtma veya manuel olarak kod yazma dışında, bunu başarmanın en iyi yolu nedir?

Bir örnek, bu nesneyi ObjectMessage türünü kullanmadan jms üzerinden göndermek (veya gelen bir mesajı doğru türdeki bir nesneye dönüştürmek) olabilir.


java.beans.Introspector.getBeanInfo(). Doğrudan JDK'nın içine yerleştirilmiştir.
user207421

Yanıtlar:


53

Her zaman apache commons beanutils vardır, ancak elbette başlık altında


8
Üstelik kaputun altında yansımanın kullanılmasında yanlış bir şey yok. Özellikle sonuçlar önbelleğe alınırsa (ileride kullanılmak üzere), yansıma tabanlı erişim genellikle çoğu kullanım durumu için yeterince hızlıdır.
StaxMan

22
Kesin olarak söylemek gerekirse, BeanMap (fasulye), hile yapan ortak beanutillerin belirli bir parçasıdır.
vdr

3
Performansla ilgili hususları göz ardı ederek, BeanMap () 'i aşağıdaki cevaptan Jackson's ObjectMapper ile birlikte kullanmanın bana en iyi sonuçları verdiğini buldum. BeanMap, nesnemin daha eksiksiz bir haritasını oluşturmayı başardı ve ardından Jackson, onu ardışık düzen boyunca değişikliklere izin veren düz LinkedHashMap yapısına dönüştürdü. objectAsMap = objectMapper.convertValue (yeni BeanMap (myObject), Map.class);
michaelr524

java.beansPlatform üzerinde ciddi şekilde sınırlı bağımlılıklar kullandığı için Android üzerinde çalışmaz . Ayrıntılar için ilgili soruya bakın.
SqueezyMo

Apache Commons'tan bahseden bir çözüm, çoğu zaman en iyi çözümdür.
рüффп

179

Pek çok potansiyel çözüm var, ancak bir tane daha ekleyelim. Kullanım Jackson (JSON işleme lib) gibi "json-az" dönüştürme yapmak için:

ObjectMapper m = new ObjectMapper();
Map<String,Object> props = m.convertValue(myBean, Map.class);
MyBean anotherBean = m.convertValue(props, MyBean.class);

( bu blog girişinde daha fazla örnek var)

Temel olarak herhangi bir uyumlu türü dönüştürebilirsiniz: uyumlu, türden JSON'a ve bu JSON'dan sonuç türüne dönüştürme yaptıysanız, girişlerin eşleşeceği anlamına gelir (uygun şekilde yapılandırılırsa, tanınmayanları da yok sayabilir).

Haritalar, Listeler, diziler, ilkeller, fasulye benzeri POJO'lar dahil olmak üzere beklenebilecek durumlarda iyi çalışır.


2
Kabul edilen cevap bu olmalıdır. BeanUtilsgüzel, ancak dizileri ve numaralandırmaları kaldıramıyor
Manish Patel

8

Kod oluşturma, düşünebildiğim tek başka yol olabilir. Şahsen, genel olarak yeniden kullanılabilir bir yansıtma çözümüne sahiptim (kodun bu kısmı kesinlikle performans açısından kritik değilse). JMS kullanmak kulağa aşırılık gibi geliyor (ek bağımlılık ve bunun anlamı bile bu değil). Ayrıca, muhtemelen kaputun altında da yansıma kullanıyor.


Performans kritiktir, bu yüzden yansımanın ortaya çıktığını düşünüyorum. Asm veya cglib kullanan bazı araçlar olabileceğini umuyordum. Google'ın protokol arabelleklerine tekrar bakmaya başladım. JMS hakkındaki yorumunuzu almadım, bunu makineler arasında bilgi iletmek için kullanıyorum.
Shahbaz

Bunu yanlış anladım. Performansa gelince, performansın önemli olması ile bu belirli parçanın performans açısından kritik olması arasında bir fark vardır. Uygulamanın başka ne yaptığına kıyasla gerçekten ne kadar önemli olduğunu görmek için bir kıyaslama yapardım.
Michael Borgwardt

Benim düşüncem de bu. Ya yansıma kullanırsınız (doğrudan ya da dolaylı olarak) ya da kod oluşturmanız gerekir. (Veya ekstra kodu elbette kendiniz yazın).
extraneon

8

Bu, bir Java nesnesini Haritaya dönüştürmek için bir yöntemdir

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;
}

Bu nasıl adlandırılır

   Test test = new Test()
   Map<String, Object> map = ConvertObjectToMap(test);

1
İstenilen cevabın her nesnede tanımlanan yöntemlerin üzerinde yinelendiğini sanmıyorum, yansıma gibi geliyor.
Robert Karl

2
Amaçlarınızın yönteminiz olduğunu bilmiyorum. Ancak genel nesne türleriyle programlama yapmak istediğinizde bunun mükemmel bir örnek olduğunu düşünüyorum
DeXoN

6

Muhtemelen partiye geç kalmıştır. Jackson'ı kullanabilir ve bunu bir Özellikler nesnesine dönüştürebilirsiniz. Bu, Yuvalanmış sınıflar için ve for abc = değerindeki anahtarı istiyorsanız uygundur.

JavaPropsMapper mapper = new JavaPropsMapper();
Properties properties = mapper.writeValueAsProperties(sct);
Map<Object, Object> map = properties;

Eğer bir sonek istiyorsanız, o zaman sadece

SerializationConfig config = mapper.getSerializationConfig()
                .withRootName("suffix");
mapper.setConfig(config);

bu bağımlılığı eklemeniz gerekiyor

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-properties</artifactId>
</dependency>

4

Java 8 ile şunu deneyebilirsiniz:

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 "";
                        }
                    }));
}

3

JSON , örneğin XStream + Jettison kullanan, anahtar-değer çiftlerine sahip basit bir metin formatıdır. Örneğin, diğer platformlarla / dillerle Java nesne değişimi için Apache ActiveMQ JMS mesaj aracısı tarafından desteklenir.


3

Yansıma ve Groovy kullanarak:

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;
}

Havalı ama StackOverflowError'a eğilimli
Teo Choong Ping

3

Juffrou-reflektörün BeanWrapper'ını kullanın . Çok başarılı.

Bir fasulyeyi haritaya nasıl dönüştürebileceğiniz aşağıda açıklanmıştır:

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;
}

Juffrou'yu kendim geliştirdim. Açık kaynaktır, bu yüzden onu kullanmakta ve değiştirmekte özgürsünüz. Ve bununla ilgili herhangi bir sorunuz varsa, cevap vermekten çok mutlu olacağım.

Şerefe

Carlos


Bunu düşündüm ve fasulye grafiğinizde dairesel referanslar varsa önceki çözümümün kırıldığını fark ettim. Bu yüzden bunu şeffaf bir şekilde yapacak ve döngüsel referansları güzelce işleyecek bir yöntem geliştirdim. Artık bir fasulyeyi bir haritaya dönüştürebilir ve tam tersini juffrou-Refle ile yapabilirsiniz. Keyfini çıkarın :)
Martins

3

Spring kullanılırken, Spring Integration nesnesi-harita-trafosu da kullanılabilir. Spring'i bağımlılık olarak eklemeye muhtemelen sadece bunun için değmez.

Dokümantasyon için http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html adresinde " Nesneden Haritaya Dönüştürücü" araması yapın.

Esasen, girdi olarak verilen nesneden erişilebilen tüm nesne grafiğini dolaşır ve nesneler üzerindeki tüm ilkel tip / String alanlarından bir harita üretir. Aşağıdakilerden biri çıktı verecek şekilde yapılandırılabilir:

  • düz bir harita: {rootObject.someField = Joe, rootObject.leafObject.someField = Jane} veya
  • yapılandırılmış bir harita: {someField = Joe, leafObject = {someField = Jane}}.

İşte kendi sayfalarından bir örnek:

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
}

Çıktı şu şekilde olacaktır:

{person.name = George, person.child.name = Jenna, person.child.nickNames [0] = Bimbo. . . vb}

Ters transformatör de mevcuttur.


2

Joda çerçevesini kullanabilirsiniz:

http://joda.sourceforge.net/

ve JodaProperties'den yararlanın. Bu, fasulyeleri belirli bir şekilde oluşturmanızı ve belirli bir arayüz uygulamanızı şart koşar. Bununla birlikte, belirli bir sınıftan bir özellik haritasını yansıtma yapmadan döndürmenize izin verir. Örnek kod burada:

http://pbin.oogly.co.uk/listings/viewlistingdetail/0e78eb6c76d071b4e22bbcac748c57


İlginç görünüyor, maalesef artık bakımlı gibi görünmüyor. Ayrıca, döndürdüğü özellik haritası bir <String, String> haritasıdır, bu nedenle güvenli yazılmaz.
Shahbaz

1
Doğru, 2002'den beri sürdürülmedi. Düşünceye dayalı olmayan bir çözümün var olup olmadığını merak ediyordum. Döndürdüğü mülk haritası aslında sadece standart bir harita, böyle bir jenerik yok ...
Jon

2

Her alıcıya ve ayarlayıcıya çağrıları kodlamak istemiyorsanız, yansıtma bu yöntemleri çağırmanın tek yoludur (ancak zor değildir).

Gerçek verileri tutmak için bir Properties nesnesini kullanmak için söz konusu sınıfı yeniden düzenleyebilir ve her alıcı ve ayarlayıcının üzerinde get / set çağırmasına izin verebilir misiniz? O zaman yapmak istediklerinize çok uygun bir yapıya sahip olursunuz. Bunları kaydetme ve anahtar-değer biçiminde yükleme yöntemleri bile vardır.


Yöntem yoktur, çünkü neyin anahtar neyin değer olduğuna size bağlıdır! Böyle bir dönüşüm yapan bir servis yazmak kolay olmalı. Basit bir temsil için CSV kabul edilebilir.
Martin K.

2

En iyi çözüm Dozer kullanmaktır. Eşleştirici dosyasında buna benzer bir şeye ihtiyacınız var:

<mapping map-id="myTestMapping">
  <class-a>org.dozer.vo.map.SomeComplexType</class-a>
  <class-b>java.util.Map</class-b>
</mapping> 

İşte bu, Dozer gerisini halleder !!!

Dozer Belgeleri URL'si


Neden eşleme dosyası gerektiriyor? Nasıl dönüştürüleceğini biliyorsa, neden bu ekstra çalışmaya ihtiyaç duysun - sadece kaynak nesneyi, beklenen türü alın?
StaxMan

Tamam. Geçerli olduğundan emin
olmasam

2

Elbette mümkün olan en basit dönüştürme yöntemi var - hiç dönüştürme yok!

sınıfta tanımlanan özel değişkenleri kullanmak yerine, sınıfın yalnızca örnek için değerleri depolayan bir HashMap içermesini sağlayın.

Ardından alıcılarınız ve ayarlayıcılarınız HashMap içine ve dışına değerleri geri döndürür ve ayarlar ve onu bir haritaya dönüştürme zamanı geldiğinde, işte! - bu zaten bir harita.

Biraz AOP sihirbazlığıyla, tek tek alıcıları ve ayarlayıcıları gerçekten yazmak zorunda kalmadan, her değer adına özgü alıcıları ve ayarlayıcıları kullanmaya devam etmenize izin vererek, bir çekirdeğin doğasında bulunan esnekliği bile koruyabilirsiniz.


2

Java 8 stream filtre toplayıcı özelliklerini kullanabilirsiniz,

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)
            );
}

1

JavaDude Bean Ek Açıklama İşlemcim bunu yapmak için kod üretir.

http://javadude.googlecode.com

Örneğin:

@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 {}

Yukarıdakiler, @Bean kullanılarak tanımlanan tüm özellikler için bir Harita oluşturan createPropertyMap () yöntemini içeren süper sınıf PersonGen'i oluşturur.

(Bir sonraki sürüm için API'yi biraz değiştirdiğimi unutmayın - ek açıklama özelliği defineCreatePropertyMap = true olacaktır)


Kod incelemeleri yaptınız mı? Bu iyi bir yaklaşım gibi görünmüyor! Ek açıklamalarla miras yolunu değiştirirsiniz! Ya fasulye zaten bir sınıfta uzarsa ?! Nitelikleri neden iki kez yazmanız gerekiyor?
Martin K.

Zaten bir üst sınıfınız varsa, @Bean (süper sınıf = XXX.class, ...) kullanırsınız ve oluşturulan süper sınıfı arasına ekler. Bu, kod incelemeleri için harikaydı - yine de elemek için potansiyel olarak hataya daha az eğilimli standart şablon.
Scott Stanchfield

"Öznitelikleri iki kez yazın" derken neyi kastettiğinizden emin değilim - nerede iki kez görünürler?
Scott Stanchfield

@Martin K.Eğer gerçekten yansıma kullanmak istemiyorsanız, kaputun altında bile, o zaman muhtemelen bir tür kod üretmeye zorlanırsınız.
extraneon

1

Genel bir dönüşüm hizmeti yazmalısınız! Yazıyı serbest tutmak için jenerikler kullanın (böylece her nesneyi anahtar => değere ve geri dönüştürebilirsiniz).

Anahtar hangi alan olmalıdır? Bu alanı çekirdekten alın ve diğer geçici olmayan değeri bir değer haritasına ekleyin.

Dönüş yolu oldukça kolay. (X) anahtarını okuyun ve önce anahtarı ardından her liste girişini yeni bir nesneye geri yazın.

Bir fasulyenin özellik adlarını apache commons beanutils ile alabilirsiniz !


1

Gerçekten performans istiyorsanız, kod oluşturma yoluna gidebilirsiniz.

Bunu, kendi düşüncenizi oluşturarak ve AspectJ ITD'de bir karışım oluşturarak yapabilirsiniz.

Veya Spring Roo'yu kullanabilir ve Spring Roo Addon yapabilirsiniz . Roo eklentiniz yukarıdakine benzer bir şey yapacak ancak Spring Roo kullanan herkes tarafından kullanılabilecek ve Runtime Ek Açıklamalarını kullanmak zorunda kalmayacaksınız.

Ben ikisini de yaptım. İnsanlar Spring Roo ile dalga geçiyor ama bu gerçekten Java için en kapsamlı kod oluşturma.


1

Bir başka olası yol da burasıdır.

BeanWrapper, özellik değerlerini (tek tek veya toplu olarak) ayarlama ve alma, özellik tanımlayıcılarını alma ve bunların okunabilir veya yazılabilir olup olmadığını belirlemek için özellikleri sorgulama işlevi sunar.

Company c = new Company();
 BeanWrapper bwComp = BeanWrapperImpl(c);
 bwComp.setPropertyValue("name", "your Company");

1

Anahtar değer listesi eşlemesine basit bir nesne ağacı gelirse, burada anahtar, nesnenin kök öğesinden incelenmekte olan yaprağa kadar noktalı bir yol açıklaması olabilir, bir anahtar-değer listesine ağaç dönüşümünün bir anahtar-değer listesine dönüştürülmesinin bir xml eşleme nesnesi. XML belgesindeki her öğenin tanımlı bir konumu vardır ve bir yola dönüştürülebilir. Bu nedenle, XStream'i temel ve kararlı bir dönüştürme aracı olarak aldım ve hiyerarşik sürücü ve yazıcı parçalarını kendi uygulamayla değiştirdim. XStream ayrıca - diğer ikisiyle birleştirildiğinde - kesinlikle göreve uygun bir çözüme götüren temel bir yol izleme mekanizmasıyla birlikte gelir.


1

Jackson kütüphanesinin yardımıyla String / integer / double türündeki tüm sınıf özelliklerini ve bir Map sınıfındaki ilgili değerleri bulabildim. ( yansımalar api kullanmadan! )

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

0

Gson kullanarak,

  1. POJO dosyasını objectJson biçimine dönüştür
  2. Json'u Haritaya Dönüştür

        retMap = new Gson().fromJson(new Gson().toJson(object), 
                new TypeToken<HashMap<String, Object>>() {}.getType()
        );
    

Merhaba, Ya ===> kurumsal: {id: 1; şirket: {id = 1, üye: {id: 1}}} Tüm benzersiz anahtar ve değer gibi haritayı alabilir miyim
Lokesh Kumar

bu senin json dizen mi? {"corporate":{"id":1,"company":{"id":1,"member":{"id":1}}}}
Prabs

@LokeshKumar Json nedir? Yeni bir soru gönderin ve bana bağlantıyı verin. Yardım etmeye çalışacağım.
Prabs

0

Bir Java nesnesini kolayca bir Haritaya dönüştürmek için Jackson kitaplığını kullanabiliriz.

<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.6.3</version>
</dependency>

Bir Android projesinde kullanıyorsanız, uygulamanızın build.gradle dosyasına aşağıdaki gibi jackson ekleyebilirsiniz:

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'

Örnek Uygulama

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

 }

}

Çıktı:

{ad = XYZ, id = 1011, beceriler = [python, java]}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.