XSLT. Как добавить узел в крайнем левом листе с заданным именем? - PullRequest
0 голосов
/ 01 февраля 2010

Мне бы хотелось добавить узел в крайнем левом листе с указанным именем. Например,

<root>
  <aaa name="a">
    <aaa name="x"/>
  </aaa>
  <aaa name="b">
    <aaa name="y">
      <aaa name="z"/>
    </aaa>
  </aaa>
  <aaa name="c">
    <aaa name="z"/>
  </aaa>
</root>

Имя name = "z" и новый узел <aaa name="w">. Новое дерево должно иметь следующую форму:

<root>
  <aaa name="a">
    <aaa name="x"/>
  </aaa>
  <aaa name="b">
    <aaa name="y">
      <aaa name="z">
        <aaa name="w"/>
      </aaa>   
    </aaa>
  </aaa>
  <aaa name="c">
    <aaa name="z"/>
  </aaa>
</root>

Ответы [ 5 ]

2 голосов
/ 01 февраля 2010

Если под самым левым вы подразумеваете узел 'z' с наибольшей глубиной, вы можете сначала определить переменную для определения глубины самого левого 'z', а затем добавить узел 'w' при совпадении с узел на такой глубине

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <!-- Work out depth of left most 'z' node -->
   <xsl:variable name="LeftMost">
      <xsl:for-each select="//*[@name='z']">
         <xsl:sort select="count(ancestor::*)" order="descending"/>
         <xsl:if test="position() = 1">
            <xsl:value-of select="count(ancestor::*)"/>
         </xsl:if>
      </xsl:for-each>
   </xsl:variable>
   <xsl:template match="/">
      <xsl:apply-templates/>
   </xsl:template>
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
         <xsl:if test="@name='z' and count(ancestor::*) = $LeftMost">
            <aaa name="w"/>
         </xsl:if>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

Используя это, если бы у вас было два узла 'z' на одной и той же глубине, вы в конечном итоге получили бы оба узла 'w'.

Альтернативный подход состоит в том, чтобы использовать generate-id (), чтобы получить идентификатор кулака 'z' на наибольшей глубине, а затем добавить 'w', когда вы сопоставляете узел с тем же идентификатором. Тогда это только добавит узел 'w' к первому узлу 'z', который он найдет на наибольшей глубине.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:variable name="LeftMost">
        <xsl:for-each select="//*[@name='z']">
      <xsl:sort select="count(ancestor::*)" order="descending"/>
            <xsl:if test="position() = 1">
                <xsl:value-of select="generate-id()"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
            <xsl:if test="@name='z' and generate-id() = $LeftMost">
                <aaa name="w"/>
            </xsl:if>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
2 голосов
/ 01 февраля 2010

Вариант @ подхода Тима С, основанного на переменных , будет включать <xsl:key> и предыдущую ось, например:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> 
  <xsl:key 
    name="kCountPreceding" match="aaa[@name='z']" 
    use="count(preceding::aaa[@name='z'])"
  />
  <xsl:variable name="vLeftMostId" select="
    generate-id(key('kCountPreceding', 0))" 
  />

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
      <xsl:if test="generate-id() = $vLeftMostId">
        <aaa name="w" />
      </xsl:if>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Недостатком этого подхода является то, что в выражении соответствия не допускаются переменные. Это означает, что это нельзя сделать динамическим, ключ match="aaa[@name='z']" должен быть жестко закодирован.

0 голосов
/ 01 февраля 2010

крайний левый лист - первый лист в порядок документов, не так ли?

да, первый лист с указанным именем

0 голосов
/ 01 февраля 2010

Почему вы не используете предыдущую ось?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match="/">
    <xsl:apply-templates />
  </xsl:template>
  <xsl:template match="*[@name='z' and not(preceding::*[@name='z'])]">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <aaa name="w" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="*">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Кстати, самый левый лист - это первый лист в порядке документа, не так ли?

<xsl:for-each select="/descendant::*[@name='z' and not(*)][1]">
  ...
  <aaa name="w" />
  ...
</xsl:for-each>
0 голосов
/ 01 февраля 2010

Вы можете начать с чего-то вроде этого:

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

  <xsl:template match="/">
    <xsl:apply-templates />
  </xsl:template>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <xsl:if test="@name='z'">
        <aaa name="w"/>
      </xsl:if>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...