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

测试环境

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对比 - Python技术站

(0)
上一篇 2023年4月2日
下一篇 2023年4月2日

相关文章

  • odoo 给列表视图添加按钮实现数据文件导入

    实践环境 Odoo 14.0-20221212 (Community Edition) 代码实现 模块文件组织结构 说明:为了更好的表达本文主题,一些和主题无关的文件、代码已略去 odoo14customestate │ __init__.py │ __manifest__.py │ ├─models │ estate_customer.py │ __ini…

    2023年3月31日
    00
  • odoo 通过Javascript显示或隐藏form自带按钮

    实践环境 Odoo 14.0-20221212 (Community Edition) 需求描述 如下,根据条件对form视图自带按钮的显示、隐藏进行控制 代码实现 隐藏、显示编辑和创建按钮为例 odoo14/custom/estate/static/src/js/hide_or_show_form_button.js function isHideEdit…

    2023年3月31日
    00
  • Odoo 基于Win10搭建基于Win10搭建odoo14开发环境搭建

    实践环境 win10 Python 3.6.2 odoo_14.0.latest.tar.gz下载地址: https://download.odoocdn.com/download/14/src?payload=MTY3MDg1MTM3Ni4xNC5zcmMud0tZRWZLX2I5UVF0Tm51UUVqT1lQVE5PbGRyYW5zWTc4dHhuOW…

    2023年4月2日
    00
  • Python 基于win32com客户端实现Excel操作

    测试环境 Python 3.6.2 代码实现 非多线程场景下使用 新建并保存EXCEL import win32com.client from win32api import RGB def save_something_to_excel(result_file_path): excel_app = win32com.client.Dispatch(‘Exc…

    python 2023年4月30日
    00
  • odoo 为可编辑列表视图字段搜索添加查询过滤条件

    实践环境 Odoo 14.0-20221212 (Community Edition) 需求描述 如下图,列表网仓记录详情页面(form视图),编辑内联视图中的货主记录,为货主和仓库字段搜索,添加过滤条件,具体如下: 添加、编辑货主时,下拉列表中只展示选取和当网仓记录所属公司关联的货主,点击搜索更多,仅展示和当前网仓记录所属公司关联的货主 添加、编辑货主时,…

    2023年3月31日
    00
  • odoo 开发入门教程系列-QWeb简史

    QWeb简史 到目前为止,我们的房地产模块的界面设计相当有限。构建列表视图很简单,因为只需要字段列表。表单视图也是如此:尽管使用了一些标记,如<group>或<page>,但在设计方面几乎没有什么可做的。 然而,如果我们想给我们的应用程序一个独特的外观,就必须更进一步,能够设计新的视图。此外,PDF报告或网站页面等其他功能需要另一个更…

    python 2023年4月22日
    00
  • odoo 开发入门教程系列-约束(Constraints)

    约束(Constraints) 上一章介绍了向模型中添加一些业务逻辑的能力。我们现在可以将按钮链接到业务代码,但如何防止用户输入错误的数据?例如,在我们的房地产模块中,没有什么可以阻止用户设置负预期价格。 odoo提供了两种设置自动验证恒定式的方法:Python约束 and SQL约束。 SQL 参考:与此主题相关的文档可以查看 Models 和Postgr…

    python 2023年4月18日
    00
  • Odoo 菜单定义和修改学习总结

    odoo菜单定义和修改学习总结 环境 odoo-14.0.post20221212.tar 定义菜单 方式1: <?xml version=”1.0″?> <odoo> <menuitem id=”root_menu_id” name=”TopMenu” web_icon=”estate,static/img/icon.png”…

    Python开发 2023年3月31日
    00
合作推广
合作推广
分享本页
返回顶部