xml2: захват текста в массиве после указанного текста в той же строке

У меня есть XML с кучей envelope элементы. Внутри каждого есть массив. Каждая строка в массиве имеет 2 элемента. Первый - это идентификатор, а второй - текст, который я хочу взять. Мне нужно первое значение строки, чтобы определить правильную строку, чтобы я мог получить правильное значение.

В приведенном ниже примере "еда" в строках обозначена кодом 610954, Я хочу захватить 2 элемента после этого кода (c('pizza', 'burger'), Также есть "напитки", обозначенные кодом 605380, Я хочу схватить c('coke', 'pepsi'), Как я могу использовать пакет xml2 для этого?

library(xml2)
library(magrittr)

myxml <- read_xml('
<inside>
 <envelope>
  <card-entries type="array">
     <card-entry>
       <card-id type="integer">605380</card-id>
       <value>coke</value>
     </card-entry>
     <card-entry>
       <card-id type="integer">610954</card-id>
       <value>pizza</value>
     </card-entry>  
   </card-entries>
 </envelope>
 <envelope>
  <card-entries type="array">
     <card-entry>
       <card-id type="integer">605380</card-id>
       <value>pepsi</value>
     </card-entry>
     <card-entry>
       <card-id type="integer">610954</card-id>
       <value>burger</value>
     </card-entry>  
   </card-entries>
 </envelope>
</inside>
'
)

## as far as I can parse it (but not specific enough)
myxml %>%
    xml_find_all('//envelope/card-entries[@type="array"]/card-entry') %>%
    xml_text()

food <- -CODE THAT GIVES HERE c('pizza', 'burger')- # 610954
drinks <- -CODE THAT GIVES HERE c('coke', 'pepsi')- # 605380

2 ответа

Решение

Ваш оригинальный подход может быть изменен так, чтобы получить напитки:

myxml %>%
  xml_find_all('//envelope/card-entries[@type="array"]/card-entry[card-id = "605380"]/value') %>%
  xml_text()
#[1] "coke"  "pepsi"

Но вы могли бы пойти с множеством других подходов

# get following sibling called value
myxml %>% 
  # foods
  xml_find_all('//card-id[text()="610954"]/following-sibling::value') %>%
  xml_text()
#[1] "pizza"  "burger"

# get following::value[1] - Specify [1] or you would get all following values, 
# including "pepsi".  With value[1] you get only the following value.
myxml %>% 
  # foods
  xml_find_all('//card-id[text()="610954"]/following::value[1]') %>%
  xml_text()
#[1] "pizza"  "burger"

# look for value nodes with a preceding sibling with the appropriate card-id
myxml %>% 
  # drinks
  xml_find_all('//value[preceding-sibling::card-id[text()="605380"]]') %>%
  xml_text()
#[1] "coke"  "pepsi"

# Get value node that is a child of card-entry nodes with the appropriate card-id.
# specifically looking in envelope elements
myxml %>% 
  # drinks
  xml_find_all('//envelope/card-entries/card-entry[card-id = "605380"]/value') %>%
  xml_text()
#[1] "coke"  "pepsi"

# less specific
myxml %>% 
  xml_find_all('//card-entry[card-id = "605380"]/value') %>%
  xml_text()
#[1] "coke"  "pepsi"

Как насчет чего-то вроде:

library(tidyverse)
library(stringr)
myxml %>%
  xml_find_all('//envelope/card-entries[@type="array"]/card-entry') %>%
  xml_text() %>% 
  map(.f = str_sub, start = c(1, 7), end = c(6, 1000000L)) %>% 
  reduce(rbind) %>% 
  as_tibble() %>% 
  mutate(type = ifelse(V1 == 605380, yes = "drinks", no = "food"))

И тогда вы можете легко подгруппировать напитки и еду отдельно.

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