使用R的XML/xml2包提取XML数据至数据框失败,返回Null求解决
问题:提取XML数据到R数据框返回Null值
我刚接触R语言中的XML和xml2包,尝试将XML文件中的数据提取至数据框,但编写的脚本运行无报错,最终却返回Null值,未能成功提取数据。恳请帮忙排查问题并提供可行的解决方法。
XML示例数据
<?xml version="1.0" encoding="utf-8"?> <mod:ModificationSet xmlns:hci="http://riziv.fgov.be/szv/HealthCareInstitution" xmlns:per="http://riziv.fgov.be/szv/Person" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:pto="http://riziv.fgov.be/szv/PersonToOrganization" xmlns:org="http://riziv.fgov.be/szv/Organization" xmlns:hca="http://riziv.fgov.be/szv/HealthCareAppliance" xmlns:ati="http://riziv.fgov.be/szv/HcApplianceToHcInstitution" xmlns:p12="http://www.w3.org/2001/XMLSchema-instance" xmlns:szv="http://riziv.fgov.be/szv/BasicTypes" xmlns:hcw="http://riziv.fgov.be/szv/HealthCareWorker" xmlns:mod="http://riziv.fgov.be/szv/ModificationSet" xmlns:dev="http://riziv.fgov.be/szv/Device" xmlns:wti="http://riziv.fgov.be/szv/HcWorkerToHcInstitution"> <mod:Payload> <mod:Modifications> <mod:Modification> <mod:Context> <szv:Origin>63080900</szv:Origin> <szv:CreationDate>2018-04-05</szv:CreationDate> <szv:OperationType>01</szv:OperationType> <szv:OperationDate>2018-04-05</szv:OperationDate> </mod:Context> <mod:HealthCareAppliance> <hca:Identification> <hca:RizivNumber>00000182</hca:RizivNumber> </hca:Identification> <hca:Device> <dev:DeviceNumber>30016</dev:DeviceNumber> <dev:DeviceType>PET-CT</dev:DeviceType> <dev:Model>Philips-Gemini TF Big Bore PET/CT</dev:Model> <dev:StartDateInvoicing>2016-06-01</dev:StartDateInvoicing> <dev:EndDateInvoicing p12:nil="true" /> <dev:LocationIsAddress>false</dev:LocationIsAddress> <dev:IsFixedDevice>true</dev:IsFixedDevice> <dev:IsExtraMuros>false</dev:IsExtraMuros> </hca:Device> </mod:HealthCareAppliance> </mod:Modification> <mod:Modification> <mod:Context> <szv:Origin>63080900</szv:Origin> <szv:CreationDate>2018-04-05</szv:CreationDate> <szv:OperationType>01</szv:OperationType> <szv:OperationDate>2010-07-13</szv:OperationDate> </mod:Context> <mod:HealthCareAppliance> <hca:Identification> <hca:RizivNumber>00000182</hca:RizivNumber> </hca:Identification> <hca:Status> <hca:StatusCode>InUse</hca:StatusCode> <hca:StatusStartDate>2010-07-13</hca:StatusStartDate> </hca:Status> </mod:HealthCareAppliance> </mod:Modification> <mod:Modification> <mod:Context> <szv:Origin>63080900</szv:Origin> <szv:CreationDate>2018-04-05</szv:CreationDate> <szv:OperationType>01</szv:OperationType> <szv:OperationDate>2018-04-05</szv:OperationDate> </mod:Context> <mod:HcApplianceToHcInstitution> <ati:HealthCareInstitution> <ati:RizivNumber>71024388</ati:RizivNumber> <ati:InstitutionCode>710</ati:InstitutionCode> </ati:HealthCareInstitution> <ati:HealthCareAppliance> <ati:RizivNumber>00000182</ati:RizivNumber> </ati:HealthCareAppliance> <ati:Period> <szv:StartDate>2016-08-19</szv:StartDate> <szv:EndDate p12:nil="true" /> </ati:Period> </mod:HcApplianceToHcInstitution> </mod:Modification> </mod:Modifications> </mod:Payload> </mod:ModificationSet>
已编写的R代码(未完成)
library(XML) xmldoc <- xmlParse("BAS_SIT_HCA_20180405141931.xml", useInternalNodes=TRUE) class(xmldoc) namespace_list <- c(mod="http://riziv.fgov.be/szv/ModificationSet", szv="http://riziv.fgov.be/szv/BasicTypes", hca="http://riziv.fgov.be/szv/HealthCareAppliance", dev="http://riziv.fgov.be/szv/Device", ati="http://riziv.fgov.be/szv/HcApplianceToHcInstitution") do.call(rbind, xpathApply(xmldoc, "//mod:ModificationSet/mod:Payload/mod:Modifications /mod:Modification", namespaces=namespace_list, function(node) { Origin <- xmlValue(node[["./mod:Context/szv:Origin"]]) CreationDate <- xmlValue(node[["./mod:Context/szv:CreationDate"]]) OperationType <- xmlValue(node[["./mod:Context/szv:OperationType"]]) OperationDate <- xmlValue(node[["./mod:Context/szv:OperationDate"]]) xp1 <- "./mod:HealthCareAppliance/hca:Identification/hca:RizivNumber" RizivNumber <- xpathSApply(node, xp1, namespaces=namespace_list, xmlValue) if(is.null(RizivNumber)) RizivNumber <- NA xp2 <- "./mod:HealthCareAppliance/hca:Device/dev:DeviceNumber" DeviceNumber <- xpathSApply(node, xp2, namespaces=namespace_list, xmlValue) if(is.null(DeviceNumber)) DeviceNumber <- NA xp3 <- "./mod:HealthCareAppliance/hca:Device/dev:DeviceType" DeviceType <- xpathSApply(node, xp
问题排查与解决方法
我帮你梳理下导致返回Null的几个核心问题,再给出修正后的完整代码:
关键问题点
- XPath路径错误:你写的路径
"//mod:ModificationSet/mod:Payload/mod:Modifications /mod:Modification"里有个多余的空格,这会导致XPath无法正确定位到mod:Modification节点。 - 节点选择方式不当:用
node[[xpath]]的方式如果节点不存在会直接返回NULL,而后续没有妥善处理这种情况,容易导致整个数据行返回NULL。 - 代码未完成:你提供的代码到
xp3就中断了,没有完整的字段提取和返回逻辑,自然无法生成数据框。 - 命名空间处理细节:虽然你定义了命名空间,但部分字段提取时的路径需要确保命名空间前缀完全匹配XML中的定义。
修正后的解决方案(XML包版本)
library(XML) # 读取XML文档 xmldoc <- xmlParse("BAS_SIT_HCA_20180405141931.xml", useInternalNodes=TRUE) # 完整的命名空间映射(覆盖XML中所有用到的前缀) namespace_list <- c( mod = "http://riziv.fgov.be/szv/ModificationSet", szv = "http://riziv.fgov.be/szv/BasicTypes", hca = "http://riziv.fgov.be/szv/HealthCareAppliance", dev = "http://riziv.fgov.be/szv/Device", ati = "http://riziv.fgov.be/szv/HcApplianceToHcInstitution" ) # 修正XPath路径(去掉多余空格),完善提取逻辑 result_list <- xpathApply(xmldoc, "//mod:ModificationSet/mod:Payload/mod:Modifications/mod:Modification", namespaces = namespace_list, function(node) { # 提取Context下的核心字段,用length判断节点是否存在 Origin <- xpathSApply(node, "./mod:Context/szv:Origin", namespaces=namespace_list, xmlValue) Origin <- ifelse(length(Origin) == 0, NA_character_, Origin) CreationDate <- xpathSApply(node, "./mod:Context/szv:CreationDate", namespaces=namespace_list, xmlValue) CreationDate <- ifelse(length(CreationDate) == 0, NA_character_, CreationDate) OperationType <- xpathSApply(node, "./mod:Context/szv:OperationType", namespaces=namespace_list, xmlValue) OperationType <- ifelse(length(OperationType) == 0, NA_character_, OperationType) OperationDate <- xpathSApply(node, "./mod:Context/szv:OperationDate", namespaces=namespace_list, xmlValue) OperationDate <- ifelse(length(OperationDate) == 0, NA_character_, OperationDate) # 提取HealthCareAppliance的RizivNumber RizivNumber <- xpathSApply(node, "./mod:HealthCareAppliance/hca:Identification/hca:RizivNumber", namespaces=namespace_list, xmlValue) RizivNumber <- ifelse(length(RizivNumber) == 0, NA_character_, RizivNumber) # 提取Device相关字段 DeviceNumber <- xpathSApply(node, "./mod:HealthCareAppliance/hca:Device/dev:DeviceNumber", namespaces=namespace_list, xmlValue) DeviceNumber <- ifelse(length(DeviceNumber) == 0, NA_character_, DeviceNumber) DeviceType <- xpathSApply(node, "./mod:HealthCareAppliance/hca:Device/dev:DeviceType", namespaces=namespace_list, xmlValue) DeviceType <- ifelse(length(DeviceType) == 0, NA_character_, DeviceType) Model <- xpathSApply(node, "./mod:HealthCareAppliance/hca:Device/dev:Model", namespaces=namespace_list, xmlValue) Model <- ifelse(length(Model) == 0, NA_character_, Model) StartDateInvoicing <- xpathSApply(node, "./mod:HealthCareAppliance/hca:Device/dev:StartDateInvoicing", namespaces=namespace_list, xmlValue) StartDateInvoicing <- ifelse(length(StartDateInvoicing) == 0, NA_character_, StartDateInvoicing) # 提取机构关联信息 InstRizivNumber <- xpathSApply(node, "./mod:HcApplianceToHcInstitution/ati:HealthCareInstitution/ati:RizivNumber", namespaces=namespace_list, xmlValue) InstRizivNumber <- ifelse(length(InstRizivNumber) == 0, NA_character_, InstRizivNumber) InstCode <- xpathSApply(node, "./mod:HcApplianceToHcInstitution/ati:HealthCareInstitution/ati:InstitutionCode", namespaces=namespace_list, xmlValue) InstCode <- ifelse(length(InstCode) == 0, NA_character_, InstCode) # 返回单行数据框 data.frame( Origin = Origin, CreationDate = CreationDate, OperationType = OperationType, OperationDate = OperationDate, RizivNumber = RizivNumber, DeviceNumber = DeviceNumber, DeviceType = DeviceType, Model = Model, StartDateInvoicing = StartDateInvoicing, InstRizivNumber = InstRizivNumber, InstCode = InstCode, stringsAsFactors = FALSE ) }) # 合并所有行到最终数据框 result_df <- do.call(rbind, result_list) print(result_df)
更推荐的方案(xml2包版本,更现代直观)
xml2包是tidyverse生态的一部分,处理XML更简洁,命名空间管理更友好:
library(xml2) # 读取XML文档 xml_doc <- read_xml("BAS_SIT_HCA_20180405141931.xml") # 绑定命名空间,确保所有前缀匹配 xml_ns(xml_doc) <- c( mod = "http://riziv.fgov.be/szv/ModificationSet", szv = "http://riziv.fgov.be/szv/BasicTypes", hca = "http://riziv.fgov.be/szv/HealthCareAppliance", dev = "http://riziv.fgov.be/szv/Device", ati = "http://riziv.fgov.be/szv/HcApplianceToHcInstitution" ) # 获取所有Modification节点 mod_nodes <- xml_find_all(xml_doc, "//mod:Modification") # 批量提取每个节点的字段,用default参数处理缺失值 result_df <- lapply(mod_nodes, function(node) { data.frame( Origin = xml_text(xml_find_first(node, "./mod:Context/szv:Origin"), default = NA_character_), CreationDate = xml_text(xml_find_first(node, "./mod:Context/szv:CreationDate"), default = NA_character_), OperationType = xml_text(xml_find_first(node, "./mod:Context/szv:OperationType"), default = NA_character_), OperationDate = xml_text(xml_find_first(node, "./mod:Context/szv:OperationDate"), default = NA_character_), RizivNumber = xml_text(xml_find_first(node, "./mod:HealthCareAppliance/hca:Identification/hca:RizivNumber"), default = NA_character_), DeviceNumber = xml_text(xml_find_first(node, "./mod:HealthCareAppliance/hca:Device/dev:DeviceNumber"), default = NA_character_), DeviceType = xml_text(xml_find_first(node, "./mod:HealthCareAppliance/hca:Device/dev:DeviceType"), default = NA_character_), Model = xml_text(xml_find_first(node, "./mod:HealthCareAppliance/hca:Device/dev:Model"), default = NA_character_), StartDateInvoicing = xml_text(xml_find_first(node, "./mod:HealthCareAppliance/hca:Device/dev:StartDateInvoicing"), default = NA_character_), InstRizivNumber = xml_text(xml_find_first(node, "./mod:HcApplianceToHcInstitution/ati:HealthCareInstitution/ati:RizivNumber"), default = NA_character_), InstCode = xml_text(xml_find_first(node, "./mod:HcApplianceToHcInstitution/ati:HealthCareInstitution/ati:InstitutionCode"), default = NA_character_), stringsAsFactors = FALSE ) }) %>% do.call(rbind, .) print(result_df)
核心修复说明
- 修正了XPath路径中的多余空格,确保节点定位准确
- 用
length()(XML包)或default参数(xml2包)处理缺失节点,避免返回NULL - 完善了所有字段的提取逻辑,覆盖XML中的所有关键信息
- 确保命名空间映射完整,匹配XML中定义的所有前缀
内容的提问来源于stack exchange,提问作者Avinash Kumar
相关产品推荐
相关产品推荐

