Python 基於xml.etree.ElementTree實現XML對比

来源:https://www.cnblogs.com/shouke/archive/2022/12/12/16975021.html
-Advertisement-
Play Games

測試環境 Python 3.6 Win10 代碼實現 #!/usr/bin/env python 3.4.0 #-*- encoding:utf-8 -*- __author__ = 'shouke' import xml.etree.ElementTree as ET def compare_xm ...


測試環境

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]

作者:授客
微信/QQ:1033553122
全國軟體測試QQ交流群:7156436

Git地址:https://gitee.com/ishouke
友情提示:限於時間倉促,文中可能存在錯誤,歡迎指正、評論!
作者五行缺錢,如果覺得文章對您有幫助,請掃描下邊的二維碼打賞作者,金額隨意,您的支持將是我繼續創作的源動力,打賞後如有任何疑問,請聯繫我!!!
           微信打賞                        支付寶打賞                  全國軟體測試交流QQ群  
              


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 痞子衡嵌入式半月刊: 第 68 期 這裡分享嵌入式領域有用有趣的項目/工具以及一些熱點新聞,農曆年分二十四節氣,希望在每個交節之日準時發佈一期。 本期刊是開源項目(GitHub: JayHeng/pzh-mcu-bi-weekly),歡迎提交 issue,投稿或推薦你知道的嵌入式那些事兒。 上期回顧 ...
  • TencentOS Tiny AIoT 應用創新大賽是騰訊 TencentOS 團隊聯合恩智浦半導體、安謀科技(Arm China)發起的線上開發者活動,主要面向中小企業嵌入式工程師、廣大嵌入式開發者、物聯網愛好者、創客團隊等,號召廣大開發者能參與到國內開源項目中,通過開源協同,基於 Tencent ...
  • 做I2S輸出用了PT8211(實際上買到的絲印是GH8211), 雙聲道, LSB格式, 工作正常但是輸出功率非常低, 喇叭聲音要貼近了才能勉強聽到, 所以打算做一個PT8211帶功放的I2S模塊. 最開始用的是PT8211 + LM386 * 2, 能正常工作就是LM386的電壓要求比較高, 只能... ...
  • 第二十四章 使用游標 本章將介紹什麼是游標以及如何使用游標 游標 之前的select語句檢索出來的數據,沒有辦法得到第一行或者下一行 有時,需要在檢索出來的行中前進或後退一行或多行。這就是使用游標的原因。 游標(cursor)是一個存儲在MySQL伺服器上的資料庫查詢,它不是一條SELECT語句,而 ...
  • 之前安裝MYSQL8.0的時候安裝的是綠色版,在cmd中配置完所有參數之後,在連接SQLyog的時候卻報出了以下錯誤 翻譯一下大致的意思為:客戶端不支持伺服器請求的身份驗證協議;考慮升級MYSQL客戶端 這是因為MYSQL8.0之後更換了加密規則為caching_sha2_password,8.0之 ...
  • MySQL數據模型 關係型資料庫是建立在關係模型基礎上的資料庫,簡單說,關係型資料庫是由多張能互相連接的 二維表 組成的資料庫 關係型資料庫的優點: 都是使用表結構,格式一致,易於維護。 使用通用的 SQL 語言操作,使用方便,可用於複雜查詢。 關係型資料庫都可以通過SQL進行操作,所以使用方便。 ...
  • 我們前面採集的日誌數據已經保存到 Kafka 中,作為日誌數據的 ODS 層,從 Kafka 的ODS 層讀取的日誌數據分為 3 類, 頁面日誌、啟動日誌和曝光日誌。這三類數據雖然都是用戶行為數據,但是有著完全不一樣的數據結構,所以要拆分處理。將拆分後的不同的日誌寫回 Kafka 不同主題中,作為日 ...
  • 單線程的JS 就是一個傻子,腦子一根筋,做著當前的這件事情,沒有完成之前,絕對不會做下一件事情,那麼通過什麼方法可以讓JS變“聰明”? ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...