# Element manipulieren in einem DOMResult per XPath



## Lowpass (20. Apr 2010)

Hallo zusammen
Als XPath-Anfänger bin ich gerade völlig verwirrt und konfus. Ich komm einfach nicht drauf, wie man das machen kann. Anstatt Code zu posten, beschränke ich mich auf die Problembeschreibung:

Ich erhalte ein Objekt und marshal das zu einem DOMResult.

Ich habe einen XPath-Ausdruck als String, der auf ein Element in diesem DOMResult verweist - und den Inhalt dieses Elements möchte ich ändern.

Wie kann ich das (mit dem javax.xml.xpath-Paket) machen? 
Die Hauptschwierigkeit habe ich momentan damit, dass ich ja den Inhalt des Elements nicht einfach auslesen will, sondern ihn _ändern_ möchte.

Freue mich sehr über Hilfe.


----------



## Noctarius (20. Apr 2010)

Erstmal brauchst du ein DOMSource, dann kannst du daraus ein Document machen und dieses per XPath durchparsen lassen.


----------



## Lowpass (20. Apr 2010)

Danke für die Antwort.
Soweit ich gesehen habe, kann ich ganz einfach ein Document erzeugen per Casting aus dem DOMResult-Objekt (result):


```
Document doc = (Document)result.getNode()
```

Was ich dann aber machen will, ist ja nicht einfach ein parsen, sondern die gezielte Veränderung eines Attributs (nicht Element - ich war im Eingangspost zu undeutlich) per XPath und ich möchte das wenn möglich mit den Werkzeugen machen, die mir javax.xml + Subpackages zur Verfügung stellt.


----------



## Noctarius (20. Apr 2010)

Ja per XPath Ausdruck die NodeList ausgeben lassen, alle Children darin durchlaufen, Werte ändern, fertig  Mit XPath Expressions kannst du keine direkte Manipulation durchführen, weil XPath eine Query-Sprache ist.

edit:
Ab Zeile 62 siehst du, wie du mit XPath arbeiten kannst:
https://code.google.com/p/javaxmlpa...al/configuration/XPathConfigutationUtils.java


----------



## Niki (20. Apr 2010)

Vielleicht hilft dir das ja weiter:


```
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XPathParser {

	private Document doc = null;

	public XPathParser(InputStream is) throws SAXException, IOException,
			ParserConfigurationException {
		DocumentBuilderFactory domFactory = DocumentBuilderFactory
				.newInstance();
		domFactory.setNamespaceAware(true);
		DocumentBuilder builder = domFactory.newDocumentBuilder();
		doc = builder.parse(is);
	}

	public void save(OutputStream os) throws TransformerException {
		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(os);
		transformer.transform(source, result);
	}

	public void setName(String s) throws ParserConfigurationException,
			SAXException, IOException, XPathExpressionException {
		XPath xpath = XPathFactory.newInstance().newXPath();

		XPathExpression expr = xpath.compile("/response/result");

		NodeList nl = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

		Node node = nl.item(0);
		NamedNodeMap nnm = node.getAttributes();
		nnm.getNamedItem("name").setNodeValue(s);

	}

	/**
	 * @param args
	 * @throws ParserConfigurationException
	 * @throws IOException
	 * @throws SAXException
	 * @throws XPathExpressionException
	 * @throws TransformerException 
	 */
	public static void main(String[] args) throws SAXException, IOException,
			ParserConfigurationException, XPathExpressionException, TransformerException {
		File f = new File("xpath.xml");
		XPathParser xpp = new XPathParser(new FileInputStream(f));

		xpp.setName("new name");

		xpp.save(new FileOutputStream(f));

	}
}
```

und das XML:


```
<?xml version="1.0" encoding="UTF-8"?>
<response>
	<result name="foobar">

	</result>
</response>
```


----------



## Lowpass (20. Apr 2010)

@Noctarius: Da habe ich mich undeutlich ausgedrückt. Natürlich ist mir das klar, dass ich mit einem XPath-Ausdruck keine direkte Manipulation vornehmen kann. Wenn ich die Nodelist kriege, ist das für mich wunderbar... die Frage war eben: wie komme ich dahin. Ich bin irgendwie durcheinander gekommen mit verschiedenen Packages, verschiedenen Klassen und Methoden.
Danke für den Link. Das entspricht auf den ersten Blick genau dem was ich suche.

@Niki: Das sieht sehr gut aus. Danke. Versuche das grad umzusetzen.


----------



## Lowpass (20. Apr 2010)

Es will einfach nicht funktionieren. 
Ich muss den Code vor dem Posten etwas verfremden. Ich hoffe, dadurch entstehen keine zusätzlichen Fehler.


```
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ns3:Root xmlns:ns3="http://www.url.com/dto">
<control route="0"/>
</ns3:Root>
```

Und hier der dazugehörige Code in 2 verschiedenen Varianten:

```
org.w3c.dom.Document doc = (Document)result.getNode();
javax.xml.xpath.XPath xpath = XPathFactory.newInstance.newXPath();
org.w3c.dom.Node route = (Node)xpath.evaluate("Root/control/@route", doc, XPathConstants.NODE)
```
route ist immer null, auch wenn ich z.B. als XPath "Root/control" angebe, oder "ns3:Root/control".

Die zweite Variante:

```
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("Root/control/@route");
NodeList nl = (NodeList)expr.evaluate(doc, XPathConstants.NODESET);
Node node = nl.item(0);
```
Hier ist node jeweils null.

Ich habe versucht, einen NamespaceContext zu setzen, indem ich ein anonymes Objekt vom Typ NamespaceContext erzeugt habe, das mit getNamespaceUri("ns3") den String "http://www.url.com/dto" zurückliefert und bei getPrefix(...) das umgekehrte. Diesen habe ich dann per xPath.setNamespaceContext gesetzt. Das hat nichts gebracht.


----------



## Niki (20. Apr 2010)

probiers mal ohne dem Attribut aus, also:


```
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("/Root/control");
NodeList nl = (NodeList)expr.evaluate(doc, XPathConstants.NODESET);
Node node = nl.item(0);
```

dann müsstest du das control Element bekommen. Und dann:


```
NamedNodeMap nnm = node.getAttributes();
nnm.getNamedItem("route").setNodeValue("was auch immer");
```

//EDIT
mach mal einen Slash vor dem Root


----------



## Lowpass (20. Apr 2010)

Habe inzwischen in beiden Varianten schon viel Erdenkliches ausprobiert... mit Slash vorne dran, ohne Attributname... versucht, erstmal auf den Root-Node zuzugreifen etc.
Das Resultat ist immer das gleiche: null.

Ich habe extra im Code noch den Inhalt des Documents ausgegeben um zu sehen, ob überhaupt was drin ist oder da der Hund begraben liegt - aber das XML hat genau die erwartete Struktur...

bin echt ratlos... ???:L

EDIT: Ah ja - und den XPath habe ich im XMLSpy geprüft. Müsste stimmen...

EDIT EDIT: Liegt es am Namespace ns3, der mir in die Quere kommt? Muss ich da noch irgend etwas konfigurieren, damit der richtig gelesen wird? Habe grad gesehen, dass XMLSpy vor meinen XPath noch ein "ns3" hängt... im JavaCode bringt ns3:Root allerdings nicht viel...


----------



## Niki (20. Apr 2010)

So sollte es klappen:


```
XPath xpath = XPathFactory.newInstance().newXPath();	  
		
		 xpath.setNamespaceContext( new NamespaceContext( ){
			 public String getNamespaceURI(String prefix) {
				return "http://www.url.com/dto";
			}
			 public String getPrefix(String namespaceURI) {
				return "ns3";
			}
			 public Iterator getPrefixes(String namespaceURI) {
				List l = new ArrayList();
				l.add("ns3");
				return l.iterator();
			}
		 });


		XPathExpression expr = xpath.compile("/ns3:Root/control");
```


----------



## Lowpass (20. Apr 2010)

Oh mann... *anDenKopfFass*
Da kann ich natürlich noch lange ein anonymes NamespaceContext-Objekt erstellen, wenn ich nachher im XPath-String das Namespace-Prefix nicht angebe.

Ja. Jetzt funktionierts.... *uff*... herzlichen Dank an alle, die mitgeholfen haben. 

PS: Der Slash am Anfang des XPath-Ausdrucks ist nicht nötig.


----------

