Ruby - Nokogiri - Разбор XML из памяти и помещение всех одинаковых имен узлов в массив - PullRequest
0 голосов
/ 22 декабря 2010

У меня есть XML, который я пытаюсь проанализировать из памяти и получить статус каждого из моих тестов на биение сердца, используя Nokogiri. Вот решение, которое у меня есть ...

xml = 
<a:HBeat>
  <a:ElapsedTime>3 ms</a:ElapsedTime>
  <a:Name>Service 1</a:Name>
  <a:Status>true</a:Status>
</a:HBeat>
<a:HBeat>
  <a:ElapsedTime>4 ms</a:ElapsedTime>
  <a:Name>Service 2</a:Name>
  <a:Status>true</a:Status>
  </a:HBeat>
<a:HBeat>

Я пытался использовать css и xpath, чтобы получить значение для каждого состояния и поместить его в массив. Код ниже:

doc = Nokogiri::XML.parse(xml)
#service_state = doc.css("a:HBeat, a:Status", 'a' => 'http://schemas.datacontract.org/2004/07/OpenAPI.Entity').map {|node| node.children.text}
service_state = doc.xpath("//*[@a:Status]", 'a' => 'http://schemas.datacontract.org/2004/07/OpenAPI.Entity').map(&:text)

Оба вернут service_state = []. Есть мысли или рекомендации?

Кроме того, учтите, что у меня есть почти идентичный xml для другого теста, и я использовал следующий фрагмент кода, который выполняет именно то, что я хотел, но по какой-то причине не работает с xml, содержащим пространства имен.

service_state = doc.css("HBeat Status").map(&:text)

Спасибо!

Ответы [ 2 ]

2 голосов
/ 22 декабря 2010

В дополнение к ответу Грега (о том, что XML нужен содержащий элемент), ваше выражение XPath выбирает неправильную вещь:

 //*[@a:Status]

выбирает все элементы, которые имеют: Status атрибуты . Если вы хотите, чтобы все элементы имели дочерний элемент a: Status, просто удалите @ из теста узла:

 //*[a:Status]
2 голосов
/ 22 декабря 2010

Отчасти проблема в том, что ваш пример XML неверен: вы пропускаете объявление пространства имен, хотя вы используете пространства имен, и вам не хватает содержащего тега. Первое можно обойти, но второе нуждается в настройке XML.

require 'nokogiri'
require 'pp'

xml = <<EOT
<xml xmlns:a="http://schemas.datacontract.org/2004/07/OpenAPI.Entity"> # <-- changed
  <a:HBeat>
    <a:ElapsedTime>3 ms</a:ElapsedTime>
    <a:Name>Service 1</a:Name>
    <a:Status>true</a:Status>
  </a:HBeat>
  <a:HBeat>
    <a:ElapsedTime>4 ms</a:ElapsedTime>
    <a:Name>Service 2</a:Name>
    <a:Status>true</a:Status>
    </a:HBeat>
  <a:HBeat>
</xml>
EOT

doc = Nokogiri::XML(xml)
service_state = doc.css('a|Status').map(&:text)      # <-- changed to show CSS with namespace
pp service_state

service_state = doc.search('//a:Status').map(&:text) # <-- added
pp service_state                                     # <-- added

>> ruby test.rb
>> ["true", "true"]
>> ["true", "true"]                                  # <-- added

Пространства имен - это хорошо, но иметь дело с ними может быть больно, когда все, что вы хотите сделать, это получить данные. У Nokogiri есть некоторые приемы, которые делают их менее раздражающими, например, использование CSS-аксессоров, как я делал выше, что означает «найти тег Status во всех пространствах имен», поэтому даже если пространства имен не объявлены, все равно будет хорошо. *

Если вы контролируете XML, вы можете покончить с пространствами имен. Они хороши, когда имеют дело с возможными коллизиями тегов, но это не слишком вероятно, если вы владеете механизмом, генерирующим файл, поэтому, если это так, вы, вероятно, можете покончить с ними. Если вам нужно пространство имен, то оно должно быть объявлено примерно так:

<xml xmlns:a="http://schemas.datacontract.org/2004/07/OpenAPI.Entity">

Без этого XML анализирует с большим количеством ошибок пространства имен:

(rdb:1) pp doc.errors
[#<Nokogiri::XML::SyntaxError: Namespace prefix a on HBeat is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on ElapsedTime is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on Name is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on Status is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on HBeat is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on ElapsedTime is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on Name is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on Status is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on HBeat is not defined>,
#<Nokogiri::XML::SyntaxError: Opening and ending tag mismatch: HBeat line 12 and xml>,
#<Nokogiri::XML::SyntaxError: Premature end of data in tag xml line 1>]

Но после добавления список ошибок в документе становится намного меньше:

(rdb:1) pp doc.errors
[#<Nokogiri::XML::SyntaxError: Opening and ending tag mismatch: HBeat line 12 and xml>,
#<Nokogiri::XML::SyntaxError: Premature end of data in tag xml line 1>]

См. Также " Как избежать объединения всего текста с узлов при извлечении ".

...