как получить наиболее глубоко вложенные узлы элементов, используя xpath? (реализация с XMLTWIG)

Мне нужно извлечь (XSLT, xpath, xquery ... предпочтительно xpath)most deeply nested element узлы сmethod (DEST id = "РОССИЯ" method = "delete" / & gt;) и его прямой предок (SOURCE id = "AFRICA" method = "modify" & gt;).

Я не хочу получать верхние узлы с помощью методов (основной метод = & quot; изменить & gt; или основной метод = & quot; изменить & quot; & gt;).

Самые глубокие вложенные элементы метода соответствуют реальным действиям. Верхние элементы метода на самом деле являются фиктивными действиями, которые не должны приниматься во внимание.

Вот мой пример файла XML:

<?xml version="1.0" encoding="UTF-8"?>
<main method="modify">
<MACHINE method="modify">  
  <SOURCE id="AFRICA" method="modify">
    <DEST id="RUSSIA" method="delete"/>
    <DEST id="USA" method="modify"/>
  </SOURCE>

  <SOURCE id="USA" method="modify">
    <DEST id="AUSTRALIA" method="modify"/>
    <DEST id="CANADA" method="create"/>
  </SOURCE>
</MACHINE>
</main>

Это вывод Xpath, который я ожидаю:

<SOURCE id="AFRICA" method="modify"><DEST id="RUSSIA" method="delete"/>

<SOURCE id="AFRICA" method="modify"><DEST id="USA" method="modify"/>

<SOURCE id="USA" method="modify"><DEST id="AUSTRALIA" method="modify"/>

<SOURCE id="USA" method="modify"><DEST id="CANADA" method="create"/>

Моя текущая команда xpath не дает адекватного результата.

Команда xpath (& quot; // [@ method] / ancestor :: * & quot;), которая возвращает:

<main><MACHINE method="modify">                                        # NOT WANTED

<MACHINE method="modify"><SOURCE id="AFRICA" method="modify">          # NOT WANTED

<MACHINE method="modify"><SOURCE id="USA" method="modify">             # NOT WANTED

<SOURCE id="AFRICA" method="modify"><DEST id="RUSSIA" method="delete"/>

<SOURCE id="AFRICA" method="modify"><DEST id="USA" method="modify"/>

<SOURCE id="USA" method="modify"><DEST id="AUSTRALIA" method="modify"/>

<SOURCE id="USA" method="modify"><DEST id="CANADA" method="create"/>

Мой код xmltwig для дополнительной информации (контекст):

#!/usr/bin/perl -w
use warnings;
use XML::Twig;
use XML::XPath;

@my $t= XML::Twig->new;
my $v= XML::Twig::Elt->new;
$t-> parsefile ('input.xml');

@abc=$t->get_xpath("\/\/[\@method]\/ancestor\:\:\*") ;
 foreach $v (@abc)   # outer 1
 {
    foreach $v ($v ->children)  # internal 1
    {
      $w=$v->parent;
      print $w->start_tag;
      print $v->start_tag;
    }
  }
 O. R. Mapper21 июн. 2012 г., 13:38
Я добавил ответ, чтобы проиллюстрировать то, что я сказал в моем предыдущем комментарии.
 Michael Kay21 июн. 2012 г., 13:55
Выражение XPath, которое вы показываете"//[@method]/ancestor::*" не является допустимым XPath и должен дать вам синтаксическую ошибку.
 laurentngu21 июн. 2012 г., 12:46
Я исправил / обновил свой вопрос. Выходной файл является результатом моей команды xpath//[@method]/ancestor::*, Дайте мне знать, если возможно с помощью xpath отфильтровать самый дальний узел методом (и включить его прямого предка). Если это невозможно (тогда мы используем XSLT), я изменю вопрос, имея файл XML в качестве ВЫХОДА.
 O. R. Mapper21 июн. 2012 г., 13:08
Я думаю, что найти наиболее глубоко вложенные элементы невозможно с XPath, потому что XPath не имеетcurrent() функция. В противном случае решением будет выбрать все элементы, для которых нет других элементов с большим числом предков. Используя XSLT, это можно выразить.
 Martin Honnen21 июн. 2012 г., 12:28
Нам нужен XSLT, если вы хотите манипулировать узлами, чтобы только XPath не мог удалить предков, которых вы не хотите. Затем рассмотрите возможность размещения правильно сформированных выборок ввода и вывода, поэтому для входной выборки, по крайней мере, не хватает закрывающего тега, а желаемый результат вообще не является правильно сформированным, неясно, хотите ли выSOURCE элемент, содержащийDEST элементы или, если вы хотите сгладить существующую иерархию и вывести все элементы на одном уровне.

Ответы на вопрос(4)

Как я уже упоминал в своем комментарии к этому вопросу, я не думаю, что это возможно с чистым XPath, поскольку XPath не имеет ничего подобногоcurrent() функция, которая позволит ссылаться на контекст вне[] ограничение.

Наиболее похожим решением должен быть этот XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ZD="http://xyz.abc">
    <xsl:output method="text"/>

    <xsl:template match="//*">
        <xsl:choose>
            <xsl:when test="not(//*[count(ancestor::node()) > count(current()/ancestor::node())])"><xsl:value-of select="local-name(.)"/><xsl:text>
</xsl:text></xsl:when>
            <xsl:otherwise>
                <xsl:copy>
                    <xsl:apply-templates select="@*|node()"/>
                </xsl:copy>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="text()|@*"/>
</xsl:stylesheet>

<xsl:when> Элемент находит наиболее глубоко вложенные элементы. В качестве примера я вывожу локальные имена найденных элементов, за которыми следует новая строка, но, конечно, вы можете вывести там все, что вам нужно.

Update: Обратите внимание, что это основано на знаниях / инструментах XPath 1.0. Похоже, это действительно можно выразить в XPath 2.0.

Решение Вопроса

//*[count(ancestor::*) = max(//*/count(ancestor::*))]

но это может работать ужасно, в зависимости от того, насколько умен ваш оптимизатор.

Найдя эти узлы, конечно, тривиально найти их предков. Но вы ищете выход с большей структурой, чем может обеспечить только XPath.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/">
  <xsl:apply-templates 
     select="//DEST[@method and not(node())]"/>
</xsl:template>

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

<xsl:template match="DEST[@method and not(node())]">
  <xsl:apply-templates select="..">
    <xsl:with-param name="leaf" select="current()"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="*[DEST[@method and not(node())]]">
  <xsl:param name="leaf"/>
  <xsl:copy>
    <xsl:copy-of select="@* , $leaf"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

прообразы

<?xml version="1.0" encoding="UTF-8"?>
<main method="modify">
<MACHINE method="modify">  
  <SOURCE id="AFRICA" method="modify">
    <DEST id="RUSSIA" method="delete"/>
    <DEST id="USA" method="modify"/>
  </SOURCE>

  <SOURCE id="USA" method="modify">
    <DEST id="AUSTRALIA" method="modify"/>
    <DEST id="CANADA" method="create"/>
  </SOURCE>
</MACHINE>
</main>

в

<SOURCE id="AFRICA" method="modify">
   <DEST id="RUSSIA" method="delete"/>
</SOURCE>
<SOURCE id="AFRICA" method="modify">
   <DEST id="USA" method="modify"/>
</SOURCE>
<SOURCE id="USA" method="modify">
   <DEST id="AUSTRALIA" method="modify"/>
</SOURCE>
<SOURCE id="USA" method="modify">
   <DEST id="CANADA" method="create"/>
</SOURCE>
 21 июн. 2012 г., 13:15
@MartinHonnen: Вопрос не требует реорганизации узлов или устранения чего-либо. Опубликованный результат просто показывает узлы, которые, как ожидается, будут найдены. Я согласен с тем, что XPath недостаточно для поиска соответствующих узлов, поскольку в нем отсутствуют XSLT.current() функция.
 laurentngu21 июн. 2012 г., 13:04
Да, мне нужен Xpath, чтобы показать наиболее глубоко вложенные элементы и их прямого предка. В любом случае, спасибо @Martin за это решение XLST. Но на самом деле мне нужно использовать команду xpathif possible, Вот код xmltwig, который я использую:@abc=$t->get_xpath("\/\/[\@method]\/ancestor\:\:\*"); foreach my $v (@abc) {# blabla}
 21 июн. 2012 г., 13:12
Похоже, вы хотите реорганизовать узлы, исключив предков и сопоставив каждый лист своему родителю, по крайней мере, это то, что я вижу в опубликованном вами результате. Поскольку XPath не позволяет вам манипулировать узлами, а выбирает узлы в существующих документах, я думаю, что вам нужно больше, чем XPath. Пример вашего комментария предполагает, что вы хотите использовать какой-то обязательный язык хоста и XPath, но я не распознаю этот язык, поэтому не могу с этим поделать. Пометьте свой вопрос этим языком (например, Python, PHP), объясните, какой XPath API вы используете, и тогда люди с опытом в этой области могут помочь.
 21 июн. 2012 г., 12:53
Вопрос звучит так, будто laurentgnu хочет найти наиболее глубоко вложенные элементы в документе XML.
 21 июн. 2012 г., 13:25
Ну и ладно, "опубликованный результат" это что-то вроде<SOURCE id="AFRICA" method="modify"><DEST id="RUSSIA" method="delete"/> <SOURCE id="AFRICA" method="modify"><DEST id="USA" method="modify"/>, это не совсем правильно, поэтому мне пришлось сделать некоторые предположения относительно того, какой вид продукции нужен. И вопрос помечен как xslt-2.0, поэтому, на мой взгляд, представление решения XSLT является ответом. Если автор хочет использовать императивный язык вместе с XPath, хорошо, тогда другие могут помочь, я предпочитаю XSLT в качестве основного языка для XPath.

One such XPath2.0 expression is:

  and
   count(ancestor::*)
  =
   max(]/count(ancestor::*))
   ]
     /(self::node|..)

To illustrate this with a complete XSLT 2.0 example:

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

    <xsl:variable name="vResult" select=
     "
        and
          count(ancestor::*)
       =
        max(]/count(ancestor::*))
        ]
          /(self::node|..)
     "/>

 <xsl:template match="/">
     <xsl:sequence select="$vResult"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<main method="modify">
    <MACHINE method="modify">
        <SOURCE id="AFRICA" method="modify">
            <DEST id="RUSSIA" method="delete"/>
            <DEST id="USA" method="modify"/>
        </SOURCE>
        <SOURCE id="USA" method="modify">
            <DEST id="AUSTRALIA" method="modify"/>
            <DEST id="CANADA" method="create"/>
        </SOURCE>
    </MACHINE>
</main>

the XPath expression is evaluated and the selected elements (the elements at maximum depth and their parents) are copied to the output:

<SOURCE id="AFRICA" method="modify">
            <DEST id="RUSSIA" method="delete"/>
            <DEST id="USA" method="modify"/>
        </SOURCE>
<SOURCE id="USA" method="modify">
            <DEST id="AUSTRALIA" method="modify"/>
            <DEST id="CANADA" method="create"/>
        </SOURCE>

Ваш ответ на вопрос