Как эффективно перемещаться по дочерним узлам с несколькими дочерними узлами под ними?
Хороший способ навигации по XML - это XPath .ElementTree имеет ограниченную поддержку XPath , но она выглядит достаточно хорошо для того, что вам нужно.Если вам в конечном итоге потребуется использовать более сложный XPath, я бы предложил использовать XPath в lxml .
Как обрабатывать дублирующиеся теги (например: <id>
, <given>
)?
Это зависит от того, что вам нужно делать с этими элементами.Например, если вам нужны отдельные строки для каждого элемента id
, вам необходимо выполнить итерацию по каждому элементу (с findall()
в ElementTree или xpath()
в lxml).
Если вы просто хотитезначение (либо текст, либо значение атрибута), вам нужно сузить его до одного элемента в XPath.
Например, элемент id
с атрибутом assigningAuthorityName
, равным LOCAL
, будет id[@assigningAuthorityName='LOCAL']
.
Элемент given
немного сложнее;как вы можете сказать, что одно имя, а второе отчество?Единственный способ увидеть это позиция;первое given
(given[1]
) является первым именем, а второе given
(given[2]
) является вторым именем.Вы гарантированно всегда имеете два given
элемента?Если нет, то вам может потребоваться выполнить некоторые проверки или операторы / исключить, чтобы получить нужный вывод.
Кроме того, поскольку вы создаете вывод в формате csv, я бы рекомендовал использовать модуль csv * 1041.*;в частности DictWriter .
Это позволит вам хранить значения из XML в поле для записи строк.Вы можете создавать новые копии 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
Вот еще один пример, который создаст новую строку для каждой записиTarget / 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