2016-05-23 3 views
1

Я разбор файла XML в Python3 с помощью lxml.objectify:Элегантный контур Python для разбора XML плоского

<root> 
    <object_header></object_header> 
    <object_details></object_details> 
    <object_details></object_details> 
    <object_header></object_header> 
    <object_details></object_details> 
    <object_header></object_header> 
</root> 

Обратите внимание, что иногда объект не имеет атрибутов.

Путь, я в настоящее время разбор этого (который работает, но некрасиво) является следующим:

from lxml import objectify, etree 
root = objectify.parse(xmlFile).getroot() 
elems = [el for el in root.iterchildren()] 
# data is list of objects 
data = [] 
# Have to instantiate outside of for loop in case last object has not details. 
objectDetails = '' 
# Don't store first object right away. 
firstObject = True 
# Iterate through each XML element. 
for elem in elems: 
    if elem.tag == 'object_header': 
     # Remember object header info. 
     object = storeHeaderInfo(objectDetails) 
     # Skip saving if first object, need to grab object details. 
     if firstObject == True: 
      # Don't skip again, in case object has no details. 
      firstObject = False 
      continue 
     # Save object, already grabbed object details. 
     data.append(object) 
    else: 
     # Process object details in <object_details> tag. 
     objectDetails += etree.tostring(elem) 
# Save last object. 
object = storeHeaderInfo(objectDetails) 
data.append(object) 

Что мне не нравится то, как я должен закодировать хранить объект дважды. Один раз для каждого объекта в цикле for, а затем снова для последнего объекта.

Есть ли более пифонический или элегантный способ сделать это?

ответ

2

Вы можете сделать вещи проще, если вы будете использовать following-sibling::* выражение:

from lxml import objectify, etree  

root = objectify.parse("input.xml").getroot() 
elems = root.xpath("//object_header") 

for elem in elems: 
    header = elem.text 
    objectDetails = '' 
    for sibling in elem.xpath("following-sibling::*"): 
     if sibling.tag == 'object_header': 
      break 

     objectDetails += str(etree.tostring(sibling)) 

    print(header, objectDetails) 

Учитывая следующие входные:

<root> 
    <object_header>object1</object_header> 
    <object_details>detail1</object_details> 
    <object_details>detail2</object_details> 
    <object_header>object2</object_header> 
    <object_details>detail1</object_details> 
    <object_header>object3</object_header> 
</root> 

Код будет печатать:

object1 b'<object_details>detail1</object_details>'b'<object_details>detail2</object_details>' 
object2 b'<object_details>detail1</object_details>' 
object3 
Смежные вопросы