Вопрос по xml-twig, xslt-2.0, xquery, xpath, xpath-2.0 – как получить наиболее глубоко вложенные узлы элементов, используя xpath? (реализация с XMLTWIG)

4

Мне нужно извлечь (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. Mapper
Выражение XPath, которое вы показываете"//[@method]/ancestor::*" не является допустимым XPath и должен дать вам синтаксическую ошибку. Michael Kay
Я исправил / обновил свой вопрос. Выходной файл является результатом моей команды xpath//[@method]/ancestor::*, Дайте мне знать, если возможно с помощью xpath отфильтровать самый дальний узел методом (и включить его прямого предка). Если это невозможно (тогда мы используем XSLT), я изменю вопрос, имея файл XML в качестве ВЫХОДА. laurentngu
Я думаю, что найти наиболее глубоко вложенные элементы невозможно с XPath, потому что XPath не имеетcurrent() функция. В противном случае решением будет выбрать все элементы, для которых нет других элементов с большим числом предков. Используя XSLT, это можно выразить. O. R. Mapper
Нам нужен XSLT, если вы хотите манипулировать узлами, чтобы только XPath не мог удалить предков, которых вы не хотите. Затем рассмотрите возможность размещения правильно сформированных выборок ввода и вывода, поэтому для входной выборки, по крайней мере, не хватает закрывающего тега, а желаемый результат вообще не является правильно сформированным, неясно, хотите ли выSOURCE элемент, содержащийDEST элементы или, если вы хотите сгладить существующую иерархию и вывести все элементы на одном уровне. Martin Honnen

Ваш Ответ

4   ответа
1

Как я уже упоминал в своем комментарии к этому вопросу, я не думаю, что это возможно с чистым 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.

3

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

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

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

0

<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>
@MartinHonnen: Вопрос не требует реорганизации узлов или устранения чего-либо. Опубликованный результат просто показывает узлы, которые, как ожидается, будут найдены. Я согласен с тем, что XPath недостаточно для поиска соответствующих узлов, поскольку в нем отсутствуют XSLT.current() функция.
Да, мне нужен Xpath, чтобы показать наиболее глубоко вложенные элементы и их прямого предка. В любом случае, спасибо @Martin за это решение XLST. Но на самом деле мне нужно использовать команду xpathif possible, Вот код xmltwig, который я использую:@abc=$t->get_xpath("\/\/[\@method]\/ancestor\:\:\*"); foreach my $v (@abc) {# blabla} laurentngu
Похоже, вы хотите реорганизовать узлы, исключив предков и сопоставив каждый лист своему родителю, по крайней мере, это то, что я вижу в опубликованном вами результате. Поскольку XPath не позволяет вам манипулировать узлами, а выбирает узлы в существующих документах, я думаю, что вам нужно больше, чем XPath. Пример вашего комментария предполагает, что вы хотите использовать какой-то обязательный язык хоста и XPath, но я не распознаю этот язык, поэтому не могу с этим поделать. Пометьте свой вопрос этим языком (например, Python, PHP), объясните, какой XPath API вы используете, и тогда люди с опытом в этой области могут помочь.
Вопрос звучит так, будто laurentgnu хочет найти наиболее глубоко вложенные элементы в документе XML.
Ну и ладно, "опубликованный результат" это что-то вроде<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.
1

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>

Похожие вопросы