2014-11-18 1 views
1

Я не могу найти информацию, как разобрать мой XML с пространством имен:питон etree с XPath и пространства имен с префиксом

У меня есть этот XML:

<par:Request xmlns:par="http://somewhere.net/actual"> 
    <par:actual>blabla</par:actual> 
    <par:documentType>string</par:documentType> 
</par:Request> 

И пытался разобрать его:

dom = ET.parse(u'C:\\filepath\\1.xml') 
rootxml = dom.getroot() 
for subtag in rootxml.xpath(u'//par:actual'): 
    #do something 
    print(subtag) 

И получил исключение, поскольку он не знает о префиксе пространства имен. Есть ли лучший способ решить эту проблему, считая, что этот скрипт не будет знать о файле, который он будет анализировать, и тег будет искать?

Поиск Интернет и StackOverflow я нашел, что если я буду добавлять туда:

namespace = {u'par': u"http://somewhere.net/actual"} 
for subtag in rootxml.xpath(u'//par:actual', namespaces=namespace): 
    #do something 
    print(subtag) 

Это работает. Отлично. Но я не знаю, какой XML я буду разбирать, и поиск тега (например, //par:actual) также неизвестен моему сценарию. Поэтому мне нужно как-то найти способ извлечения пространства имен из XML.

Я нашел много способов, как извлечь URI пространства имен, таких как:

print(rootxml.tag) 
print(rootxml.xpath('namespace-uri(.)')) 
print(rootxml.xpath('namespace-uri(/*)')) 

Но как я должен извлечь префикс, чтобы создать словарь, который ElementTree хочет от меня? Я не хочу использовать регулярное выражение monster over xml body для извлечения префикса, я считаю, что для этого существует поддерживаемый способ, не так ли?

А может быть, существуют какие-то методы для извлечения из пространства имен ETree из XML в качестве словаря (как хочет ETree!) Без манипуляций руками?

ответ

2

Вы не можете полагаться на объявления пространства имен в корневом элементе: нет гарантии, что объявления будут даже там, или что документ будет иметь один и тот же префикс для одного и того же пространства имен. Предполагая, что у вас будет какой-то способ передать тег, который вы хотите найти (потому что вы говорите, что это неизвестно вашим сценарием), вы также должны предоставить способ передать также пространство имен.Или использовать обозначения Джеймса Кларка, как {http://somewhere.net/actual}actual (ETXPath имеет поддержку этого синтаксиса, в то время как «нормальный» не XPath, но вы также можете использовать другие методы, такие как .findall(), если вам не нужен полный XPath)

Если вы не заботятся о префиксе вообще, вы также можете использовать функцию local-name() в xpath, например. //*[local-name()="actual"] (но вы не будете «действительно» уверены, что это правильный «фактический»)

+0

Да, я подумал об этом. Сначала я даже пытался побудить пользователя использовать '{http://somewhere.net/actual} actual', чтобы убедиться, что он понимает, что именно он использует. Но это также не работает, и etree не понимают «//{...}actual», выбрасывая исключение. Но тогда я просто добавил пространство имен ввода от пользователя и сравнил его с существующим пространством имен xml, поэтому эта проблема была решена. – Arkady

+0

Класс ETXPath должен решить проблему непонятия синтаксиса {}, но вы не сможете использовать его с методом '.xpath()', вы должны использовать его, как класс XPath (при использовании скомпилированных выражений xpath). Пример: 'path = etree.ETXPath ('// {http://somewhere.net/actual} actual')', а затем использовать его 'results = path (rootxml)' – Steven

+0

Это правда. Я буду вам советоваться :-) – Arkady

1

О, я нашел.

После того как мы делаем, что:

dom = ET.parse(u'C:\\filepath\\1.xml') 
rootxml = dom.getroot() 

Объект rootxml содержит словарь nsmap, который содержит все пространства имен, которые я хочу.

Таким образом, простое решение я нашел:

dom = ET.parse(u'C:\\filepath\\1.xml') 
rootxml = dom.getroot() 
nss = rootxml.nsmap 
for subtag in rootxml.xpath(u'//par:actual', namespaces=nss): 
    #do something 
    print(subtag) 

Это работает.

UPD:, который работает, если пользователь понимает, что означает «par» в XML, с которым он работает. Например, сравнивая предполагаемое пространство имен с существующим пространством имен перед любыми другими операциями.

Тем не менее, мне очень нравится вариант с XPath, который понимает {...} фактический, этого я и пытался достичь.