2025, Dec 28 06:02
Как исправить XPath для SOAP Fault в lxml: пространства имен
Разбираем проблему XPath в SOAP Fault: элементы в пространстве имен по умолчанию. Покажем решение на lxml/Python: nsmap и префиксы для WebServiceFault и message.
Когда вы извлекаете данные из SOAP‑ошибок (Fault), все может выглядеть нормально, пока не столкнетесь с элементами, принадлежащими другому пространству имен XML. Типичный симптом — часть XPath‑запросов срабатывает, а другие молча возвращают пустой результат, хотя нужный элемент явно присутствует в ответе. Ниже — минимальный, но реальный пример такой ситуации и способ исправить ее с помощью lxml и XPath.
Воспроизведение проблемы
Ответ содержит SOAP Fault. Чтение soap:Subcode/soap:Value и soap:Reason/soap:Text работает, но попытка получить текст элемента message внутри WebServiceFault заканчивается пустым результатом XPath.
from lxml import etree as ET
payload = '''<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<soap:Fault>
<soap:Code>
<soap:Value>soap:Sender</soap:Value>
<soap:Subcode>
<soap:Value xmlns:ns1="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd">
ns1:unauthorized
</soap:Value>
</soap:Subcode>
</soap:Code>
<soap:Reason>
<soap:Text xml:lang="en">AccessResult: result: Access Denied | AuthenticationAsked: true |
ErrorCode: IDP_ERROR:
137 | ErrorReason: null</soap:Text>
</soap:Reason>
<soap:Detail>
<WebServiceFault xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07">
<code>SystemError</code>
<message>AccessResult: result: Access Denied | AuthenticationAsked: true | ErrorCode:
IDP_ERROR: 137 |
ErrorReason: null</message>
</WebServiceFault>
</soap:Detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>'''
doc = ET.fromstring(payload)
nsmap = {
'soap': 'http://www.w3.org/2003/05/soap-envelope',
'ns1': 'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"'
}
print(doc.xpath('//soap:Subcode/soap:Value', namespaces=nsmap)[0].text)
print(doc.xpath('//soap:Reason/soap:Text', namespaces=nsmap)[0].text)
print(doc.xpath('//soap:Detail/WebServiceFault/message', namespaces=nsmap)[0].text)
В последней строке мы обращаемся к элементу, который визуально находится именно там, где и должен быть, однако запрос не находит ни одного узла, и при обращении по индексу возникает IndexError: list index out of range.
Что на самом деле происходит
Элемент WebServiceFault объявляет пространство имен по умолчанию: xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07". В его области действия элементы WebServiceFault, code и message находятся не в пустом пространстве имен, а принадлежат этому пространству Taleo.
XPath рассматривает имена без префиксов как относящиеся к «пустому» пространству имен. Поэтому //soap:Detail/WebServiceFault/message ни с чем не совпадает. Ранние запросы сработали, потому что обращались к элементам в пространстве SOAP через префикс soap, который мы указали в namespaces=....
Как исправить запрос
Решение — добавить пространство имен Taleo в карту пространств имен и явным образом использовать соответствующий префикс для каждого элемента из этого пространства.
from lxml import etree as ET
xml_text = payload # используем тот же XML из примера выше
root_node = ET.fromstring(xml_text)
ns_alias = {
'soap': 'http://www.w3.org/2003/05/soap-envelope',
'ns1': 'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"',
'ns2': 'http://www.taleo.com/ws/integration/toolkit/2005/07'
}
result_text = root_node.xpath(
'//soap:Detail/ns2:WebServiceFault/ns2:message',
namespaces=ns_alias
)[0].text
print(result_text)
AccessResult: result: Access Denied | AuthenticationAsked: true | ErrorCode:
IDP_ERROR: 137 |
ErrorReason: null
Почему это важно
В SOAP‑сообщениях пространства имен обычно смешаны: конверт и Fault находятся в пространстве SOAP, а предметные детали часто лежат в заданном сервисом пространстве имен по умолчанию. Если эти пространства не учесть в XPath, запросы выглядят правильными, но молча возвращают пустые результаты. Явное использование префиксов делает селекторы точными и надежными.
Выводы
Проверяйте XML на наличие пространств имен по умолчанию и добавляйте каждое из них в контекст выполнения XPath. Последовательно используйте соответствующие префиксы в запросах — даже для элементов, которые в самом сообщении выглядят без префикса. Этого достаточно, чтобы избежать «пустых» наборов узлов и последующих ошибок.