Individuelles Wrapper-Element um Collection mit JAXB

Nexus6

Mitglied
Hallo liebe Java-Gemeinde,

ich war bisher seit einigen Wochen passiver Leser und suche nun Hilfe bei einem Problem, welches ich schon etwas laenger habe.
Bisher im Netz, auch bei den Sun-Blogs, aber nichts brauchbares gefunden habe.


Zu meinem Problem:
Ich habe ein Listenelement welches verschiedene Arten Objekten (über ein gemeinsames Interface Fahrzeug) aufnehmen kann, aber immer nur vom gleichen Typ zu einem Zeitpunkt.
Beispielhaft moechte ich zur Veranschaulichung das Problem mit Fahrzeugen vom Typ Auto und Motorrad erklaeren.
Also etwa so (in der Liste befinden sich zunaechst Fahrzeuge vom Typ Auto):
<Autos>
<Fahrzeug name="BMW"/>​
<Fahrzeug name="AUDI"/>​
...
</Autos>​

Zu einem anderem Zeitpunkt können in der Liste Fahrzeuge vom Typ Motorrad sein:
<Motorraeder>
<Fahrzeug name="KAWASKI"/>​
<Fahrzeug name="HONDA"/>​
...
</Motorraeder>​

Mit JAXB kann man die Liste mit Hilfe von Element Referenzen nach folgender Art mappen:
Java:
@XmlElementRefs(value = {
                    @XmlElementRef(type = Auto.class, name = "Fahrzeug", required = false),
                    @XmlElementRef(type = Motorrad.class, name = "Fahrzeug", required = alse)
List<Fahrzeug>fahrzeuge = new ArrayList<Fahrzeug>();

Es gibt die Annotation @XmlElementWrapper, aber diese kann nur einmal über dem Feld "Fahrzeuge"
angegeben werden.

Mir ist klar, dass das Wrapper-Element auch einfach "Fahrzeug" heissen koennte, aber das XSD - Schema gibt es halt so vor, das wenn sich in der Liste Auto-Typen befinden auch das Elternelement "Autos" heisst und bei Motorraedern eben "Motorraeder".

Wünschenswert waere beim Mapping so etwas zu machen (um es nochmal auf den Punkt zu bringen):

Java:
@XmlElementWrappers(value = { 
                               @XmlElementWrapper(type = Auto.class, name = "Autos"), 
                               @XmlElementWrapper(type = Motorrad.class, name = "Motorraeder") })
@XmlElementRefs(value = {
                    @XmlElementRef(type = Auto.class, name = "Fahrzeug", required = false),
                    @XmlElementRef(type = Motorrad.class, name = "Fahrzeug", required = alse) })
List<Fahrzeug>fahrzeuge = new ArrayList<Fahrzeug>();

So eine Annotation gibt es aber nicht.
Ich habe mir z.Z. so geholfen, dass ich neben der Liste "fahrzeuge" noch 2 weitere 'konkrete' Listen halte, in die ich die jeweiligen Elemente aus der Fahrzeug-Liste umkopiere.
Die beiden extra Listen sind mit dem jeweiligen @XmlElementWrapper Annotation versehen, damit das Gewünschte rauskommt.

Das hin- und herkopieren ist aber unschön, denn gearbeitet wird eigentlich nur mit der Fahrzeug-Liste und nur dafür gibt es Zugriffsmethoden.
Fuer das Marschallen und Unmarschallen muss ich also noch extra Aufwand treiben, damit ich dem Schema entspreche (nach Marshall) bzw. die Fahrzeug-Liste gefuellt bekomme (nach Unmaraschall).

Wie schaffe ich es, dass je nach Fahrzeugtypen in der Liste das Wrapper-Element mal "Autos" und mal "Motorraeder" heisst mit JAXB-Mitteln ?

Ich hoffe das war jetzt nicht zu lang, um das Problem zu erklaeren 😉

Vielen Dank schon mal fuer Tipps und Tricks im Voraus.
 
Wie du schon selbst erkannt hast, gibt es eine derartige Annotation nicht (hab auch nichts in der JAXB API gefunden). Deine Fahrzeugliste kann ja Objekte vom Typ Auto und Motorrad aufnehmen. JAXB kann hier aber nicht unterscheiden, ob es sich da um eine reine Autoliste oder eine Motorradliste handelt. Du müsstest also mit 2 Listen arbeiten (für Autos und Motorräder), die Fahrzeugliste bräuchtest du dann nicht mehr.

Besser wüsste ich es jetzt auch nicht.
 
Wenn es nicht zwangsläufig JAXB sein muss, sondern du die Klassen auch fix selber instanzieren und befüllen kannst könntest du dir Lycia (siehe Link in der Signatur) ansehen.
 
Vielen Dank zunächst mal für die Antworten🙂

@kidsos
Ja genau, z.Z. verwende ich sogar 8 "konkrete" Listen in denen ich die jeweiligen Typen ablege.
Der Nachteil ist aber, dass mit diesen Listen nicht gearbeitet wird. D.h. die API User verwenden ausschliesslich Methoden die auf die allgemeine Liste zugreifen.
Ich muss also die jeweiligen Listen für das Java->XML bzw. XML->Java ständig hin- und herkopieren.

@Noctarius
JAXB ist erstmal gesetzt. Die selben POJOs kommen ausserdem noch für JAX-WS zum Einsatz. Dieses Framework setzt ja auch auf JAXB auf.

Vielleicht ist es ja möglich jeweils einen Interceptor zu schreiben, der dann vor dem Marshallen/Unmarschallen noch das jeweilige Element-Tag einbringt.
Immerhin würde das dann autom. im Zuge des JAXB Prozesses erledigt werden und ich kann mir das manuelle Vorgeplenkel sparen.

Grüße.
 
Es gibt für JAXB die Möglichkeit zusätzliche Steuerinformationen mit in das XML zu schreiben (als Attribute aus dem JAXB-Schema). Wie das aber genau funktioniert weiß ich auch spontan nicht. Einfach mal suchen.
 
Java:
List<Fahrzeug>fahrzeuge = new ArrayList<Fahrzeug>();

Ist eigentlich eine andere Frage, aber zum Thema:
das funktioniert bei dir?
Ich bin in JAXB leider ziemlich DAU und versuche aktuell, eine ArrayList zu marshallen, wobei sich das Problem ergibt, dass JAXB von sich aus keine ArrayList's kennt:
Code:
javax.xml.bind.JAXBException: class java.util.ArrayList nor any of its super class is known to this context.
eine jaxb.index Datei habe ich angelegt und wird auch genommen. Allerdings kann ich da ja nicht ArrayList als Klasse reinschreiben.
Gibt's da keine Möglichkeit, ausser einer Wrapper-Klasse, JAXB die ArrayList schmackhaft zu machen?
Bzw. könnte es klappen, falls ich meine
Java:
ArrayList bla = new ArrayList<x>();
durch
Java:
 List bla = new ArrayList<x>();
ersetze, kennt sich da jemand aus?
Ich frage, da es ein ziemlicher Aufwand wäre, das im gesamten Projekt zu ändern und falls jemand bereits Erfahrung hat, wäre ich sehr dankbar, wenn er sie mir mitteilen könnte 🙂.
 
Egal, hat sich geklärt, habe den Fehler gefunden. 😳
😀
zum einen funktioniert sowas nicht:
Java:
marshaller.marshal(System.out, writer);
(ich weiß nicht, wie es dazu kam...)

aber soweit kam er auch gar nicht, stattdessen kam die Exception, da ich zum die Ausgabe eines Setters verwendet hatte, anstatt dem Modelelement:
Java:
marshaller.marshal(srcControl.getContent(), writer);
So gehts richtig:
Java:
marshaller.marshal(SourceModel.getInstance(), writer);
Jetzt geht's und ich bin glücklich.
 

Neue Themen


Zurück
Oben