Поиск XML и получение подмножества узлов в виде XML

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

Вот пример входного XML:

<root>
  <node name = "Amaths"> 
    <node name = "Bangles"/> 
  </node>
  <node name = "C">
    <node name = "Dangles">
      <node name = "E"> 
        <node name = "Fangles"/> 
      </node>
    </node>
    <node name = "Gdecimals" />
  </node>
  <node name = "Hnumbers"/> 
  <node name = "Iangles"/> 
</root>

Вывод, который я ищу по поисковому запросу «углы»:

<root>
  <node name = "Amaths"> 
    <node name = "Bangles"/> 
  </node>
  <node name = "C">
    <node name = "Dangles">
      <node name = "E"> 
        <node name = "Fangles"/> 
      </node>
    </node>
  </node>
  <node name = "Iangles"/> 
</root>

XPath, который я использую для поиска xml: "//*[contains(@name,'angles')]"

Я использую Nokogiri в Ruby для поиска XML, который предоставляет мне NodeSet всех узлов, соответствующих термину. Я не могу понять, как восстановить XML из этого набора узлов.

Спасибо!

РЕДАКТИРОВАТЬ: Исправлен пример, который должен был быть . Спасибо Димитре.

РЕДАКТИРОВАТЬ 2: снова исправил xml для корректности.


person Vijay Dev    schedule 17.07.2010    source источник
comment
Хороший вопрос (+1). Смотрите мой ответ для объяснения и простого решения XSLT.   -  person Dimitre Novatchev    schedule 18.07.2010


Ответы (1)


Во-первых, обратите внимание, что представленный желаемый вывод неверен, а следующий элемент не имеет закрывающего тега далее в документе:

<node name = "C">

Результатами оценки выражений XPath может быть набор узлов из XML-документа, но XPath не может изменить эти примечания.

Это выражение XPath выбирает

узлы, которые соответствуют термину вместе со своими родителями, полностью отслеживая до корневого узла

//*[contains(@name,'angles') and not(node())]/ancestor::*

Однако узлы не изменяются и содержат всех своих дочерних элементов, а это означает, что полное поддерево с корнем в Root по-прежнему является поддеревом Root в возвращаемом результате.

Если вы хотите получить новый документ (набор узлов) со структурой, отличной от исходной XML-документа, вы должны использовать другой язык, на котором размещен XPath. Существует множество таких языков, например XSLT, XQuery и любой язык с реализацией XML DOM.

Вот преобразование XSLT, дающее желаемый результат:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="*[not(descendant-or-self::*[contains(@name, 'angles')])]"/>
</xsl:stylesheet>

когда это преобразование применяется к предоставленному XML-документу (скорректирован на корректный формат):

<root>
  <node name = "Amaths">
    <node name = "Bangles"/>
  </node>
  <node name = "C">
    <node name = "Dangles">
      <node name = "E">
        <node name = "Fangles"/>
      </node>
      <node name = "Gdecimals" />
    </node>
  </node>
  <node name = "Hnumbers"/>
  <node name = "Iangles"/>
</root>

выдается желаемый (правильный) результат:

<root>
   <node name="Amaths">
      <node name="Bangles"/>
   </node>
   <node name="C">
      <node name="Dangles">
         <node name="E">
            <node name="Fangles"/>
         </node>
      </node>
   </node>
   <node name="Iangles"/>
</root>
person Dimitre Novatchev    schedule 17.07.2010
comment
@Dimitre: Огромное спасибо! По поводу ошибки в выводе я исправил вопрос. Попробуем ваше решение и сообщим вам. Еще раз спасибо. - person Vijay Dev; 18.07.2010
comment
@ Vijay-Dev: XML-документ все еще не имеет правильного формата. Я изменил свой ответ, включив в него ваш последний XML-документ (исправленный, чтобы он был правильно сформирован) и новый результат. - person Dimitre Novatchev; 18.07.2010
comment
@Dimitre: Не могли бы вы ответить на этот вариант? Мне нужно включить все дочерние узлы узла, которые соответствуют поисковому запросу в выходном xml, независимо от того, какой у них атрибут имени. Как изменить XPath для достижения этой цели? Спасибо! - person Vijay Dev; 20.07.2010
comment
@ Vijay-Dev: Если я хорошо вас понимаю, это первое выражение XPath в моем ответе. - person Dimitre Novatchev; 20.07.2010
comment
Написал еще один шаблон xsl:template для соответствия *[(предок-или-я::*[содержит(@name, \'angles\')])] Кажется, работает нормально. Пожалуйста, дайте мне знать, если есть лучший способ. - person Vijay Dev; 20.07.2010