Как изменить верхний узел XML в R?
Я хотел бы добавить атрибут в самый верхний узел XML-файла, а затем сохранить файл. Я пробовал каждую комбинацию xpath и подмножеств, которую только мог придумать, но просто не могу заставить ее работать. Чтобы использовать простой пример:
xml_string = c(
'<?xml version="1.0" encoding="UTF-8"?>',
'<retrieval-response status = "found">',
'<coredata>',
'<id type = "author" >12345</id>',
'</coredata>',
'<author>',
'<first>John</first>',
'<last>Doe</last>',
'</author>',
'</retrieval-response>')
# parse xml content
xml = xmlParse(xml_string)
Когда я пытаюсь
xmlAttrs(xml["/retrieval-response"][[1]]) <- c(id = 12345)
Я получаю ошибку:
object of type 'externalptr' is not subsettable
Однако атрибут вставлен, поэтому я не уверен, что делаю не так.
(дополнительная справка: это упрощенная версия данных из API Scopus. Я объединяю тысячи XML-файлов, структурированных аналогично, но идентификатор находится в узле "coredata", который является родственным узлу "author", который содержит все данные, поэтому, когда я использую SAS для компиляции комбинированного XML-документа в наборы данных, между идентификатором и данными нет никакой связи. Я надеюсь, что добавление идентификатора в верхнюю часть иерархии приведет к его распространению вниз по всем другие уровни).
2 ответа
Редактировать: попробовав подход редактирования верхнего узла (см. Старый ответ ниже), я понял, что редактирование верхнего узла не решает мою проблему, потому что преобразователь XML SAS не сохранил все идентификаторы.
Я попробовал новый подход добавления идентификатора автора к каждому из подузлов, который работал отлично. Я также узнал, что вы можете использовать XPath для выбора нескольких узлов, помещая их в вектор, например так:
c("//coredata",
"//affiliation-current",
"affiliation-history",
"subject-areas",
"//author-profile")
Итак, последняя программа, которую я использовал, была:
files <- list.files()
for (i in 1:length(files)) {
author_record <- xmlParse(files[i])
xpathApply(
author_record, c(
"//coredata",
"//affiliation-current",
"affiliation-history",
"subject-areas",
"//author-profile"
),
addAttributes,
auth_id = gsub("AUTHOR_ID:", "", xmlValue(author_record[["//dc:identifier"]]))
)
saveXML(author_record, file = files[i])
}
Старый ответ: После долгих экспериментов я нашел довольно простое решение моей проблемы.
Атрибуты могут быть добавлены в верхний узел, просто используя
addAttributes(xmlRoot(xmlfile), attribute = "attributeValue")
Для моего конкретного случая наиболее простым решением будет простой цикл:
setwd("C:/directory/with/individual/xmlfiles")
files <- list.files()
for (i in 1:length(files)) {
author_record <- xmlParse(files[i])
addAttributes(node = xmlRoot(author_record),
id = gsub (pattern = "AUTHOR_ID:",
replacement = "",
x = xmlValue(auth[["//dc:identifier"]])
)
)
saveXML(author_record, file = files[i])
}
Я уверен, что есть лучшие способы. Очевидно, мне нужно изучить XLST, это был очень мощный подход!
Чтобы перенести данные XML в двумерные строки и столбцы в соответствии со структурой наборов данных и фреймов данных, все гнезда должны быть удалены только для итерации родительского и одного дочернего уровня. Поэтому XSLT, специальный декларативный язык программирования, который реструктурирует XML-документы в соответствии с любыми нюансами, пригодится для реструктуризации XML-данных для нужд конечного использования.
Учитывая ваш пример XML, ниже приведен XSLT, который можно запустить и полученный XML успешно импортировать в SAS. Зациклите код SAS для реструктуризации всех тысяч файлов XML.
XSLT (сохранить в формате.xsl или.xslt)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:ait="http://www.elsevier.com/xml/ani/ait"
xmlns:ce="http://www.elsevier.com/xml/ani/common"
xmlns:cto="http://www.elsevier.com/xml/cto/dtd"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:ns1="http://webservices.elsevier.com/schemas/search/fast/types/v4"
xmlns:prism="http://prismstandard.org/namespaces/basic/2.0/"
xmlns:xocs="http://www.elsevier.com/xml/xocs/dtd"
xmlns:xoe="http://www.elsevier.com/xml/xoe/dtd"
exclude-result-prefixes="ait ce cto dc ns1 prism xocs xoe">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="author-retrieval-response">
<xsl:variable select="substring-after(coredata/dc:identifier, ':')" name="authorid"/>
<root>
<coredata>
<authorid><xsl:value-of select="$authorid"/></authorid>
<xsl:for-each select="coredata/*">
<xsl:element name="{local-name()}">
<xsl:value-of select="concat(.,@href)"/>
</xsl:element>
</xsl:for-each>
</coredata>
<subjectAreas>
<authorid><xsl:value-of select="$authorid"/></authorid>
<xsl:for-each select="subject-areas/*">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</subjectAreas>
<authorname>
<authorid><xsl:value-of select="$authorid"/></authorid>
<xsl:for-each select="author-profile/preferred-name/*">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</authorname>
<classifications>
<authorid><xsl:value-of select="$authorid"/></authorid>
<xsl:for-each select="author-profile/classificationgroup/classifications/*">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</classifications>
<journals>
<authorid><xsl:value-of select="$authorid"/></authorid>
<xsl:for-each select="author-profile/journal-history/journal/*">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</journals>
<ipdoc>
<authorid><xsl:value-of select="$authorid"/></authorid>
<xsl:for-each select="author-profile/affiliation-current/affiliation/ip-doc/*[not(local-name()='address')]">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</ipdoc>
<address>
<authorid><xsl:value-of select="$authorid"/></authorid>
<xsl:for-each select="author-profile/affiliation-current/affiliation/ip-doc/address/*">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</address>
</root>
</xsl:template>
</xsl:transform>
SAS (используя скрипт выше)
proc xsl
in="C:\Path\To\Original.xml"
out="C:\Path\To\Output.xml"
xsl="C:\Path\To\XSLT.xsl";
run;
** STORING XML CONTENT;
libname temp xml 'C:\Path\To\Output.xml';
** APPEND CONTENT TO SAS DATASETS;
data Work.Coredata;
retain authorid;
set temp.Coredata; ** NAME OF PARENT NODE IN XML;
run;
data Work.SubjectAreas;
retain authorid;
set temp.SubjectAreas; ** NAME OF PARENT NODE IN XML;
run;
data Work.Authorname;
retain authorid;
set temp.Authorname; ** NAME OF PARENT NODE IN XML;
run;
data Work.Classifications;
retain authorid;
set temp.Classifications; ** NAME OF PARENT NODE IN XML;
run;
data Work.Journals;
retain authorid;
set temp.Journals; ** NAME OF PARENT NODE IN XML;
run;
data Work.Ipdoc;
retain authorid;
set temp.Ipdoc; ** NAME OF PARENT NODE IN XML;
run;
XML-выход (который импортируется как набор данных Authorsdata из одной строки и 40 переменных)
<?xml version="1.0" encoding="UTF-8"?>
<root>
<coredata>
<authorid>1234567</authorid>
<url>http://api.elsevier.com/content/author/author_id/1234567</url>
<identifier>AUTHOR_ID:1234567</identifier>
<eid>9-s2.0-1234567</eid>
<document-count>3</document-count>
<cited-by-count>95</cited-by-count>
<citation-count>97</citation-count>
<link>http://api.elsevier.com/content/search/scopus?query=refauid%1234567%29</link>
<link>http://www.scopus.com/authid/detail.url?partnerID=HzOxMe3b&authorId=1234567&origin=inward</link>
<link>http://api.elsevier.com/content/author/author_id/1234567</link>
<link>http://api.elsevier.com/content/search/scopus?query=au-id%281234567%29</link>
</coredata>
<subjectAreas>
<authorid>1234567</authorid>
<subject-area>Human-Computer Interaction</subject-area>
<subject-area>Control and Systems Engineering</subject-area>
<subject-area>Software</subject-area>
<subject-area>Computer Vision and Pattern Recognition</subject-area>
<subject-area>Artificial Intelligence</subject-area>
</subjectAreas>
<authorname>
<authorid>1234567</authorid>
<initials>A.</initials>
<indexed-name>John A.</indexed-name>
<surname>John</surname>
<given-name>Doe</given-name>
</authorname>
<classifications>
<authorid>1234567</authorid>
<classification>1709</classification>
<classification>2207</classification>
<classification>1712</classification>
<classification>1707</classification>
<classification>1702</classification>
</classifications>
<journals>
<authorid>1234567</authorid>
<sourcetitle>Very Prestigious Journal</sourcetitle>
<sourcetitle-abbrev>V PRES JOU Autom</sourcetitle-abbrev>
<issn>10504729</issn>
<sourcetitle>2005 Another Prestigious Journal</sourcetitle>
<sourcetitle-abbrev>An. Prest. Jou. </sourcetitle-abbrev>
</journals>
<ipdoc>
<authorid>1234567</authorid>
<afnameid>Prestigious University#1111111</afnameid>
<afdispname>Prestigious University University</afdispname>
<preferred-name>Prestigious University University</preferred-name>
<sort-name>Prestigious University</sort-name>
<org-domain>pu.edu</org-domain>
<org-URL>http://www.pu.edu/index.shtml</org-URL>
</ipdoc>
<address>
<authorid>1234567</authorid>
<address-part>1234 Prestigious Lane</address-part>
<city>City</city>
<state>ST</state>
<postal-code>12345</postal-code>
<country>United States</country>
</address>
</root>
R АЛЬТЕРНАТИВА
Поскольку всеобъемлющей библиотеки R XSLT не существует, синтаксический анализ придется выполнять непосредственно на языке R. Однако R может вызывать процессоры XSLT других исполняемых файлов (например, Python, Saxon, VBA) через командную строку, пакет RCOMClient и другие интерфейсы.
Тем не менее, R может извлекать данные XML с помощью xmlToDataFrame()
а также xpathSApply()
(последний похож на XPath) для authorid
:
library(XML)
coredata <- xmlToDataFrame(nodes = getNodeSet(doc, '//coredata'))
coredata$authorid <- gsub(pattern = "AUTHOR_ID:", replacement = "",
xpathSApply(doc, '//coredata/dc:identifier', xmlValue)[[1]])
subjectareas <- xmlToDataFrame(nodes = getNodeSet(doc, "//subject-areas"))
subjectareas$authorid <- gsub(pattern = "AUTHOR_ID:", replacement = "",
xpathSApply(doc, '//coredata/dc:identifier', xmlValue)[[1]])
authorname <- xmlToDataFrame(nodes = getNodeSet(doc, '//author-profile/preferred-name'))
authorname$authorid <- gsub(pattern = "AUTHOR_ID:", replacement = "",
xpathSApply(doc, '//coredata/dc:identifier', xmlValue)[[1]])
classifications <- xmlToDataFrame(nodes = getNodeSet(doc, '//author-profile/classificationgroup/classifications'))
classifications$authorid <- gsub(pattern = "AUTHOR_ID:", replacement = "",
xpathSApply(doc, '//coredata/dc:identifier', xmlValue)[[1]])
journal <- xmlToDataFrame(nodes = getNodeSet(doc, '//author-profile/journal-history/journal'))
journal$authorid <- gsub(pattern = "AUTHOR_ID:", replacement = "",
xpathSApply(doc, '//coredata/dc:identifier', xmlValue)[[1]])
ipdoc <- xmlToDataFrame(nodes = getNodeSet(doc, '//author-profile/affiliation-current/affiliation/ip-doc'))
ipdoc$authorid <- gsub(pattern = "AUTHOR_ID:", replacement = "",
xpathSApply(doc, '//coredata/dc:identifier', xmlValue)[[1]])
address <- xmlToDataFrame(nodes = getNodeSet(doc, '//author-profile/affiliation-current/affiliation/ip-doc/address'))
address$authorid <- gsub(pattern = "AUTHOR_ID:", replacement = "",
xpathSApply(doc, '//coredata/dc:identifier', xmlValue)[[1]])