XML 解析:如何返回 None is not found 元素

XML parsing: how to return None is element was not found

提问人:Serge Kashlik 提问时间:10/16/2023 更新时间:10/17/2023 访问量:51

问:

我需要解析一个 XML 文件,其中同一标记可能存在不同数量的子项,如下所示:

xml_file = '''
<clients>
    <client>
        <name>John</name>
    </client>
    <client>
        <name>Mike</name>
        <age>25</age>
    </client>
</clients>
'''

如果未找到任何标签,我希望代码返回。我目前的解决方案如下所示:None

from xml.etree import ElementTree as et

tree = et.fromstring(xml_file)
client_attr = []
for client in tree.iter('client'):
    results = {'name': None, 'age': None}
    name = client.find('name')
    age = client.find('age')

    results.update({
        'name': name.text if name is not None else None,
        'age': age.text if age is not None else None
     }
    )
    client_attr.append(results)

然而,这似乎有点笨拙,当应用于我的真实数据时,会占用大量的空间和时间。在我看来,应该有一种方法可以组合这些步骤,但我无法弄清楚如何。 提前谢谢。

Python XML 解析

评论

0赞 10/16/2023
你的意思是,如果标签中没有嵌套的标签,代码应该返回吗?Noneclientclients
0赞 Serge Kashlik 10/16/2023
不,如果 tage 没有标记,我希望代码返回 none。clientage

答:

1赞 user19151114 10/16/2023 #1

乍一看,为了实现您希望实现的行为,您必须考虑将会出现的其他一些问题,这些问题您可能已经考虑过,但可惜的是,您似乎没有在发布的查询中指定您计划如何进行它们。

例如,正如您在注释中澄清的那样,您想要代码,我认为这是函数的一部分,因为如果不是,您就不可能返回 None 或查询中显示状态的任何值;如果找到未指定年龄的标签,您希望它返回 None,这引发了有关其余数据的问题,这意味着,您将如何处理到目前为止处理的内容,您是否打算简单地丢弃它?然后是关于您将如何通知用户有关差异的问题,以及最终您将采取什么措施来解决问题。

这可能会导致一个更复杂的解析系统,但它仍然更健壮,并且总体上试图明智地利用系统的资源。

尽管如此,如果这不是您打算通过代码实现的目标,那么我将简单地建议: a) 将代码包装到一个函数中,如果希望在找不到 age 或 name 时返回 None,即当函数返回 None 时,则这样做,并且循环不会继续,从而将时间整体切片,除了格式错误的元素是最后一个元素的最坏情况; b) 在发生异常时引发异常,以便调用方适当地处理它,这可能需要通知用户文件不遵守格式规则等。为此,etree有一个例外。findParseError

现在,要记住的一件事是,None 是模棱两可的,因为它可以代表任何东西,并且为了让某人知道在这种情况下它代表没有 age child 标签,这将意味着该特定的人熟悉代码,和/或文档正在描述这种行为, 并且隐含地表示,每个处理代码的人都在阅读上述文档。

在这种情况下,由于代码的编写方式,我建议使用异常方法解决上述歧义,另一种方法是返回一个类型,该类型要么保存值,在本例中为字典,要么,这将在某种程度上使您能够返回数据,直到它是正确的点, 如果第一个元素已经有故障,则为 None。OptionalSomeNone

此外,您实际上不需要调用,因为您可以直接将值分配给字典,这也可能稍微提高代码的效率。update

0赞 Hermann12 10/16/2023 #2

如果可以使用 pandas:

import pandas as pd
import numpy as np

xml_file = '''
<clients>
    <client>
        <name>John</name>
    </client>
    <client>
        <name>Mike</name>
        <age>25</age>
    </client>
</clients>
'''

df = pd.read_xml(xml_file, xpath = './/client')
print(df.replace(np.nan, None))

如果没有 numpy 和 replace 函数,pandas 将显示 NaN。

   name   age
0  John  None
1  Mike  25.0

评论

0赞 Serge Kashlik 10/17/2023
谢谢,但并不是一个真正可行的选择,因为真正的 xml 文件有几层不同深度的层,我不需要提取所有标签。
0赞 Andrej Kesely 10/17/2023 #3

另一种解决方案,使用:beautifulsoup

import pandas as pd
from bs4 import BeautifulSoup

xml_file = """
<clients>
    <client>
        <name>John</name>
    </client>
    <client>
        <name>Mike</name>
        <age>25</age>
    </client>
</clients>
"""

soup = BeautifulSoup(xml_file, "xml")

all_data = []
for client in soup.select("client"):
    all_data.append(
        {
            t.name: t.get_text(strip=True, separator=" ")
            for t in client.find_all(recursive=False)
        }
    )

df = pd.DataFrame(all_data)
print(df)

指纹:

   name  age
0  John  NaN
1  Mike   25