Получить тип xsi от XML - Python - PullRequest
1 голос
/ 02 марта 2020

У меня есть следующий файл "test. xml":

<?xml version="1.0" encoding="UTF-8"?>
<test:myXML xmlns:test="http://com/my/namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Parent>
  <Child1 xsi:type="sample-type">
    <GrandChild1>123</GrandChild1>
    <GrandChild2>BranchName</GrandChild2>
  </Child1>
  <Child2 xsi:type="sample-type2"></Child2>
</Parent>
</test:myXML>

Я хотел бы получить 'xsi: type' для любого узла (где он существует). Например, в приведенном выше xml я хотел бы выполнить итерацию по каждому узлу и вернуть «sample-type» и «sample-type2»

Пока у меня есть следующий код:

from lxml import etree

XMLDoc = etree.parse("test.xml")
rootXMLElement = XMLDoc.getroot()
tree = etree.parse("test.xml")

for Node in XMLDoc.xpath('//*'):
    if "xsi:type" in Node.attrib:
        #Do whatever

Однако это не работает, потому что кажется, что «xsi: type» в результате буквально заменяется на xmlns: xsi в объявлении пространства имен. В качестве иллюстрации, если я распечатаю каждый атрибут узла, используя следующий код:

from lxml import etree

XMLDoc = etree.parse("test.xml")
rootXMLElement = XMLDoc.getroot()
tree = etree.parse("test.xml")

for Node in XMLDoc.xpath('//*'):
    print(Node.attrib)

Результат будет:

{}
{}
{'{http://www.w3.org/2001/XMLSchema-instance}type': 'sample-type'}
{}
{}
{'{http://www.w3.org/2001/XMLSchema-instance}type': 'sample-type2'}

Как видите, где атрибут "xsi-type" существует, он буквально заменяет его на xsi в пространстве имен. Как я могу остановить это? Я хотел бы искать xsi-тип вместо ввода строкового литерала из объявления пространства имен.

1 Ответ

3 голосов
/ 02 марта 2020

xsi - это пространство имен префикс , это не пространство имен. Единственное место, где префикс должен быть согласованным, находится внутри элемента XML, который его объявляет.

Префикс даже не обязательно должен быть согласованным в одном и том же документе XML, вы можете иметь одно и то же пространство имен, на которое ссылается любое количество различных префиксов в одном и том же документе.

It особенно не обязательно должно быть согласованным между документом XML и вашим кодом обработки XML, и вы не должны (читай: должны) писать какой-либо код, который принимает префикс или полагается на префикс.

Вот почему if "xsi:type" in Node.attrib: не имеет смысла - предполагается, что префикс должен быть xsi. xsi может обычно использоваться для пространства имен http://www.w3.org/2001/XMLSchema-instance, но это всего лишь соглашение, а не гарантия.

Документ XML может быть записан как

<test:myXML xmlns:test="http://com/my/namespace" xmlns:blah="http://www.w3.org/2001/XMLSchema-instance">
<Parent>
  <Child1 blah:type="sample-type">
    <GrandChild1>123</GrandChild1>
    <GrandChild2>BranchName</GrandChild2>
  </Child1>
  <Child2 blah:type="sample-type2"></Child2>
</Parent>
</test:myXML>

, и это было бы точно так же .

Вот почему l xml использует URI пространства имен, а не префикс, когда он отображает узлы или в своем диалекте XPath - URI является важным Дело в том, что префикс эфемерный.

Вам необходимо определить карту пространства имен в вашей программе

nsmap = {
  'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
}

и использовать эту карту при выборе узлов в пространстве имен - либо явно:

if f"{{{nsmap['xsi']}}}type" in node.attrib:
    # ...

или через XPath

type = node.xpath('@xsi:type', nsmap)

Это делает вашу программу независимой от префикса - вы можете использовать любой префикс, который вам нравится, документ XML может использовать любой префикс, который ему нравится , и код будет работать в любом случае.


Экстремальный пример, но полезно изложить идею:

<test:myXML xmlns:test="http://com/my/namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Parent xmlns:blah="http://www.w3.org/2001/XMLSchema-instance">
    <Child1 foo:type="sample-type" xmlns:foo="http://www.w3.org/2001/XMLSchema-instance">
      <GrandChild1>123</GrandChild1>
      <GrandChild2>BranchName</GrandChild2>
    </Child1>
    <Child2 blah:type="sample-type2"></Child2>
  </Parent>
</test:myXML>

Здесь http://www.w3.org/2001/XMLSchema-instance получает 3 префикса. xsi, blah, foo, каждый с разной областью действия.

Когда это будет проанализировано, какой из них вы будете использовать для ссылки на xsi? Имеет ли это значение? Должно ли иметь значение? Нет, не должно. Все, что нужно для сопоставления, - это URI пространства имен, нам безразлично, что документ XML делает с префиксами:

nsmap = {
  's': 'http://www.w3.org/2001/XMLSchema-instance'
}

type = node.xpath('@s:type', namespaces=nsmap)
...