2025, Nov 21 07:00

How to Read Namespaced Attributes in SOAP with lxml: Fetch Status, Error via .get or XPath

Learn how to read namespaced attributes in SOAP using lxml. Map XML namespaces, select api:PlexViewResponse, and get Status via .get or XPath. Avoid bugs.

Extracting attributes from SOAP responses with XML namespaces trips up even experienced developers. Text nodes like SessionId are easy to reach, but attributes on namespaced elements, such as Status on api:PlexViewResponse, require the right combination of namespace mapping and attribute access. Here is how to do it consistently with lxml.

Sample SOAP payload

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
  <soap-env:Header />
  <soap-env:Body>
    <api:PlexViewResponse xmlns:api="http://alu.com/plexwebapi/api" Command="ACT-USER" SwitchName="" RequestId="ACTUSER1748031686781471283" SessionId="" CongestionIndicator="false" Status="SUCCESS">
      <SessionId>session_123456</SessionId>
    </api:PlexViewResponse>
  </soap-env:Body>
</soap-env:Envelope>

The initial approach that works for text, not for attributes

It’s common to define a namespace map and read the SessionId text node successfully. The confusion appears when trying to get an attribute like Status in the same way.

# Set namespace bindings
ns_aliases = { 'api': 'http://alu.com/plexwebapi/api' }

# Read SessionId text
# Assume soap_doc is the parsed XML tree
sess_val = soap_doc.findall('.//SessionId', ns_aliases)[0].text

SessionId is an element with text content, so .text works. However, the Status value is an attribute on api:PlexViewResponse, and attributes are not accessed via .text or by searching for a node name only. They must be read from the element object via .get("AttributeName").

Why it happens

In XML, elements and attributes are different node types. When you locate an element, you can read its text content using .text. But when a value is stored as an attribute, you need to fetch the element itself and then access the attribute with .get. Additionally, because the element is namespaced (api:PlexViewResponse), the search must supply the correct namespace bindings.

A robust way to fetch namespaced attributes

The safest pattern is to collect all namespace bindings present in the document and then use them in your search. After that, use .get to read the attribute value.

from lxml import etree as lx

# Parse the SOAP document from file
xml_root = lx.parse("soap.xml").getroot()

def gather_prefixes(root_node):
    ns_map = {}
    for n in root_node.iter():
        ns_map.update(n.nsmap)
    return ns_map

ns_bindings = gather_prefixes(xml_root)
# Optionally inspect what was collected
# print(ns_bindings)

resp_el = xml_root.find(".//api:PlexViewResponse", namespaces=ns_bindings)
status_value = resp_el.get("Status")
print("PlexViewResponse Status:", status_value)

# If the payload may include an Error attribute too
error_value = resp_el.get("Error")
print("PlexViewResponse Error:", error_value)

This prints the expected Status value (for the sample above it is SUCCESS). If Error is present, you will get its value as well; otherwise .get returns None.

Alternative: XPath attribute selection

If you prefer XPath, target the attribute axis directly. This is concise and explicit about reading an attribute rather than element text.

# Assuming xml_tree is the parsed lxml tree and ns_bindings contains the namespace map
attr_vals = xml_root.xpath("//api:PlexViewResponse/@Status", namespaces=ns_bindings)
status_value = attr_vals[0] if attr_vals else None
print("PlexViewResponse Status via XPath:", status_value)

Why this detail matters

SOAP integrations often rely on attributes to signal outcome and metadata. Misreading them as elements leads to brittle code and silent failures. Correctly handling namespaces and attribute access ensures your integration is deterministic and easier to debug, especially when multiple prefixes or redeclarations exist across the document.

Takeaways

When you need values like Status from api:PlexViewResponse, first resolve the namespace prefixes that appear in the XML, then locate the element using those bindings, and finally access the attribute with .get. For concise one-liners, use an XPath that selects the attribute. Keeping these patterns straight saves time and prevents subtle parsing bugs in production SOAP workflows.