# Persistenz mit XML



## Marco13 (23. Dez 2012)

Sorry, der Titel ist ziemlich allgemein. Aber die Fragen auch 

Ich würde gerne eine Objektstruktur als XML persistieren. Nachdem ein Kollege mich mal sehr entsetzt angesehen hat, als ich erwähnt hatte, das ich XML bisher mit DOM verarbeitet habe, hatte ich in betracht gezogen, es diesmal mit einer richtigen Schemadefinition und JAXB zu lösen. Ein paar Fragen dazu (und darüber hinaus):

1. Wenn die Objekte, die dort persistiert werden sollen, Implementierungen eines Modell-Interfaces sind, muss man die Übersetzung von dem, was mit den per JAXB generierten Klassen aus dem XML gelesen wird doch per Hand machen, ist das richtig? Also als Beispiel

```
// Modell:
interface Person 
{
    String getName(); 
    int getAge();
}

// Factory zum Erstellen von Instanzen
class Persons
{
    public static Person create(String name, int age) { ... }
}


// XML
<person>
    <name>Bla</name>
    <age>42</age>
</person>


// Automatisch per JAXB generierte "Typ-Klasse"
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "PersonType", propOrder = {...})
public class PersonType 
{
    @XmlElement(required = true)
    protected String name;
    @XmlElement(required = true)
    protected int age;
...
}


// Übersetzung nach dem Einlesen:
Person read(InputStream inputStream)
{
    PersonType personType = readWithJaxb(inputStream);
    return Persons.create(personType.getName(), personType.getAge()); 
}
```
Irgendwie erscheint mir das ziemlich "von hinten durch die Brust ins Auge" ???:L Viel einfacher, als die "Person" direkt aus dem DOM-Node zu bauen, ist das auch nicht - im Gegenteil, es entsteht ein gewaltiger Overhead. Wenn die Möglicheit zur Validierung der einzige Vorteil ist, der sich daraus ergibt ... würde ich mir das eher sparen... :noe:



2. Etwas einfacher sollte es ja sein, wenn das, was man persistiert, Beans sind. Bei mir ist es aber so, dass die eigentlichen Modellklassen KEINE Beans sind, aber ggf. Beans enthalten. Grob im Sinne von

```
// Modell:
interface Person 
{
    String getName(); 
    int getAge();

    Address getAddress(); // Das ist eine Bean
}
```
Wie kann man die beiden Konzepte verheiraten? Über Schemadefinition und JAXB sieht's da (soweit ich das bisher überblicke) dünn aus: Das, was in der Bean steht, sollte ja NICHT Teil der Schemadefinition sein. (Irgendeine Möglichkeit, in der XSD zu sagen: "Da ist ein Knoten, den du ignorieren kannst" wird es wohl geben - ich frage mich nur, ob das sinnvoll ist).


3. Unabhängig davon, ob die Bean nun Teil der XSD ist oder nicht: Wie kann man aus einem Bean-DOM-Knoten eine Bean erzeugen? Als ich gesehen habe, dass 'XMLDecoder' zwingend einen InputStream braucht, bin ich beinahe abgebrochen :noe: Es kann doch nicht sein, dass man sein schon zu einem DOM-Knoten geparstes XML erst wieder in einen String rausschreiben muss, damit man ihn als InputStream an den XMLDecoder weiterreichen und dort wieder "neu" einlesen kann?! ???:L

3a. Als Alternative, was vielleicht nicht verkehrt wäre: Sollte man den Bean-Teil vielleicht einfach als externe, eigenständige XML-Datei ablegen? Wenn ja, gibt es dafür einen Standard oder eine best practice? Erstellt in so einem Fall jeder seine eigene, "proprietäre" Verlinkung wie
<externalBeanFile name="AddressBean00001.xml" />
die dann entsprechend (wieder komplett manuell) eingelesen wird? Eine Websuche brachte erstmal nur sowas wie XInclude, aber ... das fällt ja weg, das liefert ja wieder nur einen DOM-Node...

Vielleicht kann ja jemand ein bißchen Licht in die Sache bringen....


----------



## timbeau (27. Dez 2012)

Das schöne bei JAXB ist, dass du validierte XSD-Dateien nehmen kannst und daraus Java-Beans generieren lassen kannst. Weiter erlaubt dir JAXB halt das direkte Bearbeiten von Objekte. 

Am Ende brauchst du ja nur Marshaller und Unmarshaller. Eine korrekte XML-Datei müsste der JAXB-Context direkt lesen können. Eventuell hab ich dich aber auch nicht verstanden oder dneke zu kurz.


----------



## pl4gu33 (27. Dez 2012)

Also zu 1. meinst du damit das Umwandeln von JAXB- Modell zu Interface bzw. andersrum? Das Wandeln hast du ja auch bei deinen normalen Klassen, wenn du 2 verschiedene Modells hast. Außer du packst die Annotations in deine normalen Modells rein. JAXB kann ja nur die Klassen verarbeiten, die es kennt in deinem Fall ja nur den PersonType. Das "Übersetzen" muss man meines Wissens dann per Hand durch einen eigenen Konverter machen.(Zumindest hab ich noch nix anderes gesehen )

Zu 2. Also etwas ignorieren geht mit @XMLTransient... Ich bin mir gerade nicht sicher, was du mit der Adresse meinst, also du musst ja eh Konvertieren in dein XML-Modell und dann musst du ja dort, wenn du die Bean irgendwie mitliefern willst, schaufeln.

ps. xjc vergisst übrigens die @XmlRootElement- Annotation.


----------



## Marco13 (27. Dez 2012)

@timbeau: JAXB generiert Klassen mit XML-Annotationen, die wohl in die Kategorie "Beans" fallen, die mit meinen Modellinterfaces aber nichts zu tun haben und auch nichts zu tun haben sollen. Bei dem Gedanken, dass meine Modellklassen automatisch generiert werden (und damit z.B. auch schon nicht mehr Immutable sind) nur um in irgendein hoch-spezifisches Persistenzschema zu passen, graust es mir...  

@pl4gu33: Ja, gerade dieser manuelle Konvertieraufwand ist das, was mich irritiert (wo man aber vermutlich nicht drumrumkommt, wenn man o.g. Ansprüche zur Unabhängigkeit von Modellklassen und Persistenz hat). Ob man nun schreibt (Pseudocode)

```
Person readPerson(Node node)
{
    String name; 
    int age;
    Address address;
    for (Node child : node.children())
    {
        if (child.getName().equals("name")) name = child.getValue();
        if (child.getName().equals("age")) age = child.getValue();
        if (child.getName().equals("address")) address = readAddress(child);
    }
    return Persons.create(name, age, address);
}
```
oder

```
Person readPerson(InputStream inputStream)
{
    PersonType personType = Jaxb.read(inputStream);
    return Persons.create(personType.getName(), personType.getAge(), 
        convertToAddress(personType.getAddressType());
}
```
ist vom Aufwand her fast das gleiche, nur dass bei der Jaxb-Variante nochmal ein Haufen automatisch generierter Klassen dazukommen (und damit der Aufwand für die Wartung eher noch steigt...)

Ich muss wohl auch noch ausführlicher lesen. Ich vermute ja, dass man das alles durch passend definierte "PersistenceDelegates" usw. erschlagen könnte, aber damit gehen wohl viele der (vermeintlichen?) Vorteile von JAXB wieder verloren...

Ehrlich gesagt weiß ich auch nicht, wie ich mir die Alternative vorstellen sollte. Wenn jemand so schwammigen Anforderungen und Wünsche andeutet, wie ich gerade, ermutige ich immer zum Aufschreiben der gewünschten Anwendungsweise als Pseudocode - DAraus kann man dann i.a. schon viel ablesen. Was viel anderes als sowas wie

```
Person readPerson(InputStream inputStream)
{
    Node node = read(inputStream);
    return Persons.create(
        getStringValue(node, "name"),
        getIntValue(node, "age"),
        getObjectValue(node, "address", Address.class));
}
```
könnte ich da jetzt selbst nicht schreiben - auch wenn sowas vermutlich recht leicht mit DOM machbar und irgendwie schon cool wäre :reflect:

Wie auch immer, ich pendle in bezug auf diesen Punkt noch zwischen DOM und JAXB. Bin ohnehin noch in der Experimentierphase.


In bezug auf den zweiten Punkt (Referenzen zu externen Dateien) bin ich im Moment eher in Richtung des angedeuteten 
<externalBeanFile name="AddressBean00001.xml" />
Tags, aber auch da muss ich mir noch genauer überlegen, die die Verwaltung des ganzen aussehen soll (d.h. welche Datenstruktur geeignet ist, um die Gesamtheit dieser Dateien zu beschreiben)


----------



## timbeau (28. Dez 2012)

Hab mir schon gedacht, dass ich dir halt kaum was Neues erzählen kann 

Ich habe JAXB genutzt um aus meinen fertigen Java-Objekten eine Struktur generieren zu lassen die ich allerdings nur während des Studiums "ausprobiert" habe. Letztendlich hab ich nur Beans gespeichert und diese dann entweder per Browser oder sonstwas anzeigen lassen. Mit immutable etc bin ich da garnicht in Berührung gekommen. Das einlesen war aber dann halt nur noch 3 Zeilen und man hatte seine Objekte. Du hast ja nicht mehr den "Node" sondern holst dir die XML bzw das "root"-Objekt und daraus die Liste mit den fertigen Beans. In deinem Fall nur noch "Personen/Persons" und dann die einzelnen Personen. Da muss nix mehr mit "getName.equals" abgefragt werden. Da wird jede Person fertig instantiiert. Man brauch halt ein Root-Element, welches alle Personen enthält, so hab ich das zumindest gemacht


```
try {
 
		File file = new File("XML.xml");
		JAXBContext jaxbContext = JAXBContext.newInstance(Persons.class);
 
		Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
		Persons persons = (Persons) jaxbUnmarshaller.unmarshal(file);
		List<Person> singlePersons = persons.getPersonList();
 
	  } catch (JAXBException e) {
		e.printStackTrace();
	  }
```

ohne Gewähr


----------



## Marco13 (28. Dez 2012)

Nachdem ich mit JAXB bisher nichts und mit XML an sich nur das besagte DOM-jonglieren gemacht hatte, ist fast alles neu, was ich so lese  

Dass JAXB bei komplexeren Bean-Strukturen viieeel Arbeit sparen kann, leuchtet mir ein. Einmal das Schema definieren und schon ist die Verwendung ein 3zeiler, während man sich bei manuellem DOM-lesen eben ziemlich viele ziemlich Redundante Methoden selbst basteln muss. Aber das alles unter der Voraussetzung, dass man seine Modellklassen als XML-Annotations-"verseuchte" Beans repräsentieren will. Es wundert mich halt, dass das mit bestimmten (IMHO darüber stehenden!) Designprinzipien (Interfaces statt Klassen, Immutability, Factory Methods statt public Constructors...) so wenig in Einklang zu bringen ist... :bahnhof: (Aber nochmal: WIE man es allgemeingültig und programmatisch in Einklang bringen könnte, kann ich mir gerade selbst kaum vorstellen...)


----------



## timbeau (28. Dez 2012)

Ja, in der Materie kann ich dir nicht weiterhelfen, bin noch Studi. Aber ich verstehe deine Kopfschmerzen. Aber kann man nicht z.B. den Unmarshaller ebenfalls als eine Factory sehen? Die Konstruktoren der Beans benutzt du ja eh nicht. Ok, sie müssen vorhanden sein. Mhmm, schreib dann mal wie du es gelöst hast. 

Denke man darf JAXB wirklich nur als Informationsspeicher, eben Java-Beans sehen. Was steht denn in deinen XML-Dokumenten? Kann man nicht deine Objekte mit den Werten aus den Beans füllen?


----------



## Marco13 (28. Dez 2012)

timbeau hat gesagt.:


> Was steht denn in deinen XML-Dokumenten? Kann man nicht deine Objekte mit den Werten aus den Beans füllen?



Falls ich die Frage richtig verstanden habe, ist das genau das, was ich angedeutet hatte: Man liest die Beans ein ("PersonType") und pflückt sich daraus die Informationen zusammen, die man braucht, um mit Persons.create(...) oder z.B. einem PersonBuilder die Person zu bauen...


----------

