Как перебрать дочерние элементы в XML Python? - PullRequest
0 голосов
/ 26 апреля 2020

У меня есть XML структурированный как:

<pages>
 <page>
  <textbox>
    <new_line>
     <text>
     </text>
    </new_line>
  </textbox>
 </page>
</pages>

Я перебираю text элементов, которые являются потомками элемента new_line, чтобы объединить теги с тем же атрибутом size. Но я хочу указать, что элемент new_line должен находиться внутри элемента textbox. Я попытался добавить для l oop в моем коде, но это просто не работает. Вот код:

import lxml.etree as etree

parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse('output22.xml', parser)
root = tree.getroot()

# Iterate over //newline block
for new_line_block in tree.xpath('//new_line'):
    # Find all "text" element in the new_line block
    list_text_elts = new_line_block.findall('text')

    # Iterate over all of them with the current and previous ones
    for previous_text, current_text in zip(list_text_elts[:-1], list_text_elts[1:]):
        # Get size elements
        prev_size = previous_text.attrib.get('size')
        curr_size = current_text.attrib.get('size')
        # If they are equals and not both null
        if curr_size == prev_size and curr_size is not None:
            # Get current and previous text
            pt = previous_text.text if previous_text.text is not None else ""
            ct = current_text.text if current_text.text is not None else ""
            # Add them to current element
            current_text.text = pt + ct
            # Remove preivous element
            previous_text.getparent().remove(previous_text)



newtree = etree.tostring(root, encoding='utf-8', pretty_print=True)
#newtree = newtree.decode("utf-8")
print(newtree)
with open("output2.xml", "wb") as f:
    f.write(newtree)

РЕДАКТИРОВАТЬ:

Пример строки:

"""<?xml version="1.0" encoding="utf-8"?>
<pages>
    <page>
        <textbox>
            <new_line>
                <text size="12.482">C</text>
                <text size="12.333">A</text>
                <text size="12.333">P</text>
                <text size="12.333">I</text>
                <text size="12.482">T</text>
                <text size="12.482">O</text>
                <text size="12.482">L</text>
                <text size="12.482">O</text>
                <text></text>
                <text size="12.482">I</text>
                <text size="12.482">I</text>
                <text size="12.482">I</text>
                <text></text>
          </new_line>
        </textbox>
    </page>
</pages>
"""

Ожидаемый результат:

<pages>
    <page>
        <textbox>
            <new_line>
                <text size="12.482">C</text>
                <text size="12.333">API</text>
                <text size="12.482">TOLO</text>
                <text/>
                <text size="12.482">III</text>
                <text/>
            </new_line>
        </textbox>
    </page>
</pages>

1 Ответ

1 голос
/ 28 апреля 2020

Вы можете определить рекурсивную функцию для решения многослойного XML в вашем случае. Я написал шорткод для этой проблемы.

import sys
import xml.etree.ElementTree as etree

def add_sub_element(parent, tag, attrib, text='None'):
    new_feed = etree.SubElement(parent, tag, attrib)

    if(text):
        new_feed.text = text

    return new_feed


def my_tree_mapper(parent_tag, current, element):

    if(current.tag == 'new_line' and parent_tag == 'textbox'):

        current_size = -1
        current_text = ""

        for child in element:
            child_tag = child.tag
            child_attrib = child.attrib
            child_text = child.text

            if(child_tag == 'text' and 'size' in child_attrib):
                if(child_attrib['size'] == current_size):
                    # For 'text' children with the same size
                    # Append text until we got a different size
                    current_text = current_text + child_text
                else:
                    if(current_size != -1):
                        # Add sub element into the tree when we got a different size
                        sub_element = add_sub_element(
                            current, child_tag, {'size': current_size}, current_text)

                    current_size = child_attrib['size']
                    current_text = child_text

            else:
                if(current_size != -1):
                    # Or add sub element into the tree when we got different tag
                    sub_element = add_sub_element(
                        current, child_tag, {'size': current_size}, current_text)

                # No logic for different tag
                sub_element = add_sub_element(
                    current, child_tag, child_attrib, child_text)
                my_tree_mapper(current.tag, sub_element, child)

                current_size = -1
                current_text = ""
    else:
        # No logic if not satisfy the condition
        for child in element:
            child_tag = child.tag
            child_attrib = child.attrib
            child_text = child.text

            sub_element = add_sub_element(
                current, child_tag, child_attrib, child_text)
            my_tree_mapper(current.tag, sub_element, child)


the_input = """<?xml version="1.0" encoding="utf-8"?>
<pages>
    <page>
        <textbox>
            <new_line>
                <text size="12.482">C</text>
                <text size="12.333">A</text>
                <text size="12.333">P</text>
                <text size="12.333">I</text>
                <text size="12.482">T</text>
                <text size="12.482">O</text>
                <text size="12.482">L</text>
                <text size="12.482">O</text>
                <text></text>
                <text size="12.482">I</text>
                <text size="12.482">I</text>
                <text size="12.482">I</text>
                <text></text>
          </new_line>
        </textbox>
    </page>
</pages>
"""

tree = etree.ElementTree(etree.fromstring(the_input))
root = tree.getroot()
new_root = etree.Element(root.tag, root.attrib)

my_tree_mapper('', new_root, root)
print(etree.tostring(new_root))

Надеюсь, что это может помочь вам или, по крайней мере, дать вам некоторое представление.

(Если вы хотите узнать больше о Incursive Functions документ и пример . И еще о XML etree методы здесь )

...