0
votes

I am trying to add one element from some XML to certain children elements in the same XML. The element in question is repeated already so the value has to be from the correct section of the XML. Here is the source XML:

<Extract>
  <Packet>
    <TXREQUESTID>694154</TXREQUESTID>
    <Data>
      <Property>
        <Key>phoneNumber</Key>
      </Property>
      <Property>
        <Key>ownerName</Key>
      </Property>
    </Data>
    <Milestones>
      <Milestone>
        <Code>123123</Code>
      </Milestone>
      <Milestone>
        <Code>123125</Code>
      </Milestone>
    </Milestones>
  </Packet>
  <Packet>
    <TXREQUESTID>694155</TXREQUESTID>
    <Data>
      <Property>
        <Key>phoneNumber</Key>
      </Property>
      <Property>
        <Key>ownerName</Key>
      </Property>
    </Data>
    <Milestones>
      <Milestone>
        <Code>789789</Code>
      </Milestone>
      <Milestone>
        <Code>123126</Code>
      </Milestone>
    </Milestones>
  </Packet>
</Extract>

I need to replicate the TXREQUESTID element as an element in the child Property and Milestone elements. When finished it needs to look like this:

<Extract>
  <Packet>
    <TXREQUESTID>694154</TXREQUESTID>
    <Data>
      <Property>
        <Key>phoneNumber</Key>
        <TXREQUESTID>694154</TXREQUESTID>
      </Property>
      <Property>
        <Key>ownerName</Key>
        <TXREQUESTID>694154</TXREQUESTID>
      </Property>
    </Data>
    <Milestones>
      <Milestone>
        <Code>123123</Code>
        <TXREQUESTID>694154</TXREQUESTID>
      </Milestone>
      <Milestone>
        <Code>123125</Code>
        <TXREQUESTID>694154</TXREQUESTID>
      </Milestone>
    </Milestones>
  </Packet>
  <Packet>
    <TXREQUESTID>694155</TXREQUESTID>
    <Data>
      <Property>
        <Key>phoneNumber</Key>
        <TXREQUESTID>694155</TXREQUESTID>
      </Property>
      <Property>
        <Key>ownerName</Key>
        <TXREQUESTID>694155</TXREQUESTID>
      </Property>
    </Data>
    <Milestones>
      <Milestone>
        <Code>789789</Code>
        <TXREQUESTID>694155</TXREQUESTID>
      </Milestone>
      <Milestone>
        <Code>123126</Code>
        <TXREQUESTID>694155</TXREQUESTID>
      </Milestone>
    </Milestones>
  </Packet>
</Extract>

I've spent hours on this and haven't had any luck. This seems like it should be simple but I'm finding the XSLT syntax baffling. Can anyone point me in the right direction?

1
Pretty much any XSLT problem of the form "I want to keep most of the XML the same but tweak X, Y and Z" can be solved using an identity transformation and a few careful overrides. - Ian Roberts

1 Answers

2
votes

Start with the identity transform and override it for the elements you want to change (Property and Milestone)

XML Input

<Extract>
    <Packet>
        <TXREQUESTID>694154</TXREQUESTID>
        <Data>
            <Property>
                <Key>phoneNumber</Key>
            </Property>
            <Property>
                <Key>ownerName</Key>
            </Property>
        </Data>
        <Milestones>
            <Milestone>
                <Code>123123</Code>
            </Milestone>
            <Milestone>
                <Code>123125</Code>
            </Milestone>
        </Milestones>
    </Packet>
    <Packet>
        <TXREQUESTID>694155</TXREQUESTID>
        <Data>
            <Property>
                <Key>phoneNumber</Key>
            </Property>
            <Property>
                <Key>ownerName</Key>
            </Property>
        </Data>
        <Milestones>
            <Milestone>
                <Code>789789</Code>
            </Milestone>
            <Milestone>
                <Code>123126</Code>
            </Milestone>
        </Milestones>
    </Packet>
</Extract>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

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

</xsl:stylesheet>

XML Output

<Extract>
   <Packet>
      <TXREQUESTID>694154</TXREQUESTID>
      <Data>
         <Property>
            <TXREQUESTID>694154</TXREQUESTID>
            <Key>phoneNumber</Key>
         </Property>
         <Property>
            <TXREQUESTID>694154</TXREQUESTID>
            <Key>ownerName</Key>
         </Property>
      </Data>
      <Milestones>
         <Milestone>
            <TXREQUESTID>694154</TXREQUESTID>
            <Code>123123</Code>
         </Milestone>
         <Milestone>
            <TXREQUESTID>694154</TXREQUESTID>
            <Code>123125</Code>
         </Milestone>
      </Milestones>
   </Packet>
   <Packet>
      <TXREQUESTID>694155</TXREQUESTID>
      <Data>
         <Property>
            <TXREQUESTID>694155</TXREQUESTID>
            <Key>phoneNumber</Key>
         </Property>
         <Property>
            <TXREQUESTID>694155</TXREQUESTID>
            <Key>ownerName</Key>
         </Property>
      </Data>
      <Milestones>
         <Milestone>
            <TXREQUESTID>694155</TXREQUESTID>
            <Code>789789</Code>
         </Milestone>
         <Milestone>
            <TXREQUESTID>694155</TXREQUESTID>
            <Code>123126</Code>
         </Milestone>
      </Milestones>
   </Packet>
</Extract>

If element order matters, split the xsl:apply-templates like this:

<xsl:apply-templates select="@*|node()"/>
<xsl:apply-templates select="ancestor::Packet/TXREQUESTID"/>