# JAXB: XML-Elemente einlesen und als XML-Attribute ausgeben



## chrk (4. Jul 2012)

Hallo zusammen 

Ich komme bei meinem Vorhaben einfach nicht mehr weiter... :noe:

Einlesen will ich ein einfaches XML-Dokument das so aussieht:

[XML]
<root>
	<detailierteInformationen>
		<zahl>12345</zahl>
		<att1>"hier kommt ein Wert"</att1>
		<att2>"noch ein Wert"</att2>
	</detailierteInformationen>
</root>
 [/XML]

JAXB soll beim Marshal folgendes ausgeben:

[XML]
 <de-schema:detailierteInformationen att1="hier kommt ein Wert"" att2="noch ein Wert">12345</de-schema:detailierteInformationen>
 [/XML]


Dafür habe ich zwei Schemata. Die Schemata werden mit JAXB eingelesen und soll dann mit XmlAdapter behandelt werden.

Hauptschema (Namespace: de-schema):

[XML]
<xs:element name="detailierteInformationen"
               id="de-schema-detailierteInformationen"
               substitutionGroup="xi:item"
               type="xi:zahlItemType" />
[/XML]

2. Schema (Namespace: xi):
[XML]
<element name="root">
    <complexType>
      <sequence>
        <element ref="...." minOccurs="1" maxOccurs="unbounded" />
		<element ref="...." minOccurs="1" maxOccurs="unbounded" />
        <choice minOccurs="0" maxOccurs="unbounded">
          <element ref="xi:item"/>
        </choice>
      </sequence>
      <attribute .... />
    </complexType>
</element>

<element name="item" type="anyType" abstract="true">

<complexType name="zahlItemType" final="extension">
    <simpleContent>
      <extension base="xi:zahl">
        <attributeGroup ref="xi:weitereAttribute" />
      </extension>
    </simpleContent>
</complexType>

<simpleType name="zahl">
    <restriction base="decimal" />
</simpleType>

 <attributeGroup name="weitereAttribute">
    <attributeGroup ref="xi:nochmalsWeitereAttribute" />
    <attribute name="att1" type="xi:att1Type" use="optional" />
    <attribute name="att2" type="xi:att2Type" use="optional" />
 </attributeGroup>
 [/XML]



Mit JAXB generierter Java-Code.

Objectfactory vom Hauptschema:

```
@XmlElementDecl(namespace = "de-schema", name = "detailierteInformationen", substitutionHeadNamespace = "xi", substitutionHeadName = "item")
    public JAXBElement<zahlItemType> createDetaillierteInformation(zahlItemType zahl) {
        return new JAXBElement<zahlItemType>(_detailierteInformationen_QNAME, zahlItemType.class, null, zahl);
    }
```
 

XI-Schema hat den Java-Code:

```
Root.class

 @XmlElementRef(name = "item", namespace = "xi", type = JAXBElement.class, required = false)
//...
protected List<Object> itemList;

ZahlItemType.class

@XmlJavaTypeAdapter(ZahlItemTypeAdapter.class)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "zahlItemType", namespace = "xi", propOrder = {
    "zahl"
})

public class ZahlItemType {
	// Hier stehen die Getter und Setter Methoden, sowie Annotationen @XmlValue und @XmlAttribute.
}
```


Beim Einlesen der XML habe ich die Werte als Elemente und sollen dann als Attribute erscheinen.

Dafür hätte ich die Klasse ZahlenItemTypeAdapter.


```
public  class ZahlItemTypeAdapter extends XmlAdapter<AdaptedZahl, ZahlItemType> {
 // Methoden marshal und unmarshal...
}
```


Jedoch funktioniert das nicht so wie ich es will! :noe:  Er erkennt die XmlAdapter Klasse nicht.

Es soll später auch möglich sein, z.B. das Element detailierteInformationen zu erkennen und die Werte von dem auf die Werte eines anderen Elements addieren. Desweiteren muss beim Einlesen der XML kein Namespace vorhanden sein. Beim Ausgeben jedoch schon.


----------



## jstei001 (5. Jul 2012)

Ich nehme an du nutzt Java 1.6? Denn die Klasse XML-Adapter gibts erst seit JAXB 2.0. Bei mir erkennt er die nämlich ohne Probleme


----------



## chrk (5. Jul 2012)

jstei001 hat gesagt.:


> Ich nehme an du nutzt Java 1.6? Denn die Klasse XML-Adapter gibts erst seit JAXB 2.0. Bei mir erkennt er die nämlich ohne Probleme



Danke für die Hilfe. Ich habe JAXB 2.2 und JDK1.7

Das Ding ist, dass die JAXBElemente nur Attribute hat.
Aber ich möchte das jedes JAXBElement Unterelmente beim Einlesen hat.

Ich dachte ich löse das über das hier:
Java XML & JSON Binding: Mapping an Arbitrary List of Objects using JAXB's @XmlAnyElement and XmlAdapter

Jedoch ist es bei mir etwas komplexer, da ich nur eine Liste mit Objects besitze. Dort eben Items, das JAXBElemente.class sind. Jetzt sollen alle JAXBElemente.class unterelemente beim Einlesen habe, die ich dann weiterverarbeiten kann.

<JAXBElement>
element1
element2
etc.
</JAXBElement>

Ich verzweifle so langsam... ;(
Mittlerweile denke ich, dass JAXB nicht das richtige ist und ich es manuell coden muss.


----------



## chrk (6. Jul 2012)

Ich könnte die XML mit DOM einlesen und mit JAXB ausgeben. Aber das wäre ja nicht das Ziel, da es einiges an Handarbeit bedeuten würde.


----------



## jstei001 (6. Jul 2012)

Ich verstehe das jetzt so das dein Wunsch-JAXB so aussehen soll:


```
Class JAXBElement

Object element1;
Object element2;

getElement1(();

setElement1(object value);

.....
```

Dann musst du aber dein XSD ändern damit dir JAXB das so generiert, oder musst du mit diesem XSD arbeiten und kannst daran nichts ändern? Mein Problem ist ich verstehe dein Vorhaben nicht ganz was du eigentlich machen willst, wenn du das genauer erklärst gibts vllt ne einfache Lösung. Mit DOM bist du natürlich flexibler allerdings beinhaltet das auch en menge Arbeit gerade den XML Code zu erzeugen. Da ist ein JAXB Marshaller doch schon sehr angenehm.


----------



## chrk (6. Jul 2012)

Die XSD lässt sich nicht ändern und ist etwas komplexer. Für das Marshalling will ich den Java-Code benutzen, der durch die XSD generiert wurde. Für das Unmarshalling will ich jedoch eine zusätzliche Java-Klasse benutzen, die die ZahlItemType.class ersetzt.


Das JAXBElement bekommt ja die ZahlItemType.class zugewiesen. 

*ZahlItemType.class*


```
@XmlJavaTypeAdapter(ZahlItemTypeAdapter.class)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "zahlItemType", namespace = "xi", propOrder = {
    "zahl"
})
 
public class ZahlItemType {
   @XmlValue 
   String value;
   @XmlAttribute
   String att1;
   @XmlAttribute
   String att2;

   public void setValue(String value) {
        this.value = value;
    }
    public String getValue() {
        return value;
    }

    // weitere Set/Get Methoden
}
```

Das wäre dann die XML:

[XML] 
<root>
<de-schema:detailierteInformationen att1="hier kommt ein Wert"" att2="noch ein Wert">12345</de-schema:detailierteInformationen>
</root>
[/XML]

Nun möchte ich aber diese XML auslesen:

[XML]
<root>
    <detailierteInformationen>
        <zahl>12345</zahl>
        <att1>"hier kommt ein Wert"</att1>
        <att2>"noch ein Wert"</att2>
    </detailierteInformationen>
</root>
[/XML]

Und dann das Element zahl aus der XML dem XmlValue in der Java-Klasse ZahlItemType zuweisen.. Element att1 aus der XML dem XmlAttribute att1 in der Java-Klasse ZahlItemType zuweisen....

Hierfür würde ich gerne ein XmlAdapter auf ZahlItemType setzen. Mit @XmlJavaTypeAdapter(ZahlItemTypeAdapter.class).


```
public  class ZahlItemTypeAdapter extends XmlAdapter<AdaptedZahl, ZahlItemType> {

 	@Override
	public ZahlItemType unmarshal(AdaptedZahl az) throws Exception {
		ZahlItemType mit = new ZahlItemType();
		mit.setValue(az.getZahl);
		return mit;
	}
}
}
```

*AdaptedZahl.class* hat nun die Elemente zahl, att1, att2


```
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "adaptedZahl", namespace = "", propOrder = {
    "zahl",
    "att1",
    "att2"
})

public class AdaptedZahl {
	
	@XmlElement(name ="zahl", nillable=true, required=true)
	protected  String zahl;
       // ...weiter XmlElemente

	public String getZahl() {
	      return zahl;
	}
```

*Demo.class*

```
JAXBContext jc = JAXBContext.newInstance("package.name");
    	ZahlItemTypeAdapter adapter = new ZahlItemTypeAdapter();
        Unmarshaller u = jc.createUnmarshaller();
        u.setAdapter(adapter);
        File xml = new File("D:/", "test.xml");
        Root root = (Root) u.unmarshal(xml);
		JAXBElement o = (JAXBElement) root.getItemList().get(0);
        ZahlItemType mit = (ZahlItemType) o.getValue();
```


----------



## jstei001 (6. Jul 2012)

OK ich verstehe dein Problem, da muss ich aber auch bissl drüber nachdenken, mit JAXB wirds glaub ich nicht direkt gehen, weil der nur XML Unmarshalln kann die auch Valide zu den erzeugten Klassen sind. 

Ich würde da jetzt vllt mal über XPATH oder STAX nachdenken, denn du brauchst ja nur die Element-Werte. Das ist glaub ich vom Aufwand her noch ertragbar.

Dann kannste ja die Setter von der JAXB Klasse nutzen um diese einzupflegen und dann mit marshal eine XML erzeugen. 

Wenn mir was besseres einfällt schreib ich nochmal.


----------



## chrk (6. Jul 2012)

jstei001 hat gesagt.:


> OK ich verstehe dein Problem, da muss ich aber auch bissl drüber nachdenken, mit JAXB wirds glaub ich nicht direkt gehen, weil der nur XML Unmarshalln kann die auch Valide zu den erzeugten Klassen sind.
> 
> Ich würde da jetzt vllt mal über XPATH oder STAX nachdenken, denn du brauchst ja nur die Element-Werte. Das ist glaub ich vom Aufwand her noch ertragbar.
> 
> ...



Genau darüber habe ich auch schon nachgedacht. Das Problem ist das es über 2500 JAXBElemente gibt. Die auzulesene XML hat einen Wurzelelement (Root), dann die 2500 Parentelemente(bei JAXB die JAXBElemente) und diese dann ca. 10 Childelemente.

Manche Parentelemente müssen ohnehin zusammen addiert werden und ergeben ein neues Parentelement.

Deswegen wäre wohl DOM die beste Möglichkeit und dann alle Elemente als XML über die Setter Methoden von JAXB ausgeben


----------

