Cannot add namespace prefix for children using XSL

I checked a lot of answers here, and I think I'm almost there. One thing that erodes me (and for some reason her peer needs her):

I have the following input XML:

<?xml version="1.0" encoding="utf-8"?> <MyRoot> <MyRequest CompletionCode="0" CustomerID="9999999999"/> <List TotalList="1"> <Order CustomerID="999999999" OrderNo="0000000001" Status="Shipped"> <BillToAddress ZipCode="22221"/> <ShipToAddress ZipCode="22222"/> <Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/> </Order> </List> <Errors/> </MyRoot> 

I was asked to do this:

 <ns:MyNewRoot xmlns:ns="http://schemas.foo.com/response" xmlns:N1="http://schemas.foo.com/request" xmlns:N2="http://schemas.foo.com/details"> <N1:MyRequest CompletionCode="0" CustomerID="9999999999"/> <ns:List TotalList="1"> <N2:Order CustomerID="999999999" Level="Preferred" Status="Shipped"> <N2:BillToAddress ZipCode="22221"/> <N2:ShipToAddress ZipCode="22222"/> <N2:Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/> </N2:Order> </ns:List> <ns:Errors/> </ns:MyNewRoot> 

Please note that for children N2: for the order, you also need the N2 prefix, as well as the ns: prefix for the remaining elements.

I am using the XSL transform below:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="/MyRoot"> <MyNewRoot xmlns="http://schemas.foo.com/response" xmlns:N1="http://schemas.foo.com/request" xmlns:N2="http://schemas.foo.com/details"> <xsl:apply-templates/> </MyNewRoot> </xsl:template> <xsl:template match="/MyRoot/MyRequest"> <xsl:element name="N1:{name()}" namespace="http://schemas.foo.com/request"> <xsl:copy-of select="namespace::*"/> <xsl:apply-templates select="@* | node()"/> </xsl:element> </xsl:template> <xsl:template match="/MyRoot/List/Order"> <xsl:element name="N2:{name()}" namespace="http://schemas.foo.com/details"> <xsl:copy-of select="namespace::*"/> <xsl:apply-templates select="@* | node()"/> </xsl:element> </xsl:template> </xsl:stylesheet> 

This process does not handle ns (I could not figure it out). When I process the above XSL transform with AltovaXML, I finish below:

 <MyNewRoot xmlns="http://schemas.foo.com/response" xmlns:N1="http://schemas.foo.com/request" xmlns:N2="http://schemas.foo.com/details"> <N1:MyRequest CompletionCode="0" CustomerID="9999999999"/> <List xmlns="" TotalList="1"> <N2:Order CustomerID="999999999" Level="Preferred" Status="Shipped"> <BillToAddress ZipCode="22221"/> <ShipToAddress ZipCode="22222"/> <Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/> </N2:Order> </List> <Errors/> </MyNewRoot> 

Please note that the N2 prefix for order children does not exist after the XSL conversion. Also added xmlns = "" in the order header (for any reason). I could not figure out how to put the ns: prefix for the rest of the elements (e.g. Errors and List).

First of all, why do I need to put a prefix for children if the parent already has it. Doesn't the parent namespace define the namespaces of the child nodes / attributes?

Secondly, I want to add prefixes in the above XML, as expected, how can I do this using XSL?

+6
xml namespaces xslt xml-namespaces transformation
source share
2 answers

This conversion (42 lines total) :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://schemas.foo.com/response" xmlns:N1="http://schemas.foo.com/request" xmlns:N2="http://schemas.foo.com/details" > <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="*"> <xsl:element name="ns:{name()}" namespace="http://schemas.foo.com/response"> <xsl:apply-templates select="node()|@*"/> </xsl:element> </xsl:template> <xsl:template match="@*"> <xsl:copy-of select="."/> </xsl:template> <xsl:template match="/MyRoot"> <xsl:element name="ns:{name()}" namespace="http://schemas.foo.com/response"> <xsl:copy-of select= "document('')/*/namespace::*[name()='N1' or name()='N2']"/> <xsl:apply-templates select="node()|@*"/> </xsl:element> </xsl:template> <xsl:template match="MyRequest"> <xsl:element name="N1:{name()}" namespace="http://schemas.foo.com/request"> <xsl:apply-templates select="node()|@*"/> </xsl:element> </xsl:template> <xsl:template match="*[ancestor-or-self::Order]"> <xsl:element name="N2:{name()}" namespace="http://schemas.foo.com/details"> <xsl:apply-templates select="node()|@*"/> </xsl:element> </xsl:template> </xsl:stylesheet> 

when applied to the provided XML document :

 <MyRoot> <MyRequest CompletionCode="0" CustomerID="9999999999"/> <List TotalList="1"> <Order CustomerID="999999999" OrderNo="0000000001" Status="Shipped"> <BillToAddress ZipCode="22221"/> <ShipToAddress ZipCode="22222"/> <Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/> </Order> </List> <Errors/> </MyRoot> 

creates the desired result :

 <ns:MyRoot xmlns:N1="http://schemas.foo.com/request" xmlns:N2="http://schemas.foo.com/details" xmlns:ns="http://schemas.foo.com/response"> <N1:MyRequest CompletionCode="0" CustomerID="9999999999"/> <ns:List TotalList="1"> <N2:Order CustomerID="999999999" OrderNo="0000000001" Status="Shipped"> <N2:BillToAddress ZipCode="22221"/> <N2:ShipToAddress ZipCode="22222"/> <N2:Totals Tax="0.50" SubTotal="10.00" Shipping="4.95"/> </N2:Order> </ns:List> <ns:Errors/> </ns:MyRoot> 

Please note :

  • Using <xsl:element> and its name and namespace attributes.

  • How the identity template evolved into the first two transformation templates - this solution was based on the fact that only in exceptional cases the element should not be in the ns: namespace.

  • As indicated by the N2: namespace N2: for the Order element or any of its children.

+6
source share

If you are really interested in what namespace prefixes are in the output, then you will want to use literal elements in your templates, not the xsl:element constructor:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://schemas.foo.com/response" xmlns:N1="http://schemas.foo.com/request" xmlns:N2="http://schemas.foo.com/details"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="MyRoot"> <ns:MyNewRoot> <xsl:apply-templates/> </ns:MyNewRoot> </xsl:template> <xsl:template match="MyRequest"> <N1:MyRequest> <xsl:apply-templates select="@* | node()"/> </N1:MyRequest> </xsl:template> <xsl:template match="List"> <ns:List> <xsl:apply-templates select="@* | node()"/> </ns:List> </xsl:template> <xsl:template match="Order"> <N2:Order> <xsl:apply-templates select="@* | node()"/> </N2:Order> </xsl:template> <xsl:template match="BillToAddress"> <N2:BillToAddress> <xsl:apply-templates select="@* | node()"/> </N2:BillToAddress> </xsl:template> <xsl:template match="ShipToAddress"> <N2:ShipToAddress> <xsl:apply-templates select="@* | node()"/> </N2:ShipToAddress> </xsl:template> <xsl:template match="Totals"> <N2:Totals> <xsl:apply-templates select="@* | node()"/> </N2:Totals> </xsl:template> <xsl:template match="Errors" /> </xsl:stylesheet> 

You should know that the namespace is important, but the namespace prefix is ​​not. This is syntactic sugar. You can have several namespace prefixes associated with the same uri namespace, or you can not have a namespace prefix and still create the same types of elements (bound to a specific uri namespace).

+4
source share

All Articles