【问题标题】:Python extract from XML keep the same schema从 XML 中提取的 Python 保持相同的模式
【发布时间】:2018-01-05 13:23:10
【问题描述】:

我有一个具有以下架构的 XML:

<?xml version="1.0" encoding="utf-8" ?>
<ROOT>
<facturic id_user="18446195"><artfacturic/></facturic>
<facturic id_user="18446195"><artfacturic/></facturic>
<facturic id_user="34259554"><artfacturic/></facturic>
</ROOT>

它们比 id_user 有更多的属性,但只有一个是相关的,因为我需要将 xml 文件拆分为具有重复 ID 的单独文件。

我设法使用下面的代码找到了重复项:

import os
from xml.etree import ElementTree
dom = ElementTree.parse(fullpath)
findNode = 'facturic'
findAttr = 'id_user'
childNodes = dom.findall(findNode)
userIdDict = dict()     #student={'name':'john','age':23}
duplicateUserId = dict()

#GET UNIQUE USER ID AND COUNT
for c in childNodes:
    userId = c.attrib.get(findAttr)  #gets attributes
    #print(nrFact)

    if userId not in userIdDict:
        userIdDict[userId] = 1
    else:
        userIdDict[userId] = userIdDict[userId] + 1

# print(userIdDict)
for userId in userIdDict:
    userIdCount = userIdDict[userId]
    if userIdCount > 1:
        duplicateUserId[userId]=userIdCount

你们能给我一个想法,如何创建新的 xml 文件,该文件只包含具有重复项的节点,但使用与初始文件相同的模式? 它应该类似于为每个重复节点创建新的 xml 文件,或者理想情况下,假设重复用户 ID 的最大数量为 4,只创建 4 个文件,但每个文件应该只包含具有所有其他初始属性的唯一 ID。

【问题讨论】:

    标签: python xml python-3.x split


    【解决方案1】:

    我会这样做:使用defaultdict(list) 来收集每个id_user 值的节点。然后,对生成的字典进行后处理,将副本写入单独的文件。使用lxml.etree

    from collections import defaultdict
    from lxml import etree
    
    tree = etree.parse("input.xml")
    
    facturics = defaultdict(list)
    
    for node in tree.xpath(".//facturic"):
        facturics[node.attrib["id_user"]].append(node)
    
    for user_id, nodes in facturics.items():
        if len(nodes) > 1:  # save duplicates
            with open("{user_id}.xml".format(user_id=user_id), "w") as output_file:
                root = etree.Element("ROOT")
                for node in nodes:
                    root.append(node)
                etree.ElementTree(root).write(output_file, pretty_print=True)
    

    运行这段代码后,会在当前目录下生成一个名为18446195.xml的新文件,内容如下:

    <ROOT>
        <facturic id_user="18446195"><artfacturic/></facturic>
        <facturic id_user="18446195"><artfacturic/></facturic>
    </ROOT>
    

    【讨论】:

      【解决方案2】:

      考虑XSLT,这是一种专用语言,旨在转换XML,例如保留具有重复属性的节点。 Python 的第三方模块lxml 可以运行 XSLT 1.0 脚本。美妙之处在于 XSLT 可以移植到其他语言/软件,并且不需要 Python 来运行它!

      具体来说,下面使用Muenchenian Grouping 为每个不同的@id_user 使用xsl:key 索引文档。然后模板匹配只检索计数大于 1 的那些。

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

      <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
        <xsl:strip-space elements="*"/>
      
        <xsl:key name="idkey" match="facturic" use="@id_user" />
      
        <xsl:template match="/ROOT">
          <xsl:copy>
            <xsl:apply-templates select="facturic"/>
          </xsl:copy>
        </xsl:template>
      
        <xsl:template match="facturic[count(key('idkey', @id_user)) > 1]">
          <xsl:copy>
              <xsl:copy-of select="*|@*"/>    
          </xsl:copy>
        </xsl:template>
      
      </xsl:stylesheet>
      

      Python (没有for 循环或if 逻辑)

      import lxml.etree as et
      
      # LOAD XML AND XSL FILES
      doc = et.parse('Input.xml')
      xsl = et.parse('XSLTScript.xsl')
      
      # INITIALIZE TRANSFORMER AND RUN
      transform = et.XSLT(xsl)    
      result = transform(doc)
      
      # PRINT TO CONSOLE
      print(result)
      
      # SAVE TO FILE
      with open('Output.xml', 'wb') as f:
         f.write(result)
      

      输出

      <?xml version="1.0"?>
      <ROOT>
        <facturic id_user="18446195">
          <artfacturic/>
        </facturic>
        <facturic id_user="18446195">
          <artfacturic/>
        </facturic>
      </ROOT>
      

      【讨论】:

        猜你喜欢
        • 2014-06-26
        • 2022-07-01
        • 2016-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多