需要在 Python 中用其他元素的子重复一个 XML 元素

Need to repeat one XML element with other element's subs in Python

提问人:Bushido_T 提问时间:11/18/2023 最后编辑:TimelessBushido_T 更新时间:11/18/2023 访问量:79

问:

我有一些晦涩难懂的XML文件,其中包含一些大量嵌套的元素。我能够解析元素并获取所有内容,但遇到了无法解决的问题。可能有更好的方法,但我在解析 XML 方面还不是很精通。

XML 文件

<NAXML-MaintenanceRequest xmlns:vxt="urn:vfi-sapphire:np.naxmlext.2005-06-24" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.4" xmlns="http://www.naxml.org/POSBO/Vocabulary/2003-10-16">
  <TransmissionHeader>
    <StoreLocationID>7500</StoreLocationID>
    <VendorName>PDI</VendorName>
    <VendorModelVersion>PDI/Enterprise 11.9.01</VendorModelVersion>
  </TransmissionHeader>
  <ItemListMaintenance>
    <TableAction type="initialize" />
    <RecordAction type="addchange" confirm="no" />
    <ILTDetail>
      <RecordAction type="addchange" confirm="no" />
      <ItemListID>100</ItemListID>
      <ItemListDescription>MM\Dasani 1L</ItemListDescription>
      <ItemListEntry>
        <ItemCode>
          <POSCodeFormat format="gtin" checkDigit="present" />
          <POSCode>00049000026566</POSCode>
          <POSCodeModifier name="EACH10">0</POSCodeModifier>
        </ItemCode>
      </ItemListEntry>
    </ILTDetail>
    <ILTDetail>
      <RecordAction type="addchange" confirm="no" />
      <ItemListID>101</ItemListID>
      <ItemListDescription>MM\Vitamin Water 20oz</ItemListDescription>
      <ItemListEntry>
        <ItemCode>
          <POSCodeFormat format="gtin" checkDigit="present" />
          <POSCode>00786162150004</POSCode>
          <POSCodeModifier name="EACH5">0</POSCodeModifier>
        </ItemCode>
      </ItemListEntry>
      <ItemListEntry>
        <ItemCode>
          <POSCodeFormat format="gtin" checkDigit="present" />
          <POSCode>00786162002976</POSCode>
          <POSCodeModifier name="EACH5">0</POSCodeModifier>
        </ItemCode>
      </ItemListEntry>
    </ILTDetail>
  </ItemListMaintenance>
</NAXML-MaintenanceRequest>

我正在尝试获取 ItemListID 和分配给它的任何相应 POSCode。当我尝试时,我只能获得每个值,但不能将它们放在一起。我确信我的方法步骤太多,但这是我所能理解的当前状态。

import pandas as pd
import xml.etree.ElementTree as ET
import numpy as np
import os, os.path, sys
import csv

itemFile = "myfilepathhere.xml"
tree = ET.parse(itemFile)
root = tree.getroot()

# Returns key value pairs #
def extract_elements(element, path='', elements=None):
    if elements is None:
        elements = []
    path = '/'.join([path, element.tag]) if path else element.tag
    for child in element:
        if len(child) == 0:
            elements.append({'Path': child.tag, 'Value': child.text})
        else:
            extract_elements(child, path, elements)
    return elements

all_elements = []
for record in root:
    all_elements.extend(extract_elements(record))
df = pd.DataFrame(all_elements)
print(df)

如果有更好的道路,我将不胜感激。理想情况下,结果是

ItemListID POSCode
100 00049000026566
101 00786162150004
101 00786162002976
Python 熊猫 XML

评论


答:

1赞 erny 11/18/2023 #1

您可以使用 or 方法并通过 XPATH 进行搜索,例如(示例已更新,感谢 @mzjn):findalliterfind

tree = ET.parse('<xml file>')
details = tree.findall('.//{*}ILTDetail')
for detail in detail:
    itemListID, POSCode = detail[1].text, detail[3][0][1].text
    ...

这些索引是关于获得第 n 个子项的,即 意思是:获取细节的第4个子项,获取结果的第一个子项,然后获取最后一个结果的第二个子项。(索引从 0 开始)detail[3][0][1]

除此之外,lxml 库还提供了一个 ElementTree 接口,它的用法与 xml.etree 非常相似。但它更易于使用:

from lxml import etree as ET
tree = ET.parse('<xml file>')
details = tree.findall(".//{*}ILTDetail")

评论

1赞 mzjn 11/18/2023
您可以为命名空间使用通配符:stackoverflow.com/a/62117710/407651
0赞 erny 11/18/2023
@mzjn:太酷了,谢谢。我更新了示例。
0赞 Bushido_T 11/18/2023
谢谢你,lxml确实有一些易于使用的功能,我现在正在研究。
1赞 Timeless 11/18/2023 #2

遍历每个 ,制作一个字典列表并将其传递给 DataFrame 构造函数:ILTDetail

import xml.etree.ElementTree as ET
import pandas as pd    

elems = ET.parse("file.xml").findall(".//{*}ILTDetail")

df =  pd.DataFrame(
    [
        {
            "ItemListID": det.findtext("{*}ItemListID"),
            "POSCode": psc.text
        } for det in elems for psc in det.findall(".//{*}POSCode")
    ]
)

输出:

print(df)

  ItemListID         POSCode
0        100  00049000026566
1        101  00786162150004
2        101  00786162002976

[3 rows x 2 columns]

评论

1赞 Bushido_T 11/18/2023
非常感谢你!我能够完全按照我的需要提取所有值。
2赞 LMC 11/18/2023 #3

Pandas 唯一的解决方案
与功能一起使用,然后“重塑”数据帧。 每个元素最多考虑 3 个元素
pandas.read_xml()iterparsePOSCodeItemListID

iterdict = {"ILTDetail": ["ItemListID", "POSCode", "POSCode", "POSCode"]}
names = ["ItemListID", "POSCode1", "POSCode2","POSCode3"]
df_tmp = pd.read_xml('tmp2', iterparse=iterdict, dtype=object, names=names)
df = pd.melt(df_tmp, id_vars=names[0], value_vars=names[1:], value_name="POSCode").dropna()[["ItemListID","POSCode"]

结果

0        100  00049000026566
1        101  00786162150004
2        102  00786162003458
4        101  00786162002976
5        102  00786162004621
8        102  00786162021870