Find a Range of values and use starts with xsl - xslt

I need to ask if Range of Supplier_ID between 985000 and 989999 then set in tag AllocationNumber the value 'FX' else let it empty.
I thought to solve it with the starts with xsl function.
But it does not work (I'm new in XSL)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:user="http://www.altova.com/MapForce/UDF/user" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="user xs fn user">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Form">
<MaxPostInvoice>
<xsl:attribute name="xsi:noNamespaceSchemaLocation">C:\Program Files (x86)\MaxPostInvoice-v1.18.xsd</xsl:attribute>
<Header>
<VendorID>
<xsl:for-each select="Fields">
<xsl:for-each select="Supplier_ID">
<xsl:for-each select="#source">
<xsl:attribute name="source"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="Fields">
<xsl:for-each select="Supplier_ID">
<xsl:for-each select="#confidence">
<xsl:attribute name="confidence"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="Fields">
<xsl:for-each select="Supplier_ID">
<xsl:for-each select="#rawContents">
<xsl:attribute name="rawValue"><xsl:if test="//Fields/Supplier_ID[#rawContents = '']"><xsl:value-of select="''"/></xsl:if><xsl:if test="//Fields/Supplier_ID[#rawContents != 'X']"><xsl:value-of select="."/></xsl:if></xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="Fields">
<xsl:for-each select="Supplier_ID">
<xsl:for-each select="#contents">
<xsl:attribute name="value"><xsl:if test="//Fields/Supplier_ID[#contents = 'X']"><xsl:value-of select="''"/></xsl:if><xsl:if test="//Fields/Supplier_ID[#contents != 'X']"><xsl:value-of select="."/></xsl:if></xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</VendorID>
<AllocationNumber>
<xsl:attribute name="rawValue">
<xsl:if test="number(./Fields/Supplier_ID/#contents) >= 985000 and number(./Fields/Supplier_ID/#contents) <= 989999">
<xsl:value-of select="FX"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:if test="number(./Fields/Supplier_ID/#contents) >= 985000 and number(./Fields/Supplier_ID/#contents) <= 989999">
<xsl:value-of select="FX"/>
</xsl:if>
</xsl:attribute>
</AllocationNumber>
</Header>
</MaxPostInvoice>
</xsl:template>
</xsl:stylesheet>
This is the input XML:
<Form name="FP0001">
<Fields>
<Supplier_ID contents="985000" rawContents="" source="OCR" confidence="100"/>
<Supplier_Name contents="Leifheitstr" rawContents="" source="OCR" confidence="100"/>
<Supplier_Street contents="abcde" rawContents="" source="OCR" confidence="100"/>
</Fields>
</Form>
Result
<Header>
<VendorID source="OCR" confidence="100" rawValue="" value="985000 " />
<AllocationNumber rawValue="" />
</Header>

You have not provided a input XML, if your XPath is correct, you could do:
<xsl:attribute name="rawValue">
<xsl:if test="number(./Fields/Supplier_ID/#contents) >= 985000 and number(./Fields/Supplier_ID/#contents) <= 989999">
<xsl:value-of select="FX"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="rawValue">
<xsl:if test="number(./Fields/Supplier_ID/#contents) >= 985000 and number(./Fields/Supplier_ID/#contents) <= 989999">
<xsl:value-of select="FX"/>
</xsl:if>
</xsl:attribute>
If it not works share your input XML and more of your XSLT (complete).

Why do you have the same attribute twice? In any case, you need to change:
<xsl:value-of select="FX"/>
to:
<xsl:value-of select="'FX'"/>
BTW, it seems to me you could simplify this thing significantly to something like:
<xsl:template match="/Form/Fields">
<MaxPostInvoice xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="C:\Program Files (x86)\MaxPostInvoice-v1.18.xsd">
<Header>
<VendorID source="{Supplier_ID/#source}" confidence="{Supplier_ID/#confidence}" >
<xsl:attribute name="rawValue">
<xsl:if test="Supplier_ID/#rawContents != 'X'">
<xsl:value-of select="Supplier_ID/#rawContents"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:if test="Supplier_ID/#contents != 'X'">
<xsl:value-of select="Supplier_ID/#contents"/>
</xsl:if>
</xsl:attribute>
</VendorID>
<AllocationNumber>
<xsl:attribute name="rawValue">
<xsl:if test="number(Supplier_ID/#contents) >= 985000 and number(Supplier_ID/#contents) <= 989999">
<xsl:value-of select="'FX'"/>
</xsl:if>
</xsl:attribute>
</AllocationNumber>
</Header>
</MaxPostInvoice>
</xsl:template>

Related

Combine space-separated attributes in XSLT

The transform I'm working on mergers two templates that has attributes that are space-separated.
An example would be:
<document template_id="1">
<header class="class1 class2" />
</document>
<document template_id="2">
<header class="class3 class4" />
</document>
And after the transform I want it to be like this:
<document>
<header class="class1 class2 class3 class4" />
</document>
How to achieve this?
I have tried (writing from memory):
<xsl:template match="/">
<header>
<xsl:attribute name="class">
<xsl:for-each select=".//header">
<xsl:value-of select="#class"/>
</xsl:for-each>
</xsl:attribute>
</header>
</xsl:template>
But that appends them all together, but I need them separated... and would be awesome if uniqued as well.
Thank you
Try this template, which simply adds a space before all the headers, apart from the first
<xsl:template match="/">
<header>
<xsl:attribute name="class">
<xsl:for-each select=".//header">
<xsl:if test="position() > 1">
<xsl:text> </xsl:text>
</xsl:if>
<xsl:value-of select="#class"/>
</xsl:for-each>
</xsl:attribute>
</header>
</xsl:template>
If you want your classes without repetitions, then use the following XSLT:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<document>
<xsl:variable name="cls1">
<xsl:for-each select=".//header/#class">
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cls" select="exsl:node-set($cls1)"/>
<header>
<xsl:attribute name="class">
<xsl:for-each select="$cls/*[not(preceding-sibling::* = .)]">
<xsl:value-of select="."/>
<xsl:if test="position() < last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</header>
</document>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="txt"/>
<xsl:if test="$txt">
<xsl:if test="contains($txt, ' ')">
<cls>
<xsl:value-of select="substring-before($txt, ' ')"/>
</cls>
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="substring-after($txt, ' ')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($txt, ' '))">
<cls>
<xsl:value-of select="$txt"/>
</cls>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:transform>
In XSLT 1.0 it is much more difficult than in XSLT 2.0, but as you see,
it is possible.
Edit
A revised version using Muenchian grouping (inspired by comment from Michael):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:variable name="cls1">
<xsl:for-each select=".//header/#class">
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cls2" select="exsl:node-set($cls1)"/>
<xsl:key name="classKey" match="cls" use="."/>
<xsl:template match="/">
<document>
<header>
<xsl:attribute name="class">
<xsl:for-each select="$cls2/*[generate-id() = generate-id(key('classKey', .)[1])]">
<xsl:value-of select="."/>
<xsl:if test="position() < last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</header>
</document>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="txt"/>
<xsl:if test="$txt">
<xsl:if test="contains($txt, ' ')">
<cls>
<xsl:value-of select="substring-before($txt, ' ')"/>
</cls>
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="substring-after($txt, ' ')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($txt, ' '))">
<cls>
<xsl:value-of select="$txt"/>
</cls>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:transform>

xsl check if a root node contains any child nodes

Is there a way to determine if a a root level node contains any child nodes?
I have this code file that builds up a navigation menu for a drop-down menu, but for the root nodes that have no nodes below them I want to apply a different template to them:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/Home">
<xsl:apply-templates select="root" />
</xsl:template>
<xsl:template match="root">
<ul class="nav navbar-nav">
<xsl:apply-templates select="node">
<xsl:with-param name="level" select="0"/>
</xsl:apply-templates>
</ul>
</xsl:template>
<xsl:template match="node">
<xsl:param name="level" />
<xsl:choose>
<xsl:when test="$level=0">
<li>
<xsl:attribute name="class">
<xsl:if test="#breadcrumb = 1">active</xsl:if>
<xsl:if test="node">
<xsl:text> dropdown</xsl:text>
</xsl:if>
</xsl:attribute>
<xsl:choose>
<xsl:when test="#enabled = 1">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<xsl:attribute name="class">
<xsl:if test="node">
<xsl:text>dropdown-toggle</xsl:text>
</xsl:if>
</xsl:attribute>
<xsl:if test="node">
<xsl:attribute name="data-toggle">dropdown</xsl:attribute>
</xsl:if>
<xsl:value-of select="#text" />
<xsl:if test="node">
<b class="caret"></b>
</xsl:if>
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#text" />
</xsl:otherwise>
</xsl:choose>
<xsl:if test="node">
<ul class="dropdown-menu">
<xsl:apply-templates select="node">
<xsl:with-param name="level" select="$level + 1" />
</xsl:apply-templates>
</ul>
</xsl:if>
</li>
</xsl:when>
<xsl:otherwise>
<li>
<xsl:attribute name="class">
<xsl:if test="#breadcrumb = 1">active</xsl:if>
<xsl:if test="node">
<xsl:text> dropdown</xsl:text>
</xsl:if>
</xsl:attribute>
<xsl:choose>
<xsl:when test="#enabled = 1">
<a href="{#url}">
<xsl:value-of select="#text" />
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#text" />
</xsl:otherwise>
</xsl:choose>
</li>
<xsl:if test="node">
<!-- no extra level in default bootstrap -->
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Your question is not clear. If the root node does not have any child nodes, then the XML document is empty. Perhaps you meant the root element; there will be exactly one element like that and it's easy to see if it has any child nodes by using:
test="/*/node()"
in an xsl:if or xsl:when instruction.
Alternatively, you could use two templates - one matching a root element with child nodes:
<xsl:template match="/*[node()]">
and one for the other case:
<xsl:template match="/*[not(node())]">
You can use this XSLT-file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="/*[count(descendant::*) = 0]">
No siblings
</xsl:template>
<xsl:template match="/*[count(descendant::*) > 0]">
Has siblings
</xsl:template>
</xsl:stylesheet>
With an input XML file with one or more siblings of the root element like this
<?xml version="1.0"?>
<root>
<a />
</root>
it will output "Has siblings".
And with an input file with an empty root tag like this
<?xml version="1.0"?>
<root>
</root>
it will output "No siblings".
If I understand the question correctly, try adding the template rule
<xsl:template match="root[not(*)]"/>

jaxb transformerfactory xml to xml via xslt using generate-id for unique values

I have the following bit of code to transform one xml document into another via xslt
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new javax.xml.transform.stream.StreamSource(
this.getClass().getClassLoader().getResourceAsStream(ie.getXslFileName())));
transformer.transform(
new javax.xml.transform.stream.StreamSource(reader),
new javax.xml.transform.stream.StreamResult(stream));
output.put(fileName, stream.toByteArray());
It works perfectly if I do not attempt to select disctinct values. However, as soon as I put in the generate-id line and key it fails. In every other test tool I use the transformation is perfect so I am thinking it is a limitation in jaxb's transformer? Does anyone have any suggestion as to how I can select unique values that will work here AND enable jaxb transformation? OR, alternatively, is there another transformer I could use that would do the trick?
XSLT that works everywhere BUT here:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:key name="groupKey"
match="//questionGroup/externalCodes/externalCode/externalCode/text()"
use="." />
<xsl:template match="/">
<xsl:apply-templates select="submissionReport"/>
</xsl:template>
<xsl:template match="submissionReport" >
<component>
<structuredBody>
<xsl:for-each select="//questionResponse/question/questionGroup
/externalCodes/externalCode[governingBody
[internalCode='GVB-AHRQ-1']]
/externalCode/text()[generate-id() =
generate-id(key('groupKey',.)[1])]">
<component>
<section>
<entry>
<templateId>
<xsl:attribute name="root">
<xsl:value-of select="."/>
</xsl:attribute>
</templateId>
<organizer classCode="CLUSTER" moodCode="EVN">
<id nullFlavor="NA"/>
<statusCode code="completed"/>
<xsl:call-template name="groupedResponses">
<xsl:with-param name="groupInternalCode" select="."/>
</xsl:call-template>
</organizer>
</entry>
</section>
</component>
</xsl:for-each>
</structuredBody>
</component>
</xsl:template>
<xsl:template name="groupedResponses">
<xsl:param name="groupInternalCode"/>
<xsl:for-each select="//questionResponse[question[externalCodes
[externalCode[governingBody[internalCode='GVB-AHRQ-1'] and
not(externalCode='DE42') and not(externaCode='DE3') and
not(externalCode='DE46') and not(externalCode='DE49') and
not(externalCode='DE30')]] and
questionGroup[externalCodes[externalCode[governingBody[internalCode='GVB-AHRQ-1'] and
externalCode=$groupInternalCode]]]]]">
<component>
<observation>
<xsl:attribute name="classCode">OBS</xsl:attribute>
<xsl:attribute name="moodCode">EVN</xsl:attribute>
<templateId>
<xsl:attribute name="root">2.16.840.1.113883.3.263.1.11.3.100</xsl:attribute>
</templateId>
<xsl:for-each select="question/externalCodes/externalCode
[governingBody[internalCode='GVB-AHRQ-1'] or
governingBody[internalCode='GVB-CDCRACE-1'] or
governingBody[internalCode='GVB-HL7NULL-1'] or
governingBody[internalCode='GVB-HL7GENDER-1']]">
<!-- Question information -->
<code>
<xsl:attribute name="code">
<xsl:value-of select="externalCode"/>
</xsl:attribute>
<xsl:attribute name="displayName">
<xsl:value-of select="../../text"/>
</xsl:attribute>
<xsl:attribute name="codeSystem">
<xsl:value-of select="governingBody/externalCode"/>
</xsl:attribute>
<xsl:attribute name="codeSystemName">
<xsl:value-of select="governingBody/name"/>
</xsl:attribute>
</code>
<!-- Response information. Can be more than one -->
<xsl:for-each select="../../../response">
<value>
<xsl:choose>
<xsl:when test="responseOption">
<xsl:for-each select="responseOption/externalCodes/externalCode">
<value>
<xsl:attribute name="type">CD</xsl:attribute>
<xsl:attribute name="code">
<xsl:value-of select="externalCode"/>
</xsl:attribute>
<xsl:attribute name="displayName">
<xsl:value-of select="../../response"/>
</xsl:attribute>
<xsl:attribute name="codeSystem">
<xsl:value-of select="governingBody/externalCode"/>
</xsl:attribute>
<xsl:attribute name="codeSystemName">
<xsl:value-of select="governingBody/name"/>
</xsl:attribute>
</value>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<value>
<xsl:attribute name="type">ED</xsl:attribute>
<xsl:attribute name="mediaType">text/plain</xsl:attribute>
<xsl:value-of select="response"/>
</value>
</xsl:otherwise>
</xsl:choose>
</value>
</xsl:for-each>
</xsl:for-each>
</observation>
</component>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I decided to go with importing Saxon to enable XSLT 2.0. Seconds later grouping is perfect and works easily.

Removing the root element using xslt

I have an xml schema that has a structure like
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
.
.
.
</Level>
I want to remove the <Level> tag. How am I supposed to do that, with the help of xslt.
The standard answer to any "how do I keep most of my XML the same but tweak some little bits of it" question is "use an identity template and then override it for the specific things you want to change"
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- omit the <?xml?> line in the output, it won't be well-formed anyway -->
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
but as Mr Lister points out in the comments, this will leave you with something that is not well formed XML, as it will have more than one document element.
When I apply this stylesheet on the input XML
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
</Level>
it produces the result
<Level1>...data...</Level1>
<Level2>...data...</Level2>
If you want to store all child elements of the document element in a variable, you can do something like:
<xsl:variable name="myVar" select="/*/*"/>
If, however, you want to the stylesheet to produce a string, this might be a solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="*">
<!-- We write the opening tag -->
<xsl:value-of select="concat('<',local-name())"/>
<!-- Now, all attributes -->
<xsl:apply-templates select="#*"/>
<!-- Depending on whether we have an empty element or not,
we're adding the content or closing it immediately -->
<xsl:choose>
<xsl:when test="node()">
<!-- Close opening tag -->
<xsl:value-of select="'>'"/>
<!-- Add the content -->
<xsl:apply-templates select="node()"/>
<!-- Write closing tag -->
<xsl:value-of select="concat('</',local-name(),'>')"/>
</xsl:when>
<xsl:otherwise>
<!-- Create empty element by closing tag immediately -->
<xsl:value-of select="'/>'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*">
<!-- Write an attribute -->
<xsl:value-of select="concat(' ',local-name(),'="',.,'"')"/>
</xsl:template>
</xsl:stylesheet>
It produces text (and therefore you won't get non-well-formed XML). It's a little over-simplified because it does not handle namespaces, comments, processing instructions and quotes in attributes. If your input XML contains any of these, you'd have to refine the stylesheet.
I have created a new XSLT definition which fulfill my requremtn with the help of your code
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="text" omit-xml-declaration="yes"/>
<xsl:variable name="nl">
<xsl:text/>
</xsl:variable>
<xsl:variable name="tb">
<xsl:text/>
</xsl:variable>
<xsl:template match="/*">
<!-- Open the root array -->
<xsl:text>[{</xsl:text>
<xsl:value-of select="$nl"/>
<!-- Process all the child nodes of the root -->
<xsl:apply-templates mode="detect" select="*">
<xsl:with-param name="indent" select="$tb"/>
</xsl:apply-templates>
<!-- Close the root array -->
<xsl:value-of select="$nl"/>
<xsl:text>}]</xsl:text>
</xsl:template>
<xsl:template match="*" mode="detect">
<xsl:choose>
<xsl:when test="name(preceding-sibling::*[1]) = name(current()) and name(following-sibling::*[1]) != name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>]</xsl:text>
<xsl:if test="count(following-sibling::*[name() != name(current())]) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="name(preceding-sibling::*[1]) = name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:if test="name(following-sibling::*) = name(current())">, </xsl:if>
</xsl:when>
<xsl:when test="following-sibling::*[1][name() = name(current())]">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>" : [</xsl:text>
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>, </xsl:text>
</xsl:when>
<xsl:when test="count(./child::*) > 0 or count(#*) > 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : [<xsl:apply-templates
mode="obj-content" select="."/>
<xsl:if test="count(following-sibling::*) > 0">], </xsl:if>
</xsl:when>
<xsl:when test="count(./child::*) = 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:apply-templates select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="obj-content">
<xsl:text>{</xsl:text>
<xsl:apply-templates mode="attr" select="#*"/>
<xsl:if test="count(#*) > 0 and (count(child::*) > 0 or text())">, </xsl:if>
<xsl:apply-templates mode="detect" select="./*"/>
<xsl:if test="count(child::*) = 0 and text() and not(#*)">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="count(child::*) = 0 and text() and #*">
<xsl:text>: "</xsl:text>
<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:text>}</xsl:text>
<xsl:if test="position() < last()">, </xsl:if>
</xsl:template>
<xsl:template match="#*" mode="attr">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="position() < last()">,</xsl:if>
</xsl:template>
<xsl:template match="node/#TEXT | text()" name="removeBreaks">
<xsl:param name="pText" select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="not(contains($pText, '
'))">
<xsl:copy-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring-before($pText, '
'), ' ')"/>
<xsl:call-template name="removeBreaks">
<xsl:with-param name="pText" select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSLT blank xmlns="" after transform

I'm using an XSLT to transform from one XML standard to another. The particular resulting XML standard contains a root element which is part of a namespace and a child node which is part of another namepsace.
The transform successfully reflects these namespaces but the child's child now contains a blank xmlns attribute. How can I prevent this xmlns=""?
XSLT Snippet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:apply-templates select="REQUEST_GROUP" />
</xsl:template>
<xsl:template match="REQUEST_GROUP">
<ONCORE_ERECORD xmlns="http://test.com">
<xsl:apply-templates select="REQUEST/PRIA_REQUEST/PACKAGE"/>
<PAYMENT PaymentType="ACH" />
<TRANSACTION_INFO _AgentKey="" _AgentPassword="" />
</ONCORE_ERECORD>
</xsl:template>
<xsl:template match="PACKAGE">
<DOCUMENT_RECORDATION xmlns="http://test2.org">
<xsl:apply-templates select="PRIA_DOCUMENT"/>
</DOCUMENT_RECORDATION>
</xsl:template>
<xsl:template match="PRIA_DOCUMENT">
<PRIA_DOCUMENT _PRIAVersion="1.2">
<xsl:attribute name="_Type">
<xsl:value-of select="#RecordableDocumentType"/>
</xsl:attribute>
<xsl:attribute name="_Code"/>
<xsl:apply-templates select="GRANTOR" />
<xsl:apply-templates select="GRANTEE" />
<xsl:choose>
<xsl:when test="count(PROPERTY) = 0">
<PROPERTY>
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="#StreetAddress"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="#StreetAddress2"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="#City"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="#State"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="#PostalCode"/>
</xsl:attribute>
<xsl:attribute name="_County">
<xsl:value-of select="#County"/>
</xsl:attribute>
<xsl:apply-templates select="LEGAL_DESCRIPTION"/>
</PROPERTY>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PROPERTY" />
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(PARTIES) = 0">
<PARTIES>
<_RETURN_TO_PARTY _UnparsedName="" _StreetAddress="" _StreetAddress2="" _City="" _State="" _PostalCode="" />
</PARTIES>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PARTIES" />
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="EXECUTION" />
<xsl:apply-templates select="CONSIDERATION" />
<xsl:apply-templates select="RECORDABLE_DOCUMENT/_ASSOCIATED_DOCUMENT" />
<xsl:apply-templates select="EMBEDDED_FILE" />
</PRIA_DOCUMENT>
Source XML:
<REQUEST_GROUP PRIAVersionIdentifier="2.4">
<REQUEST>
<PRIA_REQUEST _Type="RecordDocuments">
<PACKAGE>
<PRIA_DOCUMENT PRIAVersionIdentifier="2.4" RecordableDocumentSequenceIdentifier="1" RecordableDocumentType="Mortgage">
Resulting XML:
<?xml version="1.0" encoding="utf-8"?>
<ONCORE_ERECORD xmlns="http://test.com">
<DOCUMENT_RECORDATION xmlns="http://test2.org">
<PRIA_DOCUMENT _PRIAVersion="1.2" _Type="Mortgage" _Code="" xmlns="">
This is happening because PRIA_DOCUMENT is in the default namespace, while its parent DOCUMENT_RECORDATION is in a non-default namespace. You must put the PRIA_DOCUMENT in the same namespace as its parent, otherwise the serializer is required to generate xmlns="".
.
.
<xsl:template match="PRIA_DOCUMENT">
<PRIA_DOCUMENT _PRIAVersion="1.2" xmlns="http://pria.org">
.
.
.
See Michael Kay's "XSLT 2.0 and XPATH 2.0, 4th edition", page 475 where he discusses this exact situation.
I found a solution that worked, though it may not have been the most efficient way to achieve the desired results.
I simply changed all literal element declarations to:
</xsl:element>
and declared the namespace. The resulting xslt is as follows:
<xsl:template match="REQUEST_GROUP">
<xsl:element name="ONCORE_ERECORD" namespace="http://test.com">
<xsl:apply-templates select="REQUEST/PRIA_REQUEST/PACKAGE"/>
<xsl:element name="PAYMENT" namespace="http://test.com">
<xsl:attribute name="PaymentType">
<xsl:value-of select="'ACH'"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="TRANSACTION_INFO" namespace="http://test.com">
<xsl:attribute name="_AgentKey">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_AgentPassword">
<xsl:value-of select="''"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="PACKAGE">
<xsl:element name="DOCUMENT_RECORDATION" namespace="http://test2.org">
<xsl:apply-templates select="PRIA_DOCUMENT"/>
</xsl:element>
</xsl:template>
<xsl:template match="PRIA_DOCUMENT">
<xsl:element name="PRIA_DOCUMENT" namespace="http://test2.org">
<xsl:attribute name="_PRIAVersion">
<xsl:value-of select="'1.2'"/>
</xsl:attribute>
<xsl:attribute name="_Type">
<xsl:value-of select="#RecordableDocumentType"/>
</xsl:attribute>
<xsl:attribute name="_Code"/>
<xsl:apply-templates select="GRANTOR" />
<xsl:apply-templates select="GRANTEE" />
<xsl:choose>
<xsl:when test="count(PROPERTY) = 0">
<xsl:element name="PROPERTY" namespace="http://test2.org">
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="#StreetAddress"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="#StreetAddress2"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="#City"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="#State"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="#PostalCode"/>
</xsl:attribute>
<xsl:attribute name="_County">
<xsl:value-of select="#County"/>
</xsl:attribute>
<xsl:apply-templates select="LEGAL_DESCRIPTION"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PROPERTY" />
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(PARTIES) = 0">
<xsl:element name="PARTIES" namespace="http://test2.org">
<xsl:element name="_RETURN_TO_PARTY" namespace="http://test2.org">
<xsl:attribute name="_UnparseName">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="''"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PARTIES" />
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="EXECUTION" />
<xsl:apply-templates select="CONSIDERATION" />
<xsl:apply-templates select="RECORDABLE_DOCUMENT/_ASSOCIATED_DOCUMENT" />
<xsl:apply-templates select="EMBEDDED_FILE" />
</xsl:element>
</xsl:template>
I was having a similar issue even declaring the namespace on the child elements but was still ending up with
xmlns=""
I thought it was due to the xslt transformation but the string result of the transform was correct and it was when I then transformed the string to a org.w3c.dom.Document that the default namespaces were being added.
Making the DocumentBuilderFactory namespace aware fixed this
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document metadataDOM = db.parse(new ByteArrayInputStream(stringWriter.toString().getBytes()));
Put the calling template and the applied template in the same namespace.
You're re-defining the default namespace with each of those nodes with the 'xmlns=' declaration. Because the PRIA_DOCUMENT doesn't have a namespace, the output needs to redeclare it as empty, or it would have the same namespace as it's parent. I'd recommend adding in a named namespace to those elements that have one defined, for example:
<pria:DOCUMENT_RECORDATION xmlns:pria="http://pria.org">
and
<as:ONCORE_ERECORD xmlns:as="http://aptitudesolutions.com">
With these named namespaces in place, the blank declaration on the PRIA_DOCUMENT element becomes unnecessary, and is not added.