xml - захватить sububchild и свернуть внутри дочернего элемента, даже если текст элемента отсутствует

У меня есть вложенный набор данных xml, подобный приведенному ниже, который я пытаюсь проанализировать с пакетами w / xml2 и tidyverse. Есть три детских конверта. Я хочу взять весь текст для <card-id> а также <value> дочерние теги subub в каждом <envelope> пометить и свернуть их с легко идентифицируемым разделителем, как ;;; или составьте список data.frames из них.

МВЭ: Вот данные:

library(xml2)
library(tidyverse)

myxml <- read_xml('
<inside>
 <envelope>
     <card-entry>
       <card-id type="integer">605380</card-id>
       <value>coke</value>
       <random></random>
     </card-entry>
     <card-entry>
       <card-id type="integer">610954</card-id>
       <value>pizza</value>
       <random>false</random>
     </card-entry>  
     <card-entry>
       <card-id type="integer">605381</card-id>
       <value>surprise</value>
     </card-entry>
     <card-entry>
       <card-id type="integer">610958</card-id>
       <value>joke</value>
       <random>true</random>
     </card-entry> 
 </envelope>
 <envelope>
     <card-entry>
       <card-id type="integer">605381</card-id>
       <value>charlie horse</value>
     </card-entry>
     <card-entry>
       <card-id type="integer">605380</card-id>
       <value>rug bug</value>
     </card-entry>
     <card-entry>
       <card-id type="integer">610954</card-id>
       <value>mario cart</value>
     </card-entry>  
 </envelope>
 <envelope>
     <card-entry>
       <card-id type="integer">605377</card-id>
       <value>trogdor</value>
     </card-entry>
     <card-entry>
       <card-id type="integer"></card-id>
       <value>jorb</value>
     </card-entry>
     <card-entry>
       <card-id type="integer">605333</card-id>
       <value></value>
     </card-entry>    
 </envelope>
</inside>
'
)

Желаемый результат:

c(
    "605380;;;coke;;;610954;;;pizza;;;605381;;;surprise;;;610958;;;joke",
    "605381;;;charlie horse;;;605380;;;rug bug;;;610954;;;mario cart",
    "605377;;;trogdor;;;;;;jorb;;;605333;;;"
)

Или так же хорошо (может быть, лучше) вложенный список, как это:

[[1]]
  card_id    value
1  605380     coke
2  610954    pizza
3  605381 surprise
4  610958     joke

[[2]]
  card_id         value
1  605381 charlie horse
2  605380       rug bug
3  610954    mario cart

[[3]]
  card_id   value
1  605377 trogdor
2    <NA>    jorb
3  605333    <NA>

Я думал, что мог бы использовать as_list на ребенка, а затем использовать xml_find_all создать список data.frames но as_list + lapply не атакует только один envelope но делает их все с каждым проходом (было бы неплохо узнать, что мне не хватает в этой функции тоже).

Что я пробовал

myxml %>%
    xml_find_all('//envelope') %>%
    as_list() %>%
    lapply(function(x){
        data_frame(
            card_id = x %>% xml_find_all('//card-id') %>% xml_text(),
            value = x %>% xml_find_all('//value') %>% xml_text()
        )
    })

1 ответ

Решение

Не совсем красиво, но вы можете получить список data.frames, сначала собрав все дочерние элементы каждого конверта в отдельные элементы списка, а затем пройдя по циклу, чтобы получить текст от каждого идентификатора карты и узла значения.

myxml %>%
    xml_find_all('//envelope') %>%
    lapply(xml_children) %>%
    lapply(function(x) data.frame(
        card_id = xml_child(x, 'card-id') %>% xml_text,
        value = xml_child(x, 'value') %>% xml_text
        )
    )

#[[1]]
#  card_id    value
#1  605380     coke
#2  610954    pizza
#3  605381 surprise
#4  610958     joke
#
#[[2]]
#  card_id         value
#1  605381 charlie horse
#2  605380       rug bug
#3  610954    mario cart
#
#[[3]]
#  card_id   value
#1  605377 trogdor
#2            jorb
#3  605333  

Для НС вместо "" можно добавить %>% ifelse(. == "", NA, .) после каждого xml_text

Другие вопросы по тегам