Search code examples
xmlxsltxslt-2.0

XSLT Add Type to Specific Tags


I have an XML doc where some of the tags include the type in the tag name. For the tags within the <TXT> tags I've successfully appended the type to the tag name, i.e. <NUMEX type="CASENUM"> becomes <NUMEX_CASENUM>. However, within the <SUBJECT> tag I need to add the type to <SubjectPhone> but not to any of the tags related to subject name.

I tried to read up on conditionally appending the type to the tag name but I didn't have much success.

Below I've included my XML, the current state of my XSLT, the actual output, and the output I would like to see.

XML

<?xml version="1.0" encoding="UTF-8"?>
<NORMDOC>
   <DOC>
      <DOCID>123456789</DOCID>
      <FI fitype="B" xref="54815594127">
         <FIName>BANK OF FURY, N.A.</FIName>
      </FI>
      <OIs>
         <OI xref="54815594128">
            <OIName>BANK OF FURY, N.A.</OIName>
         </OI>
      </OIs>
      <Subjects>
         <Subject stype="PER" xref="54815594140">
            <SubjectFullName type="L">SMITH/JANE/C</SubjectFullName>
            <SubjectLastName type="L">SMITH</SubjectLastName>
            <SubjectFirstName type="L">JANE</SubjectFirstName>
            <SubjectPhone type="Mobile">123-456-7890</SubjectPhone>
         </Subject>
      </Subjects>
      <TXT>
         <S sid="123456789-SENT-075">For assistance contact <ENAMEX type="BANK" id="BAN-123456789-323">BANK OF FURY</ENAMEX> Law Enforcement Liaison at <IDEX type="PHONE" id="PHO-123456789-324">000-000-0000</IDEX> or <CYBEX type="EMAIL" id="EMA-123456789-325">[email protected]</CYBEX>.</S>
      </TXT>
   </DOC>
   <ENTINFO ID="ACC-123456789-013"
            TYPE="ACCOUNT"
            NORM="333222111000"
            REFID="ACC-123456789-013"
            ACCT-TYPE="CHK"
            MENTION="MI checking account # 333222111000"/>
</NORMDOC>

XSLT

<xsl:stylesheet version="2.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="*"/>

<xsl:template match="/NORMDOC">
    <xsl:apply-templates select="DOC"/>
    <ENTINFOS>
        <xsl:apply-templates select="ENTINFO"/>
    </ENTINFOS>
</xsl:template>

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

<xsl:template match="TXT">
    <RAW_TXT>
        <xsl:value-of select="."/>
    </RAW_TXT>
    <xsl:copy>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

<xsl:template match="S">
    <xsl:copy>
        <xsl:apply-templates select="*" mode="extra"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*" mode="extra">
    <xsl:element name="{name()}_{@type}">
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="ENTINFO">
    <xsl:copy>
        <xsl:for-each select="@*">
            <xsl:element name="ENTINFO_{translate(name(), '-', '_')}">
                <xsl:value-of select="." />
             </xsl:element>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

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

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

</xsl:stylesheet>

Actual Output

<NORMDOC>
   <DOC>
      <DOCID>123456789</DOCID>
      <FI>
         <FIName>BANK OF FURY, N.A.</FIName>
      </FI>
      <OIs>
         <OI>
            <OIName>BANK OF FURY, N.A.</OIName>
         </OI>
      </OIs>
      <Subjects>
         <Subject>
            <SubjectFullName>SMITH/JANE/C</SubjectFullName>
            ...
            <SubjectPhone>123-456-7890</SubjectPhone>
         </Subject>
      </Subjects>
      <RAW_TXT>For assistance contact BANK OF FURY Law Enforcement Liaison at 000-000-0000 or [email protected].</RAW_TXT>
      <TXT>
         <S>
            <ENAMEX_BANK>BANK OF FURY</ENAMEX_BANK>
            <IDEX_PHONE>000-000-0000</IDEX_PHONE>
            <CYBEX_EMAIL>[email protected]</CYBEX_EMAIL>
         </S>
      </TXT>
   </DOC>
   <ENTINFOS>
      <ENTINFO>
         <ENTINFO_ID>ACC-123456789-013</ENTINFO_ID>
         ...
         <ENTINFO_MENTION>MI checking account # 333222111000</ENTINFO_MENTION>
      </ENTINFO>
   </ENTINFOS>
</NORMDOC>

Expected Output

<NORMDOC>
   <DOC>
      <DOCID>123456789</DOCID>
      <FI>
         <FIName>BANK OF FURY, N.A.</FIName>
      </FI>
      <OIs>
         <OI>
            <OIName>BANK OF FURY, N.A.</OIName>
         </OI>
      </OIs>
      <Subjects>
         <Subject>
            <SubjectFullName>SMITH/JANE/C</SubjectFullName>
            ...
            <SubjectPhone_Mobile>123-456-7890</SubjectPhone>
         </Subject>
      </Subjects>
      <RAW_TXT>For assistance contact BANK OF FURY Law Enforcement Liaison at 000-000-0000 or [email protected].</RAW_TXT>
      <TXT>
         <S>
            <ENAMEX_BANK>BANK OF FURY</ENAMEX_BANK>
            <IDEX_PHONE>000-000-0000</IDEX_PHONE>
            <CYBEX_EMAIL>[email protected]</CYBEX_EMAIL>
         </S>
      </TXT>
   </DOC>
   <ENTINFOS>
      <ENTINFO>
         <ENTINFO_ID>ACC-123456789-013</ENTINFO_ID>
         ...
         <ENTINFO_MENTION>MI checking account # 333222111000</ENTINFO_MENTION>
      </ENTINFO>
   </ENTINFOS>
</NORMDOC>

Solution

  • I think all you need to do is add this template into your XSLT

    <xsl:template match="SubjectPhone">
       <xsl:apply-templates select="." mode="extra" />
    </xsl:template>
    

    Alternatively, remove the template that matches S, along with template that matches * for mode "extra", and replace them with this template instead:

    <xsl:template match="S/*|SubjectPhone">
        <xsl:element name="{name()}_{@type}">
           <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    

    See http://xsltfiddle.liberty-development.net/bFN1y9K for this latter case.

    Do note that this template in your XSLT is redundant

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

    You already have a template matching @*|node() later on in your XSLT, and node() is short-hand *|text()|comment()|processing-instruction(). (Strictly speaking it is an error to have two templates matching the same thing with equal priority. XSLT processors may signal the error, or just use the last template, which is what is happening here).