# XPath-Problem mit DOM4J



## Frutz (28. Jul 2010)

Grüßt Euch,

in einer XML-Datei möchte ich ein bestimmtes Element an einer ganz bestimmten Stelle innerhalb eines Containers erstellen.
Ich komme mit einer X-Path-Anweisung allerdings nicht an die richtige Stelle. Die Struktur der Elemente ist dabei so, dass mein neues Element zwischen verschiedenen Elementen der gleichen Ebene platziert werden muss. Heißt also, ich kann es nicht ans Ende oder den Anfang stellen.

Zur Übersicht kurz die Struktur der XML:
[XML]
featureMember
  Element Ebene 1/
    Element Ebene 2
      Element Ebene 3/
    /Element Ebene 2
  Element Ebene 1/
  genau an diese Stelle soll das neue Element
  Element Ebene 1/
  Element Ebene 1/
[/XML]

Mein Quellcode sieht dabei folgendermaßen aus:

```
[...]
dat1id=(Attribute)dat1Iter.next();
   if((!dat1id.getText().contains(seq1))){
   dat1id.getParent().selectSingleNode("//NET:link").addElement("TEST");
[...]
```

Heißt also, er soll von einer Ebene zum Elternelement gehen, dort dann den bestimmten Knoten auswählen und auf der gleichen Ebene dieses Knotens direkt danach ein weiteres Element anhängen.

Zusätzlich habe ich noch das Problem, dass ich nach 
	
	
	
	





```
selectSingleNode
```
 kein Element mit 
	
	
	
	





```
addElement
```
 anhängen kann, warum ist mir noch nicht so ganz klar.

Ich hoffe, ich konnte mich verständlich ausdrücken und jemand weiß Rat.

Vielen Dank
Gruß
André


----------



## sylo (28. Jul 2010)

Hi

Wenn ich dich richtig verstanden habe, musst du einfach nur den XPath Ausdruck an die richtige Stelle lenken.

Dein Ausdruck müsste dann in deinem Fall ungefähr so lauten:
/featureMember/Element Ebene 1[1]

Wenn du diese Position hast, solltest du deinen Knoten direkt davor einfügen können.

Grüße
sylo


----------



## Frutz (30. Jul 2010)

Grüßt Euch,

sorry, dass ich jetzt erst antworte, saß noch an einem anderen Problem.

@sylo: Danke erst Mal, das funktioniert natürlich ich hatte einen Denkfehler. 

Mein Problem besteht trotzdem weiterhin, ich kann das Element nicht an der richtigen Stelle einfügen. Vielleicht muss ich noch etwas ausholen.

Ich habe 2 XML(GML)-Dateien, diese haben verschiedene Elemente, darunter ein Element mit einer ID. Hauptziel ist es, beide Dateien am Ende zu einer XML zu vereinen. Das klappt auch so weit.

Das Problem ist nun, dass es in der 2. XML ein Element gibt, welches ich so in die 1. XML an eine ganz bestimmte Stelle übergeben muss (vorgegeben durch das X-Schema). Das ganze möglichst noch vor dem Vereinen.

Ich habe als erstes alle benötigten ID's der Elemente über X-Path angesprochen und in eine Liste gepackt:

```
List<?> TEMP1 = doc.selectNodes("//gml:featureMember//Element Ebene 1/Element Ebene 2/@id" );
		    
Attribute TempAttribut1 = null;				
ArrayList<Object> temp1 = new ArrayList<Object>();
	    	
for (Object object : TEMP1){
   TempAttribut1= (Attribute) object;
   temp1.add(TempAttribut1.getValue());
   System.out.println("TEMP1: "+TempAttribut1.getValue());}
```

Dann spreche ich das ganze über einen Iterator an und möchte unter bestimmten Bedingungen(fehlt hier noch), dass das Element an der entsprechenden Stelle eingefügt werden soll:


```
Iterator<?> temp1Iter=TEMP1.iterator();
Attribute temp1id = null;

while(temp1Iter.hasNext()){
	temp1id=(Attribute)temp1Iter.next();
	System.out.println("TEST: "+temp1id.getParent());
	temp1id.getParent().addElement("TEST");
			}
```

Mit oben dargestelltem Code bin ich so weit, dass ich folgende XML erhalte:
[XML]
feature Member
[...]
Element Ebene 1
  Zielelement ist Kindelement von Element Ebene 1
[...]
[/XML]

rauskommen soll aber:
[XML]
feature Member
[...]
Element Ebene 1
Zielelement wird Geschwisterelement von Element Ebene 1, also ebenfalls Element in Ebene 1
[...]
[/XML]

Ich habe auch versucht, das Element direkt über XPath anzusprechen und dort ein neues Element einzufügen.
Der Code:

```
temp1id.selectNodes("//X-Path zu Element").addElement("TEST");
```
ist aber nicht gültig.

InsertBefore() habe ich auch versucht, aber das klappt auch nicht. (Da ich mit Eclipse arbeite, werden mir die möglichen Methoden angezeigt, InsertBefore() war nicht dabei)

Ich kann mir nicht vorstellen, dass das SO schwer ist, aber ich seh die Lösung einfach nicht. Hat noch jemand eine Idee?

Danke,
Gruß,
André


----------



## sylo (30. Jul 2010)

Hi

hab auch mal gerade ein wenig gegoogelt. Dieses Problem haben wohl andere auch  Bei dom4j gibt es keine vergleichbare Methode wie insertBefore()

Das Einzige was ich auf die schnelle gefunden habe ist diese Möglichkeit:

```
parentElements.add(newPosition, newNode);
```
Vielleicht hilft dir das weiter.

Grüße
sylo


----------



## Frutz (7. Aug 2010)

Grüßt Euch,

@Sylo: Dank Dir - musst ich gar nicht ausprobieren, ich habe einen anderen Hinweis bekommen und dann hat es funktioniert.

Falls es mal jemand braucht:


```
Element featureElement = document.getRootelement();
Element newElement = DocumentHelper.createElement("new-element");
featureElement.content().add(4, newElement);
```

Das angesprochene Element muss dabei nicht zwangsläufig das Kind-Element des Wurzelknotens sein, man kann ein beliebiges Element auch über X-Path ansprechen und sich dann von dort entsprechend durchhangeln. Im Beispiel wird an der 5. Stelle ein Kindelement (new-Element) des Elements "feature-Element" eingefügt. Funktioniert auch wunderbar.

Daran anschließend hat sich aber ein weiteres Problem mit der XSL Transformation ergeben. Möchte nicht extra einen neuen Thread eröffnen und frag gleich hier noch.

Mit nachfolgendem Code-Teil transformiere ich eine XML mit XSLT.

```
javax.xml.transform.TransformerFactory tfactory=javax.xml.transform.TransformerFactory.newInstance();
javax.xml.transform.Transformer transformer=tfactory.newTransformer(
new javax.xml.transform.stream.StreamSource(new java.io.File(xslfinal)));
						
DocumentSource source = new DocumentSource (doc);
DocumentResult result = new DocumentResult();
transformer.transform(source, result);
```

Es geht dabei vor allem darum, zwei Dokumente durch die Transformation zu einem zusammenzufassen. In der Zieldatei erhalte ich dabei folgende Zeile (das ist das Element, welches ich, wie oben beschrieben, an die bestimmte Stelle einfügen musste. Das Problem trat auch dann erst auf, vorher funktionierte das Transformieren):

[XML]<NET:NET:link xmlns:NET="" xmlns:xlink="" xlink:xlink:href="..."/>[/XML]

Rauskommen sollte eigentlich:
[XML]<NET:link xlink:href="..."/>[/XML]

Führe ich die Transformation händisch (mit Altova XML Spy) aus, funktioniert sie reibungslos und ich erhalte den Fehler nicht.

Meine Frage: Kennt jemand dieses Problem und kann mir vielleicht sagen, wie ich das beheben kann?

Vielen Dank und viele Grüße,
André


----------



## Frutz (9. Aug 2010)

Kennt keiner dieses Problem?

Ich vermute mal, es liegt mit den vergebenen Namespaces zusammen. Ich habe schon verschiedene Varianten durchprobiert, von Namespace aus dem XSL Stylesheet herausnehmen, bis Namespace selbst beim Element hinzufügen.

Nützt alles nichts, der Fehler bleibt. Gibt es eine Möglichkeit, die Namespace innerhalb der Transformation zu ignorieren.

Mich wundert es halt, dass es bei einer manuellen Transformation mit Altova XML Spy keine Probleme gibt, während es bei der Transformation mit JAVA zu oben beschriebenem Problem kommt. Ist zwar nicht das einzige, aber in dem Fall sorgt es dafür, dass die XML nicht valid ist.

Über einen Hinweis wär ich dankbar.

Gruß,
André


----------



## sylo (11. Aug 2010)

Hallo Frutz

dem transformer kannst du noch ein paar Einstellungen mitgeben. Z.B. diese: 

```
transformer.setOutputProperty("omit-xml-declaration", "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");

transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
```

Vielleicht liegt es dann an der formatieren deiner XML, dass es als nicht valide anerkannt wird.
Probier mal ein wenig rum. Normalerweise sollten die letzten zwei Zeilen reichen um deine XML-Datei zu formatieren.

Grüße
sylo


----------



## Frutz (11. Aug 2010)

Grüß Dich Sylo,

dank Dir für den Hinweis. Ein bisschen was kommt vielleicht sogar rein. Gerad das Pritty-Print (muss mal sehen, macht die Datei größer)

Aber ich glaube das Problem liegt noch woanders. Die Transformation klappt ja soweit gut - und solange ich die neuen Elemente nicht einfüge (siehe Ausgangspost und folgende), ist die Datei auch valid.

Wenn ich aber die neuen Elemente mittels JAVA einfüge (siehe Post vom 07. 08.), dann wird das Element "falsch" angelegt.

Und wie ich gerade sehe, habe ich das in meinem ersten Post nicht geschrieben.

Schon in der Ausgangsdatei (also noch vor der Transformation) werden die neuen Elemente "falsch" angelegt. Dort sieht das folgendermaßen aus:

[XML]
<NET:link xmlns="" xlink:href=.../>
[/XML]

Der Namespace [XML]xmlns=""[/XML] hat dort eigentlich nichts zu suchen bzw. muss dort nicht unbedingt hin, denn er ist schon im Wurzelelement definiert und das reicht. Es ist allerdings der richtige Namespace zu dem Element, DOM4J scheint ihn also einfach einzufügen. Die Datei ist auch valid, deshalb stört das nicht weiter.

Nach der Transformation, in der beide Dateien zusammengefasst werden, sieht es dann so aus:

[XML]
<NET:NET:link xmlns:NET="" xmlns:xlink="" xlink:xlink:href=.../>
[/XML]

Und das geht dann nicht mehr. Ich habe auch schon in der XSL-Datei versucht Namespace rauszunehmen, bringt aber alles nix. Das Ergebnis verändert sich zwar ein wenig, aber die Datei ist weiterhin nicht valid.

Weiterhin hab ich auch schon den Rat bekommen, die Namespace-Referenzen wegzulassen und habe den Namespace selbst an das Element angehangen. Alles ohne Erfolg.

Weißt Du oder jemand anders vielleicht, wie ich die Namespacebehandlung in DOM4J bzw. JAVAX für die Transformation unterdrücken kann? Vielleicht würde das schon helfen

Danke und Gruß,
André


----------



## Frutz (11. Aug 2010)

Grüßt Euch,

ich habe den entscheidenden Hinweis im DOM4J Forum bekommen.

Falls es mal jemand braucht:


```
Element newElement = DocumentHelper.createElement(QName.get("[Name des Elements]", "[Referenz zum Namensraum]"));
newElement.addAttribute(QName.get("[Name des Attributs]","[Referenz zum Namensraum]"),[Wert des Attributs]);
```

Man muss das Element bzw. das Attribut mit 
	
	
	
	





```
QName
```
 definieren und dabei die Referenz zum Namensraum hinzufügen. Dann funktioniert auch die Transformation.

Besten Dank für die Hilfe an Sylo und Gruß,
André


----------

