webentwicklung-frage-antwort-db.com.de

Wie konvertiert man ein Java-Objekt (Bean) in Schlüsselwertpaare (und umgekehrt)?

Angenommen, ich habe ein sehr einfaches Java-Objekt, das nur einige Eigenschaften von getXXX und setXXX besitzt. Dieses Objekt wird nur verwendet, um Werte zu behandeln, im Wesentlichen einen Datensatz oder eine typsichere (und performante) Zuordnung. Ich muss dieses Objekt oft in Schlüsselwertpaare konvertieren (entweder Zeichenfolgen oder typsicher) oder von Schlüsselwertpaaren in dieses Objekt konvertieren.

Anders als Nachdenken oder manuelles Schreiben von Code für diese Konvertierung. Wie kann man dies am besten erreichen?

Ein Beispiel könnte das Senden dieses Objekts über jms sein, ohne den ObjectMessage-Typ zu verwenden (oder eine eingehende Nachricht in die richtige Art von Objekt zu konvertieren).

84
Shahbaz

Es gibt immer Apache Commons Beanutils , aber natürlich verwendet es Reflexion unter der Haube

48
Maurice Perry

Viele mögliche Lösungen, aber fügen wir nur eine weitere hinzu. Verwenden Sie Jackson (JSON-Verarbeitungslib), um die Konvertierung "json-less" durchzuführen, z.

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

( dieser Blogeintrag hat einige weitere Beispiele)

Sie können grundsätzlich alle kompatiblen Typen konvertieren: Kompatibel bedeutet, dass die Einträge übereinstimmen würden, wenn Sie vom Typ in JSON konvertieren und von diesem JSON zum Ergebnistyp.

Funktioniert gut für Fälle, die man erwarten würde, einschließlich Maps, Listen, Arrays, Primitives und Bean-like-POJOs.

165
StaxMan

Die Codegenerierung wäre der einzige andere Weg, den ich mir vorstellen kann. Ich persönlich hatte eine generell wiederverwendbare Reflektionslösung (es sei denn, dieser Teil des Codes ist absolut leistungskritisch). Die Verwendung von JMS klingt nach Overkill (zusätzliche Abhängigkeit, und dafür ist es noch nicht einmal gedacht). Außerdem verwendet es wahrscheinlich auch Reflexion unter der Haube.

8

Dies ist eine Methode zum Konvertieren eines Java-Objekts in eine Map

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

So nennt man es

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

Mit Java 8 können Sie Folgendes versuchen:

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

JSON , zum Beispiel mit XStream + Jettison, ist ein einfaches Textformat mit Schlüsselwertpaaren. Sie wird beispielsweise vom Apache ActiveMQ JMS Message Broker für den Java-Objektaustausch mit anderen Plattformen/Sprachen unterstützt.

3
mjn

Wenn Sie Spring verwenden, können Sie auch Spring Integration Object-to-Map-Transformer verwenden. Es ist wahrscheinlich nicht lohnenswert, Spring als eine Abhängigkeit dazu hinzuzufügen.

Suchen Sie zur Dokumentation nach "Object-to-Map Transformer" unter http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html

Im Wesentlichen durchläuft es den gesamten Objektgraph, der von dem als Eingabe angegebenen Objekt erreichbar ist, und erstellt aus allen primitiven Typ/String-Feldern der Objekte eine Karte. Es kann konfiguriert werden, um entweder auszugeben:

  • eine flache Karte: {rootObject.someField = Joe, rootObject.leafObject.someField = Jane} oder
  • eine strukturierte Karte: {someField = Joe, leafObject = {someField = Jane}}.

Hier ist ein Beispiel von ihrer Seite:

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
}

Ausgabe wird sein: 

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

Ein Rücktransformator ist ebenfalls verfügbar.

3
Jan Żankowski

Verwenden Sie juffrou-reflect 's BeanWrapper. Es ist sehr performant. 

So können Sie eine Bean in eine Map umwandeln:

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

Ich habe Juffrou selbst entwickelt. Es ist Open Source, Sie können es also frei verwenden und ändern. Und wenn Sie Fragen dazu haben, stehe ich Ihnen gerne zur Verfügung.

Prost

Carlos

3
Martins

Einfach Reflektion und Groovy verwenden:

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

Die beste Lösung ist Dozer zu verwenden. Sie brauchen nur etwas in der Mapper-Datei:

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

Und das ist es, Dozer kümmert sich um den Rest !!!

Dozer-Dokumentations-URL

3
Guillo

Wenn Sie nicht möchten, dass Aufrufe an jeden Getter und Setter hardcodiert werden, ist Reflection die einzige Möglichkeit, diese Methoden aufzurufen (ist jedoch nicht schwer).

Können Sie die betreffende Klasse umgestalten, um ein Eigenschaftenobjekt zu verwenden, um die tatsächlichen Daten zu speichern, und lassen Sie jeden Getter und Setter einfach get/set aufrufen? Dann haben Sie eine Struktur, die für das, was Sie tun möchten, gut geeignet ist. Es gibt sogar Methoden zum Speichern und Laden im Schlüsselwertformular.

Es gibt natürlich die absolut einfachsten Konvertierungsmöglichkeiten - keine Konvertierung! 

anstelle der Verwendung von in der Klasse definierten privaten Variablen muss die Klasse nur eine HashMap enthalten, in der die Werte für die Instanz gespeichert werden.

Dann kehren Ihre Getter und Setter zurück und setzen Werte in die HashMap und aus ihr heraus, und wenn es Zeit ist, sie in eine Karte umzuwandeln, voila! - Es ist bereits eine Karte.

Mit ein wenig AOP-Zauber können Sie sogar die in einem Bean liegende Inflexibilität beibehalten, indem Sie immer noch für jeden Wertnamen spezifische Getter und Setter verwenden, ohne die einzelnen Getter und Setter tatsächlich schreiben zu müssen.

2

Sie könnten das Joda-Framework verwenden:

http://joda.sourceforge.net/

und nutzen Sie JodaProperties. Dies setzt jedoch voraus, dass Sie Beans auf eine bestimmte Weise erstellen und eine bestimmte Schnittstelle implementieren. Dann können Sie jedoch eine Eigenschaftskarte ohne Reflexion von einer bestimmten Klasse zurückgeben. Beispielcode ist hier:

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

2
Jon

Sie können die Eigenschaften des Java 8-Stream-Filterkollektors verwenden.

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

Mein JavaDude Bean-Anmerkungsprozessor generiert dafür Code. 

http://javadude.googlecode.com

Zum Beispiel:

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

Das Obige generiert Superklasse-PersonGen, die eine createPropertyMap () -Methode enthält, die eine Map für alle mit @Bean definierten Eigenschaften generiert.

(Beachten Sie, dass ich die API leicht für die nächste Version ändere. Das Annotation-Attribut lautet defineCreatePropertyMap = true.)

1

Mit Hilfe der Jackson-Bibliothek konnte ich alle Klasseneigenschaften des Typs String/integer/double und die entsprechenden Werte in einer Map-Klasse finden. ( ohne Reflexionen api! )

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

Sie sollten einen generischen Transformationsservice schreiben! Verwenden Sie Generics, um den Typ frei zu halten (damit Sie jedes Objekt in key => value und zurück konvertieren können).

Welches Feld sollte der Schlüssel sein? Holen Sie sich dieses Feld aus der Bean und hängen Sie einen anderen nicht flüchtigen Wert in einer Wertezuordnung an. 

Der Rückweg ist ziemlich einfach. Schlüssel lesen (x) und zuerst den Schlüssel und dann jeden Listeneintrag in ein neues Objekt schreiben. 

Die Eigenschaftsnamen einer Bohne erhalten Sie mit den Apache commons Beanutils !

1
Martin K.

Ein anderer möglicher Weg ist hier.

Der BeanWrapper bietet Funktionen zum Festlegen und Abrufen von Eigenschaftswerten (einzeln Oder in großen Mengen), Abrufen von Eigenschaftsdeskriptoren und Abfragen von Eigenschaften, um zu ermitteln, ob sie. sind lesbar oder beschreibbar.

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

Wahrscheinlich zu spät zur Party. Sie können Jackson verwenden und in ein Eigenschaftenobjekt konvertieren. Dies ist für verschachtelte Klassen geeignet und wenn Sie den Schlüssel für den Wert für a.b.c = haben möchten.

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

wenn Sie ein Suffix wünschen, dann tun Sie es einfach

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

müssen diese Abhängigkeit hinzufügen

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

Wenn Sie wirklich Leistung wünschen, können Sie die Codegenerierungsroute wählen.

Sie können dies für Sie tun, indem Sie Ihre eigenen Überlegungen ausführen und eine AspectJ-ITD-Mischung erstellen.

Oder Sie können Spring Roo verwenden und ein Spring Roo Addon erstellen. Ihr Roo-Addon wird etwas Ähnliches wie oben beschrieben tun, steht jedoch allen zur Verfügung, die Spring Roo verwenden, und Sie müssen keine Laufzeitanmerkungen verwenden.

Ich habe beides gemacht Menschen sind auf Spring Roo Mist, aber es ist wirklich die umfassendste Code-Generation für Java.

1
Adam Gent

Wenn es sich um eine einfache Zuordnung von Objektbaum zu Schlüsselwertliste handelt, wobei der Schlüssel eine gepunktete Pfadbeschreibung vom Wurzelelement des Objekts bis zum untersuchten Blatt sein kann, ist es ziemlich offensichtlich, dass eine Baumumwandlung in eine Schlüsselwertliste mit einem vergleichbar ist Objekt in XML-Zuordnung. Jedes Element in einem XML-Dokument hat eine definierte Position und kann in einen Pfad konvertiert werden. Deshalb habe ich XStream als grundlegendes und stabiles Konvertierungswerkzeug genommen und die hierarchischen Treiber- und Schreiberteile durch eine eigene Implementierung ersetzt. XStream verfügt außerdem über einen grundlegenden Pfadverfolgungsmechanismus, der in Kombination mit den beiden anderen dazu führt, dass eine Lösung für die Aufgabe geeignet ist.

1
Lars Wunderlich

Durch die Verwendung von Gson

  1. Konvertieren Sie POJO object in Json
  2. Konvertieren Sie Json in Karte

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