I need to add an additional namespace to an already namespaced XML file but only if a particular element does not exist.
My XML doc looks like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<ns3:firstname>Billy</ns3:firstname>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
... and if there is no ns3:firstname element in the person element, I'd like to add a new namespace and (e.g. xmlns:frog="FFF") and also an additional element within person as shown below:
Desired Output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" xmlns:frog="FFF" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<frog:title>
<Master/>
</frog:title>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
My XSL doc currently is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Copy Everything -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="ns:{local-name()}">
<xsl:attribute name="frog">fff</xsl:attribute>
<xsl:apply-templates select="node()|@*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
.... unfortunately this does not work.
I've tried lots of different things but can't seem to achieve this using XSLT v1.0. Any help would be greatly appreciated.
First you need to declare the various namespaces in your stylesheet, as well as the "AAA"
default namespace
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="AAA"
xmlns:frog="FFF"
xmlns:ns2="BBB"
xmlns:ns3="CCC">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Copy Everything -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- for a Person with no firstname, add a frog:title -->
<xsl:template match="ns2:person[not(ns3:firstname)]">
<xsl:copy>
<!-- must handle attributes before elements/text nodes -->
<xsl:apply-templates select="@*" />
<frog:title>
<Master/>
</frog:title>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This will produce
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<frog:title xmlns:frog="FFF">
<Master/>
</frog:title>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
If the xmlns:frog
absolutely must be on the everyone
element rather than on each frog:title
then you could add another template
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="document('')/xsl:stylesheet/namespace::frog" />
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
to copy the namespace declaration off the stylesheet element (though this would mean that every output document has an xmlns:frog
declaration even if it doesn't involve any frog:*
elements).
Edit: apparently Xalan doesn't like the copy-of
namespaces from document('')
, as an alternative, if you know that the document element will always have the same name then you can hard code that as a literal result element
<xsl:template match="/*">
<everyone xmlns:frog="FFF">
<xsl:copy-of select="namespace::*" />
<xsl:apply-templates select="@*|node()" />
</everyone>
</xsl:template>
(technically it will do what you want even without the explicit xmlns:frog
in this template, since literal result elements always get the namespace declarations that are in scope at the point in the stylesheet where they are declared, but the intention is arguably clearer if you include it)
This mailing list post gives some possible insights into the reason for document('')
not working as it should.