草庐IT

Python 基于xml.etree.ElementTree实现XML对比

授客的博客 2023-03-28 原文

测试环境

Python 3.6

Win10

代码实现

#!/usr/bin/env python 3.4.0
#-*- encoding:utf-8 -*-

__author__ = 'shouke'

import xml.etree.ElementTree as ET

def compare_xml_node_attributes(xml_node1, xml_node2):
    result = []
    node1_attributes_dict = xml_node1.attrib
    node2_attributes_dict = xml_node2.attrib
    for attrib1, value in node1_attributes_dict.items():
        value2 =  node2_attributes_dict.get(attrib1)
        if value == value2:
            node2_attributes_dict.pop(attrib1)
        else:
            if value2:
                attrib2 = attrib1
                node2_attributes_dict.pop(attrib2)
            else:
                attrib2 = '不存在'
            result.append('结点1属性:{attrib1} 值:{value1},结点2属性:{attrib1} 值:{value2}'.format(attrib1=attrib1 or '不存在',
                                                                                         value1=value or '不存在',
                                                                                         attrib2=attrib2,
                                                                                         value2=value2 or '不存在'))

    for attrib2, value2 in node2_attributes_dict.items():
        result.append('结点1属性:{attrib1} 值:{value1},结点2属性:{attrib1} 值:{value2}'.format(attrib1='不存在',
                                                                                         value1='不存在',
                                                                                         attrib2=attrib2,
                                                                                         value2=value2))
    return result


def compare_xml_node_children(xml_node1, xml_node2, node1_xpath, node2_xpath):
    def get_node_children(xml_node, node_xpath):
        result = {}
        for child in list(xml_node):
            if child.tag not in result:
                result[child.tag] = [{'node':child, 'xpath': '%s/%s[%s]' % (node_xpath, child.tag, 1)}]
            else:
                result[child.tag].append({'node':child, 'xpath': '%s/%s[%s]' % (node_xpath, child.tag, len(result[child.tag])+1)})
        return result

    result = []
    children_of_node1_dict = get_node_children(xml_node1, node1_xpath)
    children_of_node2_dict = get_node_children(xml_node2, node2_xpath)

    temp_list1 = []
    temp_list2 = []
    for child_tag, child_node_list in children_of_node1_dict.items():
        second_child_node_list = children_of_node2_dict.get(child_tag, [])
        if not second_child_node_list:
            # 获取xml1中比xml2中多出的子结点
            for i in range(0, len(child_node_list)):
                temp_list1.append('%s/%s[%s]' % (node1_xpath, child_node_list[i]['node'].tag, i+1))
            continue

        for first_child, second_child in zip(child_node_list, second_child_node_list):
            result.extend(compare_xml_nodes(first_child['node'], second_child['node'], first_child['xpath'], second_child['xpath']))

        # 获取xml2中对应结点比xml1中对应结点多出的同名子结点
        for i in range(len(child_node_list), len(second_child_node_list)):
            temp_list2.append('%s/%s[%s]' % (node2_xpath, second_child_node_list[i]['node'].tag, i+1))
        children_of_node2_dict.pop(child_tag)

    if temp_list1:
        result.append('子结点不一样:xml1结点(xpath:{xpath1})比xml2结点(xpath:{xpath2})多了以下子结点:\n{differences}'.format (xpath1=node1_xpath,
                                                                                                  xpath2=node2_xpath,
                                                                                                  differences='\n'.join(temp_list1)))
    # 获取xml2比xml1中多出的子结点
    for child_tag, child_node_list in children_of_node2_dict.items():
        for i in range(0, len(child_node_list)):
            temp_list2.append('%s/%s[%s]' % (node1_xpath, child_node_list[i]['node'].tag, i+1))

    if temp_list2:
        result.append('子结点不一样:xml1结点(xpath:{xpath1})比xml2结点(xpath:{xpath2})少了以下子结点:\n{differences}'.format (xpath1=node1_xpath,
                                                                                                  xpath2=node2_xpath,
                                                                                                  differences='\n'.join(temp_list2)))
    return result


def compare_xml_nodes(xml_node1, xml_node2, node1_xpath='', node2_xpath=''):
    result = []
    # 比较标签
    if xml_node1.tag !=  xml_node2.tag:
        result.append('标签不一样:xml1结点(xpath:{xpath1}):{tag1},xml2结点(xpath:{xpath2}):{tag2}'.format (xpath1=node1_xpath,
                                                                                                  tag1=xml_node1.tag,
                                                                                                  xpath2=node2_xpath,
                                                                                                  tag2=xml_node2.tag))

    # 比较文本
    if xml_node1.text !=  xml_node2.text:
        result.append('文本不一样:xml1结点(xpath:{xpath1}):{text1},xml2结点(xpath:{xpath2}):{text2}'.format (xpath1=node1_xpath,
                                                                                                  tag1=xml_node1.text or '',
                                                                                                  xpath2=node2_xpath,
                                                                                                  tag2=xml_node2.text or ''))

    # 比较属性
    res = compare_xml_node_attributes(xml_node1, xml_node2)
    if res:
        result.append('属性不一样:xml1结点(xpath:{xpath1}),xml2结点(xpath:{xpath2}):\n{differences}'.format (xpath1=node1_xpath,
                                                                                                  xpath2=node2_xpath,
                                                                                                  differences='\n'.join(res)))
    # 比较子结点
    res = compare_xml_node_children(xml_node1, xml_node2, node1_xpath, node2_xpath)
    if res:
        result.extend(res)

    return result


def compare_xml_strs(xml1_str, xml2_str, mode=3):
    '''
    @param: mode 比较模式,预留,暂时没用。目前默认 xml 子元素如果为列表,则列表有序列表,按序比较
    '''
    root1 = ET.fromstring(xml1_str.strip())
    root2 = ET.fromstring(xml2_str.strip())

    return compare_xml_nodes(root1, root2, '/%s' % root1.tag, '/%s' % root2.tag)

测试运行

xml_str1 = '''
<?xml version = "1.0" encoding="utf-8" ?>
<data>
    <country name="Liechtenstein">
        <rangk>1</rangk>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E" ></neighbor>
        <neighbor name="Switzerland" direction="W" ></neighbor>
    </country>
    <country name="Singpore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N" ></neighbor>
    </country>
    <country name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W" ></neighbor>
        <neighbor name="Colombia" direction="W" ></neighbor>
    </country>
</data>
'''
xml_str2 = '''
<?xml version = "1.0" encoding="utf-8" ?>
<data>
    <country name="Liechtenstein">
        <rangk>1</rangk>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E" ></neighbor>
        <neighbor name="Switzerland" direction="W" ></neighbor>
    </country>
    <country name="Singpore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N" ></neighbor>
    </country>
    <country name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W" ></neighbor>
        <neighbor name="Colombia" direction="W" ></neighbor>
    </country>
</data>
'''

xml_str3 = '''
<?xml version = "1.0" encoding="utf-8" ?>
<data>
    <class name="computer">
        <rangk>1</rangk>
        <year>unknow</year>
        <addr>sz</addr>
        <book name="java programming" price="10" ></book>
        <book name="python programming" price="10" ></book>
    </class>
    <class name="philosophy">
        <rangk>2</rangk>
        <year>unknown</year>
        <book name="A little history of philosophy" price="15" ></book>
        <book name="contemporary introduction" price="15" ></book>
    </class>
    <class name="history">
        <rangk>3</rangk>
        <year>unknown</year>
        <addr>other addr</addr>
        <book name="The South China Sea" price="10" ></book>
        <book name="Chinese Among Others" price="10" ></book>
    </class>
</data>
'''

xml_str4 = '''
<?xml version = "1.0" encoding="utf-8" ?>
<data>
    <class name="computer">
        <year>unknow</year>
        <addr>sz</addr>
        <book name="java programming" price="10" ></book>
        <book name="python programming" price="10" ></book>
    </class>
    <class name="philosophy">
        <year>unknown</year>
        <addr>other addr</addr>
        <book name="A little history of philosophy" price="15" ></book>
        <book name="contemporary introduction" price="16" ></book>
    </class>
</data>
'''


if __name__ == '__main__':
    res_list = compare_xml_strs(xml_str1, xml_str2)
    if res_list:
        print('xml1和xml2不一样:\n%s' % '\n'.join(res_list))
    else:
        print('xml1和xml2一样')

    res_list = compare_xml_strs(xml_str3, xml_str4)
    if res_list:
        print('xml3和xml4不一样:\n%s' % '\n'.join(res_list))
    else:
        print('xml3和xml4一样')

运行结果

xml1和xml2一样
xml3和xml4不一样:
子结点不一样:xml1结点(xpath:/data/class[1])比xml2结点(xpath:/data/class[1])多了以下子结点:
/data/class[1]/rangk[1]
属性不一样:xml1结点(xpath:/data/class[2]/book[2]),xml2结点(xpath:/data/class[2]/book[2]):
结点1属性:price 值:15,结点2属性:price 值:16
子结点不一样:xml1结点(xpath:/data/class[2])比xml2结点(xpath:/data/class[2])多了以下子结点:
/data/class[2]/rangk[1]
子结点不一样:xml1结点(xpath:/data/class[2])比xml2结点(xpath:/data/class[2])少了以下子结点:
/data/class[2]/addr[1]

有关Python 基于xml.etree.ElementTree实现XML对比的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  5. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  6. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

随机推荐