r - 在不同深度访问相同 XML 的多个元素以转换为 R tibble
问题描述
我有一个相当深的旅行数据 xml 文件,我在这里匿名了。我想提取多个航段的优惠券状态并将它们附加到行程 ID。我在使用 xml2 包时遇到了非常困难的情况,我认为原因是我的一些 XML 数据以文本结尾,而一些以属性结尾。我试图将 xml 转换为带有as_list()
. 我也尝试过,xml_find_all()
但无论我搜索的节点是什么(例如,票务或优惠券应该可以工作),都会得到一个 0 的节点集。下面是数据:
<?xml version="1.0" encoding="UTF-8"?>
<eTicketCouponRS xmlns="http://webse" xmlns:ns4="http://s" xmlns:stl="http://se" Version="2.0.0">
<stl:ApplicationResults status="Complete">
<stl:Success timeStamp="2021-06-16T11:39:52-05:00" />
</stl:ApplicationResults>
<TicketingInfos>
<TicketingInfo>
<Ticketing AgencyCity="DCA" AgentWorkArea="A" IATA_Number="0952" IssuingAgent="A" PrimeHostID="1S" PseudoCityCode="5SE0" TransactionDateTime="2021-06-16T11:39">
<CouponData InformationSource="S" IssueDate="2021-03-29" NumBooklets="1" TicketMedia="E" TicketMode="63">
<AirItineraryPricingInfo>
<FareCalculation>
<Text>SAN AA X/E/DFW AA TYO M0.00NUC0.00END ROE1.00 XFSAN4.5DFW4.5</Text>
</FareCalculation>
<ItinTotalFare>
<BaseFare Amount="0.00" CurrencyCode="USD" />
<Taxes>
<Tax Amount="19.10" TaxCode="US" />
<Tax Amount="5.60" TaxCode="AY" />
<Tax Amount="9.00" TaxCode="XF" />
</Taxes>
<TotalFare Amount=".70" CurrencyCode="USD" />
</ItinTotalFare>
<PassengerTypeQuantity Code="GV1" />
</AirItineraryPricingInfo>
<Coupons>
<Coupon CodedStatus="OK" Number="1" StatusCode="RFND">
<FlightSegment DepartureDateTime="2021-08-13T06:15" FlightNumber="2535" RPH="1" ResBookDesigCode="V">
<DestinationLocation LocationCode="DFW" />
<FareBasis Code="VCA" />
<MarketingAirline Code="AA" FlightNumber="2535" />
<OperatingAirline Code="AA" />
<OriginLocation LocationCode="SAN" />
</FlightSegment>
</Coupon>
<Coupon CodedStatus="OK" Number="2" StatusCode="RFND">
<FlightSegment ConnectionInd="X" DepartureDateTime="2021-08-13T12:20" FlightNumber="175" RPH="2" ResBookDesigCode="V">
<DestinationLocation LocationCode="HND" />
<FareBasis Code="VCA" />
<MarketingAirline Code="AA" FlightNumber="175" />
<OperatingAirline Code="AA" />
<OriginLocation LocationCode="DFW" />
<FareTypeClass>PG</FareTypeClass>
<FareTypeRule>OW-GO</FareTypeRule>
</FlightSegment>
</Coupon>
</Coupons>
<CustomerInfo>
<Customer>
<Invoice Number="126" />
<Payment ApprovalID="03" RPH="1" ReferenceNumber="XXXXXXXXXXXX" Type="CC">
<CC_Info>
<PaymentCard Code="VI" ExpirationDate="XX-XX" />
</CC_Info>
</Payment>
<PersonName NameReference="PCS" PassengerType="GV1">
<GivenName>VER</GivenName>
<Surname>DE</Surname>
</PersonName>
</Customer>
</CustomerInfo>
<ItineraryRef CustomerIdentifier="R5" ID="EXAMPLE" />
</CouponData>
</Ticketing>
</TicketingInfo>
</TicketingInfos>
</eTicketCouponRS>
我有大约 100 个分别加载并拉出一个由以下列组成的小表:SuccTimeStamp TransacTimeStamp ItineraryID CouponNumber StatusCode Origin Destination OperatingAirline FlightNumber。
您可以看到这些元素中的每一个都位于 xml 的不同深度,并且每个旅行路线都有不同数量的优惠券,从 1 到 10 不等。我还从 hrbrmstr 找到了一篇有用的帖子,从 2018 年开始帮助某人,但我无法获得类似的解决方案来“查看”我的节点,而且我不确定这是我的代码还是我的 xml 数据。
任何帮助表示赞赏!
解决方案
对于需要为最终使用需求(例如 R)进行展平的嵌套 XML 文件,请考虑XSLT,这是一种旨在转换 XML 文件的专用语言。您可以使用该包在 R 中运行 XSLT 1.0 脚本xslt
(sister to xml2
)。或者,您可以使用专用的XSLT 处理器并让 R 使用system()
. 与 SQL 一样,XSLT 是一种行业的可移植语言,不仅限于 R。
在 XSLT 中,由于您的粒度是优惠券,您可以从<Coupon>
级别中提取并使用ancestor::
XPath 轴来检索更高级别的信息。由于需要默认命名空间,因此使用了长篇大论<xsl:element>
。IATA_Number
假定为ItineraryID。
XSLT (另存为 .xsl 文件,特殊的 .xml 文件)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:w="http://webse"
xmlns:stl="http://se">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/w:eTicketCouponRS">
<xsl:copy>
<xsl:apply-templates select="descendant::w:Coupon"/>
</xsl:copy>
</xsl:template>
<xsl:template match="w:Coupon">
<xsl:copy>
<xsl:element name="SuccTimeStamp" namespace="http://webse">
<xsl:value-of select="ancestor::w:eTicketCouponRS/stl:ApplicationResults/stl:Success/@timeStamp"/>
</xsl:element>
<xsl:element name="TransacTimeStamp" namespace="http://webse">
<xsl:value-of select="ancestor::w:Ticketing/@TransactionDateTime"/>
</xsl:element>
<xsl:element name="ItineraryID" namespace="http://webse">
<xsl:value-of select="ancestor::w:Ticketing/@IATA_Number"/>
</xsl:element>
<xsl:element name="CouponNumber" namespace="http://webse">
<xsl:value-of select="@Number"/>
</xsl:element>
<xsl:element name="StatusCode" namespace="http://webse">
<xsl:value-of select="@CodedStatus"/>
</xsl:element>
<xsl:element name="Origin" namespace="http://webse">
<xsl:value-of select="w:FlightSegment/w:OriginLocation/@LocationCode"/>
</xsl:element>
<xsl:element name="Destination" namespace="http://webse">
<xsl:value-of select="w:FlightSegment/w:DestinationLocation/@LocationCode"/>
</xsl:element>
<xsl:element name="OperatingAirline" namespace="http://webse">
<xsl:value-of select="w:FlightSegment/w:OperatingAirline/@Code"/>
</xsl:element>
<xsl:element name="FlightNumber" namespace="http://webse">
<xsl:value-of select="w:FlightSegment/@FlightNumber"/>
</xsl:element>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
R
library(xml2)
library(xslt)
# LOAD XML AND XSLT
doc <- read_xml("/path/to/Input.xml")
style <- read_xml("/path/to/Style.xsl", package = "xslt")
# RUN TRANSFORMATION AND SEE OUTPUT
flat_xml <- xml_xslt(doc, style)
cat(as.character(flat_xml))
# RETRIEVE data NODES
nmsp <- c(w = "http://webse")
recs <- xml2::xml_find_all(flat_xml, "//w:Coupon", ns=nmsp)
# BIND EACH CHILD TEXT AND NAME
df_list <- lapply(recs, function(r) {
vals <- xml2::xml_children(r)
data.frame(rbind(setNames(c(xml2::xml_text(vals)),
c(xml2::xml_name(vals)))))
})
# COMBINE ALL DFS
final_df <- do.call(rbind.data.frame, df_list)
rm(recs, df_list)
final_df
# SuccTimeStamp TransacTimeStamp ItineraryID CouponNumber StatusCode Origin Destination OperatingAirline FlightNumber
# 1 2021-06-16T11:39:52-05:00 2021-06-16T11:39 0952 1 OK SAN DFW AA 2535
# 2 2021-06-16T11:39:52-05:00 2021-06-16T11:39 0952 2 OK DFW HND AA 175
以上针对单个 XML 运行。对于 100 个单独的文件,用用户定义的方法包装上面,并lapply
在最后运行用于主连接的 XML 数据帧列表。假设 XML 文件保持相同的结构,在循环外加载一次 XSLT,因为它不会改变。
style <- read_xml("/path/to/Style.xsl", package = "xslt")
xml_to_df <- function(xml_file) { ... }
xml_dfs <- lapply(list_of_xml_files, xml_to_df)
master_df <- do.call(rbind.data.frame, xml_dfs)
推荐阅读
- swift - NSTextView/NSScrollView 上的圆角?
- sql-server - Kerberos 与 SQL 服务器的连接是否需要在连接属性中提供密码?
- python - 如何在 Python 单元测试中创建 HTML 报告?
- ansible - 在ansible中特定搜索特定文本之后插入文本
- virtual-machine - 如何在 GDB 中上传和编译我的 .C 代码?
- c# - 如何将 NodaTime 与 BinaryReader 和 BinaryWriter(.NET、C#)一起使用?
- kubernetes - 部署相同的镜像两个不同的命名空间相同的端口
- typescript - TypeScript - 使用排除复制对象属性
- for-loop - 为什么在编译稳定代码时会收到关于缺少不稳定特征(std::iter::Step - 编译器不允许我实现)的错误?
- c# - 无法向 Web API 发出 Post 请求