微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

XML和XSLT-V2处理或R中的嵌套嵌套列表-药品福利计划中的数据 解决方案的一部分

如何解决XML和XSLT-V2处理或R中的嵌套嵌套列表-药品福利计划中的数据 解决方案的一部分

问题:

很长时间以来,我和我的大学一直在尝试将药品福利计划V3 XML(https://www.pbs.gov.au/browse/downloads)转换为可用格式,但是我们取得的成功有限。我已经能够将PBS XML V3转换为列表的R列表,但是以编程方式将其解压缩为可用格式非常困难。

PBS_V3_XML_Doc <- read_xml(x = Path_to_PBS_V3_XML_File,options = c("RECOVER","NOBLANKS","HUGE")   # Important options that enable import. 
                         # Lifts hardcoded limitations because of the massive file size,verbose = TRUE)    # Enables message print Feedback


PBS_V3_XML_NameSpaces <- xml_ns(PBS_V3_XML_Doc)           # xml namespaces 
PBS_V3_XML_List <- xml2::as_list(x = PBS_V3_XML_Doc)    # converts the XML document into a R list object

我尝试过组合unnest_wider,unnest_longer,unnest,unlist,unnest_auto以及更多(下面有一些)。但是我们还没有运气。

Test_docall_unnest <- do.call(c,unlist(PBS_V3_XML_List,recursive=FALSE))

flattenlist <- function(x){  
        morelists <- sapply(x,function(xprime) class(xprime)[1]=="list")
        out <- c(x[!morelists],unlist(x[morelists],recursive=FALSE))
        if(sum(morelists)){ 
                Recall(out)
                }else{
        return(out) }}

recursive_unnest <- function(.data) {
        # Exit condition: no more 'children' list-column
        if (!"children" %in% names(.data) || !is.list(.data[["children"]])) return(.data)
        x <- unnest(.data)
        # Minor clean-up to make unnest work: replace NULLs with empty data frames
        x <- mutate_if(x,is.list,~ map_if(.x,~ is.null(.x) || identical(.x,list()),~ data.frame(date = NA)))           Recall(x)  
}

解决方案的一部分

使用样式表可能有效,但是xslt软件包不接受版本2或更高版本的XML样式表,因此可悲的是,该软件包无法解决这种情况。 我试过使用PBS V3 Schema found here中的一些xsl样式表,但是老实说我不确定应该使用哪个样式表(我已经尝试过/ xsl文件夹中的文件

Errors shown on attempting to apply a xsl to the xml

R Termination

解决方法

为什么R的xslt软件包不能运行XSLT 1.0脚本? XSLT 1.0脚本可以展平所需的节点,并将其所有后代限制为具有文本信息的节点。重复的节点似乎是<previous>标签。扁平化后,转换后的XML可以轻松迁移到数据帧中。每个后代都有其父名称和祖父母名称附加到返回名称,以完成数据框列。

XSLT (另存为.xsl,一个特殊的.xml文件)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:pbs="http://schema.pbs.gov.au/"
                              xmlns:p="http://pbs.gov.au/"
                              xmlns:dbk="http://docbook.org/ns/docbook"
                              xmlns:db="http://docbook.org/ns/docbook#"
                              xmlns:dc="http://purl.org/dc/elements/1.1/"
                              xmlns:dct="http://purl.org/dc/terms/"
                              xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
                              xmlns:owl="http://www.w3.org/2002/07/owl#"
                              xmlns:skos="http://www.w3.org/2004/02/skos/core#"
                              xmlns:svg="http://www.w3.org/2000/svg"
                              xmlns:ext="http://extension.schema.pbs.gov.au/"
                              xmlns:xlink="http://www.w3.org/1999/xlink">
    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="pbs:root">
     <xsl:copy>
       <xsl:copy-of select="@*"/>
       <xsl:apply-templates select="pbs:previous-list"/>
     </xsl:copy>
    </xsl:template>
    
    <xsl:template match="pbs:previous-list">
       <xsl:apply-templates select="pbs:previous"/>
    </xsl:template>
    
    <xsl:template match="pbs:previous">
     <data>
       <xsl:for-each select="descendant::*">
           <xsl:if test="text() != ''">
           <xsl:element name="{translate(concat(local-name(../parent::*),'_',local-name(parent::*),local-name()),'-','_')}">
               <xsl:value-of select="text()"/>
           </xsl:element>
           </xsl:if>
       </xsl:for-each>
     </data>
    </xsl:template>
    
</xsl:stylesheet>

XSLT Demo

R

library(xml2)
library(xslt)
library(dplyr)

# INPUT SOURCE
doc <- read_xml("/path/to/sch-2020-09-01-r1.xml")
style <- read_xml("/path/to/style.xsl",package = "xslt")

# TRANSFORM 
new_xml <- xml_xslt(doc,style)

# RETRIEVE data NODES
recs <- xml_find_all(new_xml,"//data")

# BIND EACH CHILD TEXT AND NAME TO Player DFs
df_list <- lapply(recs,function(r) 
  data.frame(rbind(setNames(xml_text(xml_children(r)),xml_name(xml_children(r)))),stringsAsFactors = FALSE)
)

# BIND ALL DFs TO SINGLE MASTER DF
final_df <- dplyr::bind_rows(df_list)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。