xml2: xml_text() возвращает разные значения в зависимости от того, запускается ли он вручную
В настоящее время я работаю над проектом, в котором я применяю анализ текста к большому количеству XML-файлов. Я использую пакет xml2 для обработки xml и пакет stringr для выполнения большинства задач по анализу текста.
Я сталкиваюсь со странной проблемой. Некоторые из документов xml содержат странные пробелы, которые испортят функции, поэтому мне нужно сначала заменить эти пробелы обычными пробелами. Для этого я разделяю все узлы из xml-документа, извлекаю текст, изменяю его, а затем присваиваю измененные строки узлам, как здесь: xml_text(node) <- str_replace_all(xml_text(node), "[:space:]", " ")
, который успешно меняет пространство символов. Это упрощенная версия моего кода:
# required libraries
require(xml2)
require(stringr)
testfunctie <- function(xmlstring)
{
# turn string into xml nodeset
data<-read_xml(xmlstring)
# take every node separately
data<-xml_find_all(data, "//*")
browser()
# replace weird space characters by regular ones
for (i in 1:length(data))
{
xml_text(data[[i]]) <- str_replace_all(xml_text(data[[i]]), "[:space:]", " ")
}
# find all nodes containing a certain text
tree <- xml_find_all(data, "//dossiernr[text()='ExampleText']")
browser()
return(tree)
}
# XML example
exampleXML<-"<dossier>
<dossiernummer>
<dossiernr>ExampleText</dossiernr>
</dossiernummer>
<titel>AnotherPieceOfExampleText</titel>
</dossier>"
mvt <- testfunctie(exampleXML)
Обычно это работает как надо, но в некоторых случаях возникает странная проблема, и текст, извлеченный с помощью xml_text(), также содержит текст из других узлов. Вы можете увидеть это, запустив код выше. Когда срабатывает первый оператор browser(), выберите оставшийся код до второго оператора browser() (строки 13-18) и запустите его вручную. Объект "дерево" будет тогда списком длиной 1, потому что функция xml_find_all() нашла узел с именем "dossiernr" (третий узел в наборе "data"), потому что текст внутри этого узла - "ExampleText", Вы можете проверить это, набрав xml_text(data[[3]])
в консоли, которая равна xml_text(tree[[1]])
, Вот как это должно работать.
Однако, если вы вместо этого нажмете "продолжить" после первого оператора браузера и автоматически выполните оставшийся код, когда вы прибудете ко второму оператору браузера, "дерево" будет списком длины 0. Если вы затем наберете xml_text(data[[3]])
получается, что текст внутри узла был заменен на "ExampleTextAnotherPieceOfExampleText" вместо просто "ExampleText". Текст внутри другого узла (узел с именем "titel") добавляется к нему.
По какой-то причине этот код ведет себя по-разному в зависимости от того, запускаю ли я его вручную или он запускается автоматически. Может кто-нибудь помочь мне понять, почему это происходит, и как я могу решить это поведение? Заранее спасибо.
1 ответ
Функция xml_text возвращает весь текст в родительском и всех конечных узлах, поэтому некоторые узлы являются объединенными значениями. Это кажется противоречивым поведением.
Мое предложение, в отличие от попытки оперировать на каждом узле и заменить текст, выполнить глобальную замену всего XML-документа с помощью str_replace_all, а затем перечитать данные обратно как XML.
# XML example
exampleXML<-"<dossier>
<dossiernummer>
<dossiernr>ExampleText</dossiernr>
</dossiernummer>
<titel>AnotherPieceOfExampleText</titel>
</dossier>"
data<-read_xml(exampleXML)
data<-str_replace_all(data, "[:space:]", " ")
data<-read_xml(data)