我有一个具有以下架构的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。
我会做这样的事情:使用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>
考虑 XSLT,这是一种专用语言,旨在转换 XML,例如保留具有重复属性的节点。Python的第三方模块lxml
可以运行XSLT 1.0脚本。美妙之处在于XSLT可以移植到其他语言/软件,并且不需要Python来运行它!
具体来说,下面使用慕尼黑分组来索引文档,其中包含每个不同@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>