如果所有元素具有相同的父元素,则合并具有相同标签的所有元素

时间:2018-08-30 09:21:57

标签: python xml python-3.x elementtree

我正在使用以下示例XML树:

<group>
   <group_info>
      <Text>
         Text_1
      </Text>
   </group_info>
   <group_info>
      <Text>
         Text_2
      </Text>
   </group_info>
   <group_info>
      <Text>
         Text_3
      </Text>
   </group_info>
</group>

我想合并<group>中所有重复的子元素并将它们分组为一个子元素。我想要的输出:

<group>
   <group_info>
      <Text>
         Text_1 Text_2 Text_3
      </Text>
   </group_info>
</group>

我不导入任何正在使用的新模块:

import xml.etree.ElementTree
group_list = MY_XML.findall(".//group") # I do this because the actual xml is bigger with several groups 
for elem in group_list:
    string_text = ""
    for child in elem :
        for super_child in child:
            if(super_child.text is not None): #Just in case None value because I cannot use string addition
                string_text = string_text + super_child.text + " "
        elem.remove(child)
    new_child = xml.etree.ElementTree.Element("group_info")
    text_elem = xml.etree.ElementTree.Element("Text")
    text_elem.text = string_text
    new_child.append(text_elem)
    elem.append(new_child)

这个想法是我遍历所有组,将<group_info>中的所有文本信息收集到一个字符串中,然后删除树中的所有这些元素,并在信息中附加一个新元素。这可能不是最好的方法,但是我是一个相对较新的人。但是我的输出看起来像:

<group>
   <group_info>
      <Text>
         Text_1
      </Text>
   </group_info>
   <group_info>
      <Text>
         Text_2
      </Text>
   </group_info>
   <group_info>
      <Text>
         Text_3
      </Text>
   </group_info>
<group_info><Text>Text1 Text2 Text3</Text></group_info></group>

从技术上讲,最后一行是我需要的(尽管看起来并不漂亮),但是我不知道为什么即使我打<group_info> elem.remove(child)

2 个答案:

答案 0 :(得分:1)

由于没有人回答,所以花了我一些时间,但是如果其他人遇到相同的问题,我就会得到答案和指示。

我从您在问题中看到的初始代码开始,该代码不会删除旧元素,并且在最后一行留下了非常丑陋的内容。

请注意:MY_XML = xml.etree.ElementTree.parse({PATH_OF_XML})

  1. 如果使用的是xml.etree.ElementTree,则应使用remove() 删除节点的方法,但这需要您拥有父节点 节点参考。我称之为elem.remove(child) [第9行]

  2. 那么,为什么不删除它们?我发现修改     您要迭代的对象会影响迭代。这不是     完全出乎意料,如果您在更改列表的同时     遍历它。我无法存储信息的方式     一次删除元素。

我必须拆分任务:

group_list = MY_XML.findall(".//group") # I do this because the actual xml is bigger with several groups
text_list = [] 
for group in group_list:
    string_text = ""
    for child in group :
        for super_child in child:
            if(super_child.text is not None): #Just in case None value because I cannot use string addition
                string_text = string_text + super_child.text + " "
    text_list.append(string_text) #I stored all the info in 1 group as a value in this list because like I stated my overall xml might be bigger with more than 1 group

for group in group_list:
    for elem in group.findall(".//group_info"):
        #loop over all possible <group> and removes all <group_info> inside
        group.remove(elem) 

#And finally to append the information gathered:
for group in group_list:
    Text_elem = ET.Element("Text")
    Text_elem.text = text_list[group_list.index(group)]
    group_info_elem = ET.Element("Kundenhinweis_redigiert")
    group_info_elem.append(Text_elem)
    group.append(Kund_elem)

这给我留下了非常丑陋的输出:

<group>
<group_info><Text>Text1 Text2 Text3</Text></group_info></group>

,可使用xml.dom.minidom模块轻松解决​​。我首先定义:

def prettify(elem):
    rough_string = xml.etree.ElementTree.tostring(elem, 'utf-8')
    reparsed = xml.dom.minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="  ")

调用该函数:

root = MY_XML.getroot()
pretty_xml = prettify(root)
#Next line is optional, but sometimes your string contains empty lines or lines with white spaces and/or breaklines
pretty_xml = "\n".join([s for s in pretty_xml.split("\n") if not s.isspace()])
print(pretty_xml)

输出将是:

<group>
    <group_info>
        <Text>Text1 Text2 Text3</Text>
    </group_info>
</group>

希望这对其他新手有帮助。

答案 1 :(得分:1)

考虑XSLT,这是一种专用于转换XML文件的语言,您可以在其中通过索引相同的节点名称并将其文本值分组来运行Muenchian Method。尽管Python的内置xml.ElementTree不支持XSLT,但其第三方模块lxml却支持XSLT 1.0脚本。而且您可以这样做,而无需单个for循环,也无需手动修饰输出。

XSLT (另存为.xsl文件,一个特殊的.xml文件)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" method="xml"/>
    <xsl:strip-space elements="*"/>

    <xsl:key name="group_key" match="group/*" use="name()" />

    <xsl:template match="/group">            
        <xsl:copy>
            <xsl:apply-templates select="*[generate-id() =
                                   generate-id(key('group_key', name())[1])]"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="group/*">
        <xsl:copy>
            <xsl:element name="{name(*)}">
            <xsl:for-each select="key('group_key', name())">
                <xsl:value-of select="normalize-space(*)"/>
                <xsl:if test="position() != last()">
                    <xsl:text> </xsl:text>
                </xsl:if>
            </xsl:for-each>
            </xsl:element>
        </xsl:copy>
    </xsl:template>            
</xsl:stylesheet>

XSLT Demo (分为两组)

Python

import lxml.etree as et

# LOAD XML AND XSL
doc = et.parse('/path/to/Input.xml')
xsl = et.parse('/path/to/XSLT_Script.xsl')

# CONFIGURE TRANSFORMER
transform = et.XSLT(xsl)    

# RUN TRANSFORMATION
result = transform(doc)

# PRINT RESULT
print(result)  

# SAVE TO FILE
with open('/path/to/Output.xml', 'wb') as f:
   f.write(result)