python - Python:如何有效地导航 XML 子节点?
问题描述
我正在尝试从 XML 中提取某些数据点并尝试了两个选项...
- 使用 ElementTree 处理 XML 格式
- 使用 xmltodict 处理字典
这是我到目前为止所得到的,
代码
# Packages
# --------------------------------------
import xml.etree.ElementTree as ET
# XML Data
# --------------------------------------
message_xml = \
'<ClinicalDocument> \
<code code="34133-9" displayName="Summarization of Episode Note"/> \
<title>Care Summary</title> \
<recordTarget> \
<patientRole> \
<id assigningAuthorityName="LOCAL" extension="L123456"/> \
<id assigningAuthorityName="SSN" extension="788889999"/> \
<id assigningAuthorityName="GLOBAL" extension="G123456"/> \
<addr use="HP"> \
<streetAddressLine>1000 N SOME AVENUE</streetAddressLine> \
<city>BIG CITY</city> \
<state>NA</state> \
<postalCode>12345-1010</postalCode> \
<country>US</country> \
</addr> \
<telecom nullFlavor="NI"/> \
<patient> \
<name use="L"> \
<given>JANE</given> \
<given>JOE</given> \
<family>DOE</family> \
</name> \
</patient> \
</patientRole> \
</recordTarget> \
</ClinicalDocument>'
# Get Tree & Root
# --------------------------------------
tree = ET.ElementTree(ET.fromstring(message_xml))
root = tree.getroot()
# Iterate
# --------------------------------------
for node in root:
tag = node.tag
attribute = node.attrib
# Get ClinicalDocument.code values
if tag == 'code':
document_code_code = attribute.get('code')
document_code_name = attribute.get('displayName')
else:
pass
# Get ClinicalDocument.recordTarget values
if tag == 'recordTarget':
for child in node.iter():
# Multiple <id> tags
record_target_local = ??
record_target_ssn = ??
record_target_global = ??
# Multiple <given> tags
record_target_name_first = ??
record_target_name_middle = ??
record_target_name_last = ??
else:
pass
预期产出
document_code,document_name,id_local,id_ssn,id_global,name_first, name_middle,name_last
34133-9,Summarization of Episode Note,L123456,788889999,G123456,JANE,JOE,DOE
可接受的输出
document_code,document_name,id_type,id,name_first,name_middle,name_last
34133-9,Summarization of Episode Note,LOCAL,L123456,JANE,JOE,DOE
34133-9,Summarization of Episode Note,SSN,788889999,JANE,JOE,DOE
34133-9,Summarization of Episode Note,GLOBAL,G123456,JANE,JOE,DOE
问题
- 如何有效地导航具有多个子节点的子节点?
- 如何处理重复的标签(例如:
<id>
、、<given>
)?
解决方案
如何有效地导航具有多个子节点的子节点?
导航 XML 的一个好方法是使用XPath。ElementTree 对XPath 的支持有限,但它似乎足以满足您的需要。如果您最终需要使用更复杂的 XPath,我建议在 lxml 中使用 XPath。
如何处理重复的标签(例如:
<id>
、、<given>
)?
这取决于您需要对这些元素做什么。例如,如果您希望每个id
元素有单独的行,则需要遍历每个元素(findall()
在 ElementTree 或xpath()
lxml 中)。
如果您只需要一个值(文本或属性值),则需要将其缩小到 XPath 中的单个元素。
例如,属性值等于的id
元素将是。assigningAuthorityName
LOCAL
id[@assigningAuthorityName='LOCAL']
该given
元素有点棘手;你怎么知道一个是名字,一个是中间名?我能看到的唯一方法是位置;第一个given
( given[1]
) 是名字,第二个given
( given[2]
) 是第二个名字。你保证总是有两个given
元素吗?如果没有,您可能需要进行一些检查或 try/except 语句来获得所需的输出。
此外,由于您正在创建 csv 输出,我建议使用csv 模块;特别是DictWriter。
这将允许您将 XML 中的值存储在 dict 中以写入行。您可以为新行创建 dict 的新副本,同时保持常用值(如document_code
和document_name
)。
这是一个将为每个recordTarget
.
XML 输入(input.xml)
<ClinicalDocument>
<code code="34133-9" displayName="Summarization of Episode Note"/>
<title>Care Summary</title>
<recordTarget>
<patientRole>
<id assigningAuthorityName="LOCAL" extension="L123456"/>
<id assigningAuthorityName="SSN" extension="788889999"/>
<id assigningAuthorityName="GLOBAL" extension="G123456"/>
<addr use="HP">
<streetAddressLine>1000 N SOME AVENUE</streetAddressLine>
<city>BIG CITY</city>
<state>NA</state>
<postalCode>12345-1010</postalCode>
<country>US</country>
</addr>
<telecom nullFlavor="NI"/>
<patient>
<name use="L">
<given>JANE</given>
<given>JOE</given>
<family>DOE</family>
</name>
</patient>
</patientRole>
</recordTarget>
</ClinicalDocument>
Python
import csv
import xml.etree.ElementTree as ET
from copy import deepcopy
values_template = {"document_code": "", "document_name": "", "id_local": "", "id_ssn": "",
"id_global": "", "name_first": "", "name_middle": "", "name_last": ""}
with open("output.csv", "w", newline="") as csvfile:
csvwriter = csv.DictWriter(csvfile, delimiter=",", quoting=csv.QUOTE_MINIMAL,
fieldnames=[name for name in values_template])
csvwriter.writeheader()
tree = ET.parse('input.xml')
values_template["document_code"] = tree.find("code").get("code")
values_template["document_name"] = tree.find("code").get("displayName")
for target in tree.findall("recordTarget"):
values = deepcopy(values_template)
values["id_local"] = target.find("patientRole/id[@assigningAuthorityName='LOCAL']").get("extension")
values["id_ssn"] = target.find("patientRole/id[@assigningAuthorityName='SSN']").get("extension")
values["id_global"] = target.find("patientRole/id[@assigningAuthorityName='GLOBAL']").get("extension")
values["name_first"] = target.find("patientRole/patient/name/given[1]").text
values["name_middle"] = target.find("patientRole/patient/name/given[2]").text
values["name_last"] = target.find("patientRole/patient/name/family").text
csvwriter.writerow(values)
CSV 输出(output.csv)
document_code,document_name,id_local,id_ssn,id_global,name_first,name_middle,name_last
34133-9,Summarization of Episode Note,L123456,788889999,G123456,JANE,JOE,DOE
这是另一个示例,它将为每个 recordTarget/patientRole/id 创建一个新行...
Python
import csv
import xml.etree.ElementTree as ET
from copy import deepcopy
values_template = {"document_code": "", "document_name": "", "id": "",
"name_first": "", "name_middle": "", "name_last": ""}
with open("output.csv", "w", newline="") as csvfile:
csvwriter = csv.DictWriter(csvfile, delimiter=",", quoting=csv.QUOTE_MINIMAL,
fieldnames=[name for name in values_template])
csvwriter.writeheader()
tree = ET.parse('input.xml')
values_template["document_code"] = tree.find("code").get("code")
values_template["document_name"] = tree.find("code").get("displayName")
for target in tree.findall("recordTarget"):
values = deepcopy(values_template)
values["name_first"] = target.find("patientRole/patient/name/given[1]").text
values["name_middle"] = target.find("patientRole/patient/name/given[2]").text
values["name_last"] = target.find("patientRole/patient/name/family").text
for role_id in target.findall("patientRole/id"):
values["id"] = role_id.get("extension")
csvwriter.writerow(values)
CSV 输出(output.csv)
document_code,document_name,id,name_first,name_middle,name_last
34133-9,Summarization of Episode Note,L123456,JANE,JOE,DOE
34133-9,Summarization of Episode Note,788889999,JANE,JOE,DOE
34133-9,Summarization of Episode Note,G123456,JANE,JOE,DOE
推荐阅读
- haskell - 有什么方法可以获取 Haskell 中模块的信息吗?
- flutter - Flutter中如何平衡Wrap widget的children?
- javascript - 循环遍历 div 并突出显示文本
- c - gdb 在调试程序行为 wrt `accept()` 和 `close()` 中有何变化
- python - 在 python 中构建对象列表以进行矢量化:可以对结构(对象)列表进行矢量化,还是需要显式数组
- c# - 如何正确设置我的通用 C# 代码,以便我可以使用 using 指令访问它们?
- c# - 如何使用 C# 保存图像文件?
- odata - 将项目数据绑定到详细信息视图
- ios - Xcode:在 iPhone X 中将 imageView 设置为全屏
- video - FFmpeg:Concat 过滤器后的 NAL 单元大小无效