1

I am attempting to use Python’s ElementTree to parse and modify an xml file. The confusion comes with the XML Namespace. I can use the findall and finditer to get all of the servers names. However, I can't get the xpath query to work to find a specific server. instead the find just brings back the parent element.

What I need to do is find the correct server by the “name” or “machine” element and modify the “arguments”.

<? xml version=’1.0’ encoding=’UTF-8’?>
<domain xmlns=”http://xmlns.oracle.com.weblogic/domain”>
  <server>
    <name>Server1-rma</name>
    <machine>server1</machine>
    <server-start>
      <arguments> -Xms4g</arguments>
    </server-start>
  </server>
  <server>
    <name>Server2-rma</name>
    <machine>server2</machine>
    <server-start>
      <arguments> -Xms4g</arguments>
    </server-start>
  </server>
  <server>
    <name>Server3-rma</name>
    <machine>server3</machine>
    <server-start>
      <arguments> -Xms4g</arguments>
    </server-start>
  </server>
</domain>

I have attempted various iterations of the query. However, I am new to XPath and must be doing something wrong:

Failed: root + “ns0:server/[ns0:machine=’server2’]

Failed: root + “ns0:server/ns0:[machine=’server2’]

Failed: root + “ns0:server/[ns0:machine=ns0:’server2’]

sample code:

import xml.etree.ElementTree as ET
namespace = {‘ns0’: ‘ http://xmlns.oracle.com.weblogic/domain’}

tree = ET.parse(‘config.xml’)
root = tree.getroot()
for item in root.find((root + “ns0:server/[ns0:machine=’server2’]), namespace)
    print(item.tag)


output:
{http://xmlns.oracle.com.weblogic/domain}server

I was hoping be able to match the "machine" element and pull the parent element in order to access the correct "arguments" element.

I am a beginner at xpath and elementtree so I am positive, that I am just doing something incorrectly. I am just not sure what. Any help would be greatly appreciated.

2
  • 1
    The correct XPath expression is /ns0:domain/ns0:server[ns0:machine='server2']. The Predicate is part of the same Location Step. Also, do note that ElementTree from xml.etree module has limited support for XPath 1.0 Commented Apr 30, 2019 at 14:00
  • 1
    Thank you for the response and information. Commented May 1, 2019 at 9:18

1 Answer 1

4

Like Alejandro mentioned in a comment, ElementTree has limited support for XPath. That shouldn't matter too much for what you're trying to do. If you need full XPath 1.0 support, consider lxml.

However, it also has some other quirks. One of them is that it will add it's own namespace prefix to your default namespace. To keep the default namespace you'll have to register it with register_namespace().

Alejandro is also correct that the correct XPath to select the server would be:

/ns0:domain/ns0:server[ns0:machine='server2']

However, when you build the tree (with ET.parse()) or get the root (with getroot()), the context is already ns0:domain so the XPath in that context would actually be:

./ns0:server[ns0:machine='server2']

Since you're wanting to update the arguments of the server, we can add that to the XPath too:

./ns0:server[ns0:machine='server2']/ns0:server-start/ns0:arguments

See here for more info on XPath location paths.

Here's a full example. (I'm using the prefix wl instead of ns0 just to show that the prefix doesn't really matter as long as it follows the rules for namespace prefixes.)

XML Input (test.xml; fixed quotes and XML declaration so it would be well-formed)

<?xml version='1.0' encoding='UTF-8'?>
<domain xmlns="http://xmlns.oracle.com.weblogic/domain">
  <server>
    <name>Server1-rma</name>
    <machine>server1</machine>
    <server-start>
      <arguments> -Xms4g</arguments>
    </server-start>
  </server>
  <server>
    <name>Server2-rma</name>
    <machine>server2</machine>
    <server-start>
      <arguments> -Xms4g</arguments>
    </server-start>
  </server>
  <server>
    <name>Server3-rma</name>
    <machine>server3</machine>
    <server-start>
      <arguments> -Xms4g</arguments>
    </server-start>
  </server>
</domain>

Python

import xml.etree.ElementTree as ET

tree = ET.parse("test.xml")

ns = {"wl": "http://xmlns.oracle.com.weblogic/domain"}

ET.register_namespace("", ns["wl"])

try:
    tree.find("./wl:server[wl:machine='server2']/wl:server-start/wl:arguments", namespaces=ns).text = "BAM!!!"
except AttributeError:
    print("Unable to find the correct server element.")

tree.write("output.xml", xml_declaration=True, encoding="UTF-8")

XML Output (output.xml)

<?xml version='1.0' encoding='UTF-8'?>
<domain xmlns="http://xmlns.oracle.com.weblogic/domain">
  <server>
    <name>Server1-rma</name>
    <machine>server1</machine>
    <server-start>
      <arguments> -Xms4g</arguments>
    </server-start>
  </server>
  <server>
    <name>Server2-rma</name>
    <machine>server2</machine>
    <server-start>
      <arguments>BAM!!!</arguments>
    </server-start>
  </server>
  <server>
    <name>Server3-rma</name>
    <machine>server3</machine>
    <server-start>
      <arguments> -Xms4g</arguments>
    </server-start>
  </server>
</domain>
Sign up to request clarification or add additional context in comments.

2 Comments

IT WORKS!!! Thank you for the very thorough explanation. This was a in help in moving forward.
Indeed, very detailed answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.