2015-11-10 4 views
1

Я хотел бы добавить атрибут в самый верхний узел XML-файла, а затем сохранить файл. Я пробовал каждую комбинацию xpath и подмножества, о которых я могу думать, но просто не могу заставить ее работать. Чтобы использовать простой пример:Как изменить верхний узел XML в R?

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», который является дочерним узлом для «авторского» узла который содержит все данные, поэтому, когда я использую SAS для компиляции объединенного XML-документа в наборы данных, между идентификатором и данными отсутствует связь. Я надеюсь, что добавление идентификатора к вершине иерархии приведет к его распространению вплоть до всех остальных уровней).

+0

Это легко можно сделать с помощью [XSLT] (http://www.w3schools.com/xsl/) языка, который реструктурирует XML-документы для любых нужных потребностей. И если по [SAS] (https://www.sas.com/en_us/home.html) вы имеете в виду статистический пакет, то мы можем использовать его [proc xsl] (http://support.sas.com/ документация/CDL/ен/Proc/61895/HTML/по умолчанию/viewer.htm # a003356144.htm). Просьба пометить это SAS и предоставить фактический образец документа XML и желаемого результата набора данных. – Parfait

+0

[Здесь] (https://dl.dropboxusercontent.com/u/8428744/example_file.xml) - пример файла. У меня более 11 000 таких файлов, и я использовал программу mergex.exe для объединения их в один большой XML-файл. Затем я использовал XML-транслятор SAS для импорта XML-файла в SAS. Очень удобно, но структура XML-файла делает невозможным привязать идентификатор к информации об авторе. В идеале я бы хотел, чтобы каждый набор данных, созданный в SAS, содержал идентификатор автора (который я извлекаю из файла XML с помощью 'as.numeric (sub (" AUTHOR_ID: "," ", xmlValue (xml [" // dc: Идентификатор "] [[1]])))' –

ответ

1

Edit: Попробовав подход к редактированию верхнего узла (см Старый ответ ниже), я понял, что редактирование верхнего узла не решает мою проблему, потому что 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, это был очень мощный подход!

2

Чтобы перенести данные XML в двумерные строки и столбцы в соответствии со структурой наборов данных и dataframes, все гнезда должны быть удалены только для повторения родительского и одного дочернего уровня. Поэтому 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 OUTPUT(который импортируется как набор данных 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&amp;authorId=1234567&amp;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]]) 
+0

Какое колдовство ...! Это замечательный ответ, спасибо за то, что вы настолько основательны. На самом деле, цель состоит не в том, чтобы иметь один большой набор данных с все данные в одном, но для того чтобы иметь реляционные наборы данных с каждым набором информации отдельно, но уникальный идентификатор в каждом. Я прочитаю это внимательно и узнаю все, что могу, и если у вас нет других мыслей, я помету это как ответ в ближайшее время. –

+1

См. обновление. XSLT может использовать [переменные] (http://www.w3schools.com/xsl/el_variable.asp), которые могут быть переданы в другие части документа, даже проанализируйте 'Author:' с помощью функции [substring-after] (http://zvon.org/xxl/XSLTreference/OutputOverview/function_substring-after_frame.html). Таким образом, 'authorid' может быть передан в другие связанные узлы. На самом деле, я только что узнал, что SAS может импортировать несколько таблиц из одного XML! Конечно, этот пример будет добавлен в мою библиотеку.Что касается R, просто используйте 'xmltodataframe' для наборов узлов и' xmlSApply() 'для authorids. Спасибо за вопрос! – Parfait

Смежные вопросы