2

I can successful remove namespace prefixes from all elements, but would just like to remove prefixes from specific elements

I would like to remove the namespace prefix from the X509Data element:

<?xml version="1.0" encoding="UTF-8"?>
<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
    </xenc:EncryptedKey>
    <ds:X509Data>
        <ds:X509Certificate>AAA=</ds:X509Certificate>
    </ds:X509Data>
</ds:KeyInfo>

to:

<?xml version="1.0" encoding="UTF-8"?>
<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
    </xenc:EncryptedKey>
    <X509Data>
        <ds:X509Certificate>AAA=</ds:X509Certificate>
    </X509Data>
</ds:KeyInfo>

Java code:

   public class TestXmlTransformer {

        public static void main(String[] args) throws SAXException, IOException, ParserConfigurationException,
                TransformerFactoryConfigurationError, TransformerException {

            InputStream xmlData = new FileInputStream("C:\\Users\\xxxxxx\\Desktop\\spoon\\test1.xml");
            Document xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlData);

            Source stylesource = new StreamSource("C:\\Users\\xxxxxx\\Desktop\\spoon\\test1.xsl");
            Transformer transformer = TransformerFactory.newInstance().newTransformer(stylesource);
            StringWriter stringWriter = new StringWriter();
            transformer.transform(new DOMSource(xmlDocument), new StreamResult(stringWriter));

            System.out.print(stringWriter.toString());
        }
    }
kojo
  • 23
  • 3
  • Note: AAA= stills the same. – kojo Aug 29 '19 at 21:10
  • What's your actual, high-level goal? Your question as written likely suffers from the [XY Problem](https://meta.stackexchange.com/q/66377/234215). – kjhughes Aug 29 '19 at 22:51
  • There exists a more general solution, which doesn't need to know the namespace of the element that is to be un-namespaced -- in fact the local-name of the element we want to un-namespace can be passed as a global parameter to the XSLT transformation. – Dimitre Novatchev Sep 01 '19 at 17:28

2 Answers2

0

To remove the namespace only from one specific element, you have to recreate it with an own template (assuming that you defined the namespace xmlns:ds="http://www.w3.org/2000/09/xmldsig#" in the stylesheet):

<xsl:template match="ds:X509Data">
    <xsl:element name="X509Data">
        <xsl:apply-templates select="node()|@*" />
    </xsl:element>
</xsl:template>

The output is in combination with the identity template or <xsl:mode on-no-match="shallow-copy"/> is as desired.

So the whole stylesheet could look like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <xsl:output method="xml" />

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

    <xsl:template match="ds:X509Data">
        <xsl:element name="X509Data">
            <xsl:apply-templates select="node()|@*" />
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

And its output is as desired:

<?xml version="1.0" encoding="UTF-8"?>
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
  <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
  <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <xenc:EncryptedKey>
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
    </xenc:EncryptedKey>
    <X509Data>
        <ds:X509Certificate>AAA=</ds:X509Certificate>
    </X509Data>
  </ds:KeyInfo>
</xenc:EncryptedData>
zx485
  • 28,498
  • 28
  • 50
  • 59
  • Thanks, but it is removing all prefixes, – kojo Aug 29 '19 at 21:35
  • *"To remove all prefixes, you could have used exclude-result-prefixes="ds""* No, that's not what `exclude-result-prefixes` does. – michael.hor257k Aug 29 '19 at 22:08
  • I don't want to remove all prefixes.. and with the solution @zx485 is provided, it is removing all prefixes. – kojo Aug 30 '19 at 02:46
  • @kojo, I have tested the proposed solution at https://xsltfiddle.liberty-development.net/pPJ8LUM and the result seems to be the one you described where only the `X509Data` is without prefix. – Martin Honnen Aug 30 '19 at 08:08
  • Thanks guys, it works as expected when I use third party applications; but removes all prefixes in my java code – kojo Aug 30 '19 at 12:18
  • 1
    @kojo, with Java, if you want to pass a W3C `Document` to XSLT, always make sure you use a `DocumentBuilderFactory` on which you set `setNamespaceAware(true)` before building any documents. Otherwise namespaces are not supported and as XSLT/XPath expect to work with XML with namespaces the outcome is not predictable. – Martin Honnen Aug 30 '19 at 13:22
  • @MartinHonnen. many thanks!! It works with DocumentBuilderFactory on which setNamespaceAware(true)... – kojo Aug 30 '19 at 15:17
  • There exists a more general solution, which doesn't need to know the namespace of the element that is to be un-namespaced -- in fact the local-name of the element we want to un-namespace can be passed as a global parameter to the XSLT transformation. – Dimitre Novatchev Aug 31 '19 at 23:34
  • @DimitreNovatchev, could you please post a sample of the more generalized solution you described above? – kojo Sep 03 '19 at 14:44
  • @kojo --It is already in my answer :) You can just try passing the name on the command - line that invokes the transformation (or via the XSLT-processor-specific API) – Dimitre Novatchev Sep 03 '19 at 14:58
0

Here is an XSLT 1.0 solution where the element-name to be un-namespaced is not known in advance and is passed as a parameter:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:param name="pNameToUnNamespace" select="'X509Data'"/>

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

  <xsl:template match="*">
    <xsl:choose>
      <xsl:when test="local-name() = $pNameToUnNamespace">
        <xsl:element name="{local-name()}">
          <xsl:apply-templates select="node()|@*"/>
        </xsl:element>
      </xsl:when>
      <xsl:otherwise><xsl:call-template name="identity"/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Do note: Here we don't (and don't need to) know the namespace to which the element belongs. So, we have achieved a more general solution of the problem. Except for the XSLT namespace, no other namespace is declared / used in the transformation!

When this transformation is applied on the provided XML document:

<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
        </xenc:EncryptedKey>
        <ds:X509Data>
            <ds:X509Certificate>AAA=</ds:X509Certificate>
        </ds:X509Data>
    </ds:KeyInfo>
</xenc:EncryptedData>

the wanted, correct result is produced:

<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
   <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
   <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <xenc:EncryptedKey>
         <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
      </xenc:EncryptedKey>
      <X509Data>
         <ds:X509Certificate>AAA=</ds:X509Certificate>
      </X509Data>
   </ds:KeyInfo>
</xenc:EncryptedData>

We could have passed as value of the parameter 'EncryptedKey' or 'EncryptionMethod', which are in a different namespace, and we again get the wanted correct result.

We can even, with just a little additional effort, have code which is passed as a parameter a list of element-names to be un-namespaced, and it will un-namespace all of these.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431