XSL Transformation - Insert Attribute

2019-08-02 17:52发布

I'm very much a beginner with xsl transformations

I have some xml that I need to insert an attribute into an element when that attribute doesn't exist..

Using the below xml as an example.

<Order Id="IR1598756" Status="2">
  <Details>
    <SomeInfo>Sample Data</SomeInfo>
  </Details>
  <Documents>
    <Invoice>
      <Date>15-02-2011</Date>
      <Time>11:22</Time>
      <Employee Id="159">James Morrison</Employee>
    </Invoice>
    <DeliveryNote>
      <Reference>DN1235588</Reference>
      <HoldingRef>HR1598785</HoldingRef>
      <Date>16-02-2011</Date>
      <Time>15:00</Time>
      <Employee Id="25">Javi Cortez</Employee>
    </DeliveryNote>
  </Documents>
</Order>

Desired Output

<Order Id="IR1598756" Status="2">
  <Details>
    <SomeInfo>Sample Data</SomeInfo>
  </Details>
  <Documents>
    <Invoice Id="DN1235588">
      <Date>15-02-2011</Date>
      <Time>11:22</Time>
      <Employee Id="159">James Morrison</Employee>
    </Invoice>
  </Documents>
</Order>    

The <Invoice> element can have an Id attribute <Invoice Id="IR1564897">

How can I check the following.

  1. Check that the attribute exists
  2. If not then Insert the Value of the <Refernce>DN1235588</Reference> as the Id
  3. If there is no <Reference> Use the Value of the <HoldingRef>HR1598785</HoldingRef>

I was looking at implementing something like the following

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

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

  <xsl:template match="Order/Documents/Invoice[not(@Id)]">
      <xsl:attribute name="Id">
        <xsl:value-of select="//Documents/DeliveryNote/Reference"/>
      </xsl:attribute>      
  </xsl:template>

The above is not outputting the full <Invoice> element. How can I correct this?

  <xsl:if test="Order/Documents/DeliveryNote/Reference">
    <xsl:value-of select="//Documents/DeliveryNote/Reference"/>
  </xsl:if>
  <xsl:if test="Not(Order/Documents/DeliveryNote/Reference)">
    <xsl:value-of select="//Documents/DeliveryNote/HoldingRef"/>
  </xsl:if>

If either one will always exist will this work to alternate between <Reference> and <HoldingRef>?

With the help of Alex: The following has worked for me to replace the attribute

  <xsl:template match="Order/Documents/Invoice[not(@Id)]">       
    <Invoice>
      <xsl:attribute name="Id">         
        <xsl:value-of Select="//Documents/DeliveryNote/Reference"/>
      </xsl:attribute>         
      <xsl:apply-templates select="@* | node()"/>
    </Invoice>
  </xsl:template>

标签: xslt
3条回答
趁早两清
2楼-- · 2019-08-02 18:21

Give a try:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:apply-templates select="//Order"/>
    </xsl:template>

    <!-- Match invoices without ids -->
    <!-- [DeliveryNote[Reference or HoldingRef] - defend against empty attributes -->
    <xsl:template match="Invoice[not(@id)][DeliveryNote[Reference or HoldingRef]]">
        <xsl:copy>
            <!-- create an attribute and fetch required data. In case Reference is present then insert reference
             otherwise - HoldingRef -->
            <xsl:attribute name="Id">
                <xsl:value-of select="following-sibling::DeliveryNote[1]/Reference |
                        following-sibling::DeliveryNote[1]/HoldingRef"/>
            </xsl:attribute>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="//DeliveryNote"/>

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

As for your questions:

The above is not outputting the full element. How can I correct this?

See my example.

If either one will always exist will this work to alternate between <Reference> and <HoldingRef>?

Either with XPath (as in my example) or with xsl:choose.

查看更多
Juvenile、少年°
3楼-- · 2019-08-02 18:28

The shortest answer:

<xsl:template match="Invoice[not(@Id)]">
    <Invoice Id="{(../DeliveryNote/Reference|
                   ../DeliveryNote/HoldingRef)[1]}">
        <xsl:apply-templates select="@* | node()"/>
    </Invoice>
</xsl:template>
查看更多
smile是对你的礼貌
4楼-- · 2019-08-02 18:41

What about this?

  <xsl:template match="Invoice[not(@Id)]">
    <xsl:element name="Invoice">
      <xsl:attribute name="Id">
        <xsl:variable name="REF" select="../DeliveryNote/Reference"/>
        <xsl:choose>
          <xsl:when test="not($REF)">
            <xsl:value-of select="../DeliveryNote/HoldingRef"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="../DeliveryNote/Reference"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>
      <xsl:apply-templates select="*"/>
    </xsl:element>
  </xsl:template>

Use it instead your <xsl:template match="Order/Documents/Invoice[not(@Id)]">

查看更多
登录 后发表回答