简体   繁体   中英

how to copy or match only selected elements from an XML input with namespace and then remove the namespace at the same time

Given below is my XML input. How do I copy or match selected elements that I want to reflect in my XML output using XSL.

The general idea of the logic should be to specify only the elements I am interested instead of specifying the elements that I don't like to be included in the output. The elements that I want to reflect in my output XML are always present in the XML Inpput. The rest of the elements vary depending on what the system has generated and so I cannot just specify what to remove.

I was able to this without the namespace from the input by doing the copy-of, but when the namespace is present the code is not working.

I found a way to remove the namespace but when combined with the copy-of, didn't work as well. I am quite confused as to how XSL behaves.

Please bear with my inquiry, I'm very new to XML and XSL and I was assigned to this task because no one from our team had the experience working with XML. Thank you in advance.

XML Input:

<Transaction xmlns="http://www.test.com/rdc.xsd">
    <Transaction>
        <StoreName id="aa">STORE A</StoreName>
        <TransNo>TXN0001</TransNo>
        <RegisterNo>REG001</RegisterNo>
        <Items>
            <Item id="1">
                <ItemID>A001</ItemID>
                <ItemDesc>Keychain</ItemDesc>
            </Item>
            <Item id="2">
                <ItemID>A002</ItemID>
                <ItemDesc>Wallet</ItemDesc>
            </Item>
        </Items>
        <IDONTLIKETHIS_1>
            <STOREXXX>XXX</STOREXXX>
            <TRANSXXX>YYY</TRANSXXX>
        </IDONTLIKETHIS_1>
        <IDONTLIKETHIS_2>
            <STOREXXX>XXX</STOREXXX>
            <TRANSXXX>YYY</TRANSXXX>
        </IDONTLIKETHIS_2>
    </Transaction>
</Transaction>

Desired Output:

<Transaction>
    <Transaction>
        <StoreName id="aa">STORE A</StoreName>
        <TransNo>TXN0001</TransNo>
        <RegisterNo>REG001</RegisterNo>
        <Items>
            <Item id="1">
                <ItemID>A001</ItemID>
                <ItemDesc>Keychain</ItemDesc>
            </Item>
            <Item id="2">
                <ItemID>A002</ItemID>
                <ItemDesc>Wallet</ItemDesc>
            </Item>
        </Items>
    </Transaction>
</Transaction>

I've tried the code below but the problem with it is that I'm missing the second Transaction element and the xmlns attribute is present in the root element:

<xsl:template match="*">
  <xsl:copy-of select="@*"/>
  <xsl:apply-templates/>
</xsl:template>
<xsl:template match="node()[not(self::*)]">
  <xsl:copy-of select="."/>      
</xsl:template>   
<xsl:template match="*">
  <xsl:copy>
      <xsl:copy-of select="x:Transaction/x:StoreName"/>
      <xsl:copy-of select="x:Transaction/x:TransNo"/>
      <xsl:copy-of select="x:Transaction/x:RegisterNo"/>
      <xsl:copy-of select="x:Transaction/x:Items"/>
 </xsl:copy>      
</xsl:template>

This transformation uses a list of all names of elements that we want to copy after processing :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:x="http://invia.fujitsu.com/RetailDATACenter/rdc.xsd">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pNames" select=
    "'|Transaction|StoreName|TransNo|RegisterNo|Items|Item|ItemID|ItemDesc'"/>

    <xsl:template match="*" >
     <xsl:if test=
      "contains($pNames,
                concat('|',local-name(), '|')
              )">
        <xsl:element name="{name()}">
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:element>
     </xsl:if>
    </xsl:template>

    <xsl:template match="node()[not(self::*)]">
        <xsl:copy-of select="."/>
    </xsl:template>
</xsl:stylesheet>

when applied to the provided XML document :

<Transaction xmlns="http://www.test.com/rdc.xsd">
    <Transaction>
        <StoreName id="aa">STORE A</StoreName>
        <TransNo>TXN0001</TransNo>
        <RegisterNo>REG001</RegisterNo>
        <Items>
            <Item id="1">
                <ItemID>A001</ItemID>
                <ItemDesc>Keychain</ItemDesc>
            </Item>
            <Item id="2">
                <ItemID>A002</ItemID>
                <ItemDesc>Wallet</ItemDesc>
            </Item>
        </Items>
        <IDONTLIKETHIS_1>
            <STOREXXX>XXX</STOREXXX>
            <TRANSXXX>YYY</TRANSXXX>
        </IDONTLIKETHIS_1>
        <IDONTLIKETHIS_2>
            <STOREXXX>XXX</STOREXXX>
            <TRANSXXX>YYY</TRANSXXX>
        </IDONTLIKETHIS_2>
    </Transaction>
</Transaction>

the wanted, correct result is produced:

<Transaction>
   <Transaction>
      <StoreName id="aa">STORE A</StoreName>
      <TransNo>TXN0001</TransNo>
      <RegisterNo>REG001</RegisterNo>
      <Items>
         <Item id="1">
            <ItemID>A001</ItemID>
         </Item>
         <Item id="2">
            <ItemID>A002</ItemID>
         </Item>
      </Items>
   </Transaction>
</Transaction>

Do note : One advantage of this solution over other possible solution is that the string containing the pipe-delimited list of element names can be provide as an externally-specified parameter to the transformation, making it very powerful and flexible and eliminating the need to alter the code any time we include new elements (or exclude some) from our white-list.

Would this work?

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">
  <xsl:variable name="allowed">
    <names>
      <name>Transaction</name>
      <name>StoreName</name>
      <name>TransNo</name>
      <name>RegisterNo</name>
      <name>Items</name>
      <name>Item</name>
      <name>ItemID</name>
      <name>ItemDesc</name>
      <name>id</name>
    </names>
  </xsl:variable>
  <xsl:template match="text()">
    <xsl:copy-of select="."/>
  </xsl:template>
  <xsl:template match="@*">
    <xsl:copy-of select="."/>
  </xsl:template>
  <xsl:template match="comment()">
    <xsl:copy-of select="."/>
  </xsl:template>
  <xsl:template match="processing-instruction()">
    <xsl:copy-of select="."/>
  </xsl:template>
  <xsl:template match="*">
    <xsl:if test="local-name(.) = $allowed//name">
      <xsl:element name="{local-name(.)}">
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates select="node()"/>
      </xsl:element>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

This seems to do what you need, but please read up on namespaces as I'm not sure that what you plan to do is all that sensible.

Hope this helps,

Ken

<xsl:stylesheet version="1.0" xmlns:abc="http://www.test.com/rdc.xsd" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="abc:Transaction|abc:StoreName|abc:TransNo|abc:RegisterNo|abc:Items|abc:Item|abc:ItemID|abc:ItemDesc">
    <xsl:element name="{local-name()}"> 
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="@*"> 
    <xsl:copy/> 
</xsl:template> 

<xsl:template match="*"/>


</xsl:stylesheet> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM