XSLT 1.0: Как переместить атрибут и его значение только в первый элемент нового элемента

#xml #xslt

Вопрос:

У меня есть следующий XML:

 <?xml version="1.0" encoding="utf-8"?>
<DocumentElement>
  <Invoice>
    <Header Misc="5.00" TotalAmount="33.00" />
    <Packages>
      <Package>
        <Lines>
          <Line>
            <LineCharge Amount="28.00" />
          </Line>
        </Lines>
      </Package>
      <Package>
        <Lines>
          <Line>
            <LineCharge Amount="0.00" />
          </Line>
        </Lines>
      </Package>
    </Packages>
  </Invoice>
</DocumentElement>
 

И применил следующий шаблон для перемещения атрибута Разное и его значения из элемента заголовка, создал новый элемент под названием LineCharge и присвоил ему значение Разное.

 <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"  xmlns:xe="urn:XSLTExtender"
                exclude-result-prefixes="msxsl">

<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />

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

  <xsl:template match="@Misc" />
  <xsl:template match ="LineCharge[1]" >
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
    <xsl:if test="normalize-space(//Invoice/Header/@Misc) != '' and number(//Invoice/Header/@Misc) = normalize-space(//Invoice/Header/@Misc)">
      <xsl:element name ="LineCharge">        
        <xsl:attribute name="Chargecd" >
          <xsl:value-of select="'CD01'"/>
        </xsl:attribute>
        <xsl:attribute name="Amount" >
          <xsl:value-of select="//Invoice/Header/@Misc"/>
        </xsl:attribute>
      </xsl:element>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>
 

Я получил следующий результат:

 <DocumentElement><Invoice>
  <Header TotalAmount="33.00" />
  <Packages>
    <Package>
      <Lines>
        <Line>
          <LineCharge Amount="28.00" />
          <LineCharge Chargecd="CD01" Amount="5.00" />
        </Line>
      </Lines>
    </Package>
    <Package>
      <Lines>
        <Line>
          <LineCharge Amount="0.00" />
          <LineCharge Chargecd="CD01" Amount="5.00" />
        </Line>
      </Lines>
    </Package>
  </Packages>
</Invoice></DocumentElement>
 

Общее количество должно быть 33,00 (т. е. 28,00 5,00), но поскольку новый элемент LineCharge повторяется для каждого элемента пакета, общее количество теперь составляет 38,00, что неверно.

Как мне получить следующий вывод, в котором значение Misc перемещается только в первый элемент пакета, а не в его братьев и сестер:

 <DocumentElement><Invoice>
  <Header TotalAmount="33.00" />
  <Packages>
    <Package>
      <Lines>
        <Line>
          <LineCharge Amount="28.00" />
          <LineCharge Chargecd="CD01" Amount="5.00" />
        </Line>
      </Lines>
    </Package>
    <Package>
      <Lines>
        <Line>
          <LineCharge Amount="0.00" />          
        </Line>
      </Lines>
    </Package>
  </Packages>
</Invoice></DocumentElement>
 

Ответ №1:

Почему бы просто не:

XSLT 1.0

 <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:strip-space elements="*"/>

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

<xsl:template match="Package[1]/Lines/Line[1]">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
        <LineCharge Chargecd="CD01" Amount="{ancestor::Invoice/Header/@Misc}" />
    </xsl:copy>
</xsl:template>

<xsl:template match="@Misc"/>

</xsl:stylesheet>
 

Комментарии:

1. Спасибо тебе, Майкл!