2015-10-05 5 views
0

Основная проблема на самом деле довольно проста: я не могу заставить Nokogiri DocumentFragment вести себя так, как ожидалось. Он имеет два узла, а не тот, который он имел бы, если бы это был фактический документ, и он не распознает этот узел как элемент, а документ.Проблемы с вставкой элементов в фрагмент XML

Мне нужен фрагмент вместо документа, потому что я хочу вставить полученный XML в другой документ (или фрагмент) в качестве элемента. Похоже, я использовал метод неправильного фрагмента.

Я пытаюсь создать представление XML для объектов в нашем приложении Ruby on Rails в методе build_xml. Поскольку у меня есть иерархия вложенных объектов, я делаю ее универсальным методом, который будет использоваться между классами, и используйте константу класса в каждом классе для обработки информации, относящейся к классу. Каждый объект создает Nokogiri DocumentFragment вместо полного документа, так что любая возвращаемая строка XML может быть вставлена ​​в XML-объект содержащего объекта в качестве элемента.

Моя проблема в том, что я не могу получить фрагмент, чтобы показать мне его элементы. Итак, у меня есть:

xml_string = self.to_xml({skip_types:true, skip_instruct: true}) # Use default to_xml method to get started 
xml_fragment = Nokogiri::XML::DocumentFragment.parse(xml_string) # Create Nokogiri doc fragment 

И в этот момент, я хочу, чтобы цикл по каждому вложенному объекту и добавить его в качестве вложенного элемента только элемента фрагмента. Тем не менее, метод element_children() этого фрагмента возвращает пустой массив, в то время как метод children() возвращает массив из двух элементов, первым из которых является элемент, который я хочу, а вторым из них является некоторый текстовый объект, содержащий только строку ,

Пример:

df = Datafile.first 
xml_string = df.to_xml({skip_types:true, skip_instruct: true}) 
frag = Nokogiri::XML::DocumentFragment.parse(xml_string) 
frag.element_children # => returns [] 
frag.children # => returns array of two children, one of which is datafile element, the other of which is just a linefeed. 

Если я создаю фактический XML-документ, а не только фрагмент, то документ имеет element_children заселенные, как и ожидалось, и, кроме того, doc.children имеет только один элемент, без во-вторых, избыточный узел. Я мог бы попытаться выполнить мою работу с документом, а затем просто преобразовать его в фрагмент, прежде чем возвращать его, но я не знаю, будет ли возникающий фрагмент по-прежнему иметь проблемы, и я бы предпочел понять, что происходит, поэтому я могу просто сделайте это правильно, вместо этого.

Итак ...

  1. Почему не фрагмент признают его единственный реальный узел как элемент? Есть ли что-то, что я должен сделать, чтобы заставить это? Документация Nokogiri не описывает много о узлах элементов, но похоже, что у них нет доступных свойств, чтобы отличать их от общих узлов.
  2. Почему этот второй, пустой узел появляется, когда я разбираю фрагмент?
  3. Должен ли я просто пройти через полные документы? Есть ли простой способ сделать документ для фрагмента?
  4. Должен ли я делать это совсем по-другому?
+1

Добро пожаловать в переполнение стека. Трудно представить, что вы описываете. Stack Overflow ожидает минимальный пример вашего входного XML, а также код, демонстрирующий проблему, и пример вашего желаемого результата. См. Раздел «Помогите другим воспроизвести проблему» в разделе «[ask]». –

+0

Не нужно извиняться за недостающую информацию, но мы не можем подчеркнуть, насколько важно предоставить нам необходимую информацию. Без этого мы угадываем и стреляем в темноте, что только задерживает получение рабочего решения для вас и расстраивает нас. Кроме того, при добавлении дополнительной информации, не помещайте ее в начале или в конце вопроса и пометьте ее «Изменить» или «Обновить». Мы можем видеть изменения/обновления, и добавленная информация должна быть включена в вопрос, где вы бы добавили ее изначально, поэтому она имеет смысл и читается правильно. –

+0

Кроме того, помните, что минимальные примеры кода и данных LOT более полезны, чем страницы объяснения. Объяснение помогает определить намерение, но код показывает нам путь, который вы берете, чтобы добраться туда. Как рисунок, примеры стоят 1000 слов. –

ответ

0

Ну, решение было просто обновить версию Nokogiri. Предположительно, это была ошибка, которая была установлена ​​между версиями 1.6.3.1 и 1.6.6.2.

1

Вы передаете всю строку XML до parse, она просто принимает the tags as an argument.

Согласно their docs, вы должны делать что-то вроде этого:

xml_fragment = Nokogiri::XML.fragment(xml_string) 

Не уверен, если это на самом деле то, что вызывает проблему, но это может быть место, чтобы начать.

+0

Извините, если я тупой, но я не вижу определения «тегов» на этой странице. Является ли это массивом имен тегов, XML-строки с содержимым каждого тега пустым, определенного типа объектов тегов или чего-то еще? Надеюсь, это не звучит требовательно или воинственно, я просто пытаюсь заполнить пробелы в документации (или моих навыках чтения). – whognu

+0

Я попробовал 'frag = Nokogiri :: XML.fragment (xml_string)' вместо метода 'DocumentFragment.parse', и он возвращает объект фрагмента, но этот объект по-прежнему имеет два элемента вместо одного и все еще не выполняет признать значимый узел как элемент (т.е. 'frag.element_children' возвращает' [] '). – whognu

1

Хотя вопрос не ясно, возможно, этот маленький обзор вставки и удаления узлов поможет:

require 'nokogiri' 

inserted_text = 'hello world!' 

Это разбирает фрагмент:

doc = Nokogiri::XML::DocumentFragment.parse('<foo><bar></bar></foo>') 
doc.to_xml # => "<foo>\n <bar/>\n</foo>" 

сравнить его с полным разборе, который добавляет объявление XML:

doc = Nokogiri::XML('<foo><bar></bar></foo>') 
doc.to_xml # => "<?xml version=\"1.0\"?>\n<foo>\n <bar/>\n</foo>\n" 

Найти узел <bar> и добавить дочерний узел:

bar = doc.at('bar') 
bar.children = "<baz a='1'>#{ inserted_text }</baz>" 

doc.to_xml # => "<foo>\n <bar>\n <baz a=\"1\">hello world!</baz>\n </bar>\n</foo>" 

Я использую at метод, который находит первый узел согласования. Это более конкретно, чем search, который возвращает все соответствующие узлы как NodeSet, который сродни массиву узлов. Оба метода используют селектор CSS или XPath; CSS легче читать обычно, но XPath обладает гораздо большей мощностью, поэтому выбирайте между ними на основе простоты чтения, а затем мощности. Nokogiri совершенно счастлив использовать оба в том же сценарии. Существуют специальные эквиваленты CSS/XPath для at и search: at_css, at_xpath и css и xpath соответственно. at('some_selector') эквивалентен search('some_selector').first.

Также обратите внимание, что Nokogiri рада принять строку, содержащую XML или HTML, которые вы хотите добавить. Он будет анализировать его на фрагмент, позволяя вам более легко определить, что вы хотите использовать.

Это, как легко удалить узел:

baz = doc.at('baz').remove 

Чтобы изменить атрибут узла:

baz['a'] = 'hiya!' 

И, чтобы переместить узел где-нибудь еще:

doc.at('foo').add_child(baz) 

Это позволяет нам видеть узел как XML:

doc.to_xml # => "<foo>\n <bar/>\n <baz a=\"hiya!\">hello world!</baz>\n</foo>" 

Это позволяет нам видеть XML, как если бы мы смотрели на файл:

puts doc.to_xml 
# >> <foo> 
# >> <bar/> 
# >> <baz a="hiya!">hello world!</baz> 
# >> </foo> 
+0

Итак, вот суть проблемы. В вашем примере, как только вы создаете ** фрагмент **, называемый doc, он имеет элемент foo. Чтобы проверить это, вы можете выполнить 'doc.children.first.element?', И он возвращает 'true'. Однако 'doc.elements' возвращает' [] '. – whognu

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