XPath working with empty nodes - java

If I got this XML(first tag is empty)
<messages>
<message>
<name>Kiro</name>
<acc></acc>
<date>20120506</date>
<template>TMPL_ACC_UP</template>
</message>
<message>
<name>Third</name>
<acc>555</acc>
<date>20120507</date>
<template>TMPL_ACC_UP</template>
</message>
</messages>
Now i take all accpunts with this XPath expression: "//message/acc/text()", and get node list of 1(empty nodes not included). I want to get empty string for the empty tag ['555','']. How I can achieve this? Thanks!

In XPath 2.0 a one-liner like this can be used:
/*/message/acc/string()
However, this cannot be produced as the result of evaluating a single XPath 1.0 expression.
If you must use XPath 1.0, the solution is to select all /*/message/acc elements and then in the PL that is hosting XPath, output the string value of each of the selected elements.
For example, this XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>[</xsl:text><xsl:apply-templates/><xsl:text>]</xsl:text>
</xsl:template>
<xsl:template match="acc">
<xsl:if test="preceding::acc">, </xsl:if>
<xsl:text>'</xsl:text><xsl:value-of select="."/><xsl:text>'</xsl:text>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided XML document:
<messages>
<message>
<name>Kiro</name>
<acc></acc>
<date>20120506</date>
<template>TMPL_ACC_UP</template>
</message>
<message>
<name>Third</name>
<acc>555</acc>
<date>20120507</date>
<template>TMPL_ACC_UP</template>
</message>
</messages>
produces the wanted, correct result:
['', '555']

I would loop over nodes and extract their text as value
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>[
<xsl:for-each select="//message/acc">
'<xsl:value-of select="text()"/>',
</xsl:for-each>]
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Result: [ '', '555', ]

Related

An empty line in csv output file using xslt

Hi i am have an XML file and trying to convert it using xslt. But issue is i get a white line before the output.How do i eliminate it.
Below is my XML
<?xml version="1.0" encoding="UTF-8"?>
<UserInfo xmlns="http://XXXXXX">
<User>
<UserName>MNO</UserName>
<Userid>1234</Userid>
<address>xyz</address>
<city>ABC</city>
<state>XX</state>
<zip>000000</zip>
</User>
<User>
<UserName>DEF</UserName>
<Userid>4567</Userid>
<address>IJK</address>
<city>GHI</city>
<state>XX</state>
<zip>000000</zip>
</User>
</UserInfo>
below is my xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" >
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
UserName,Userid,address,city,state,zip
<xsl:for-each select="//User">
<xsl:value-of select="concat(UserName,',',Userid,',',address,',',city,',',state,',',zip,'
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When ever i run the above i get a white line first and then the output.
vendorName,vendorId,vendorTaxId,addressLine1,addressLine2,city,state,zip
MNO,1234,xyz,ABC,XX,000000
DEF,4567,IJK,GHI,XX,000000
I get the white space above the nodes(UserName,UserId etc)
Use xsl:text to output literal text, e.g.:
<xsl:template match="/">
<xsl:text>UserName,Userid,address,city,state,zip
</xsl:text>
<xsl:for-each select="//User">
<xsl:value-of select="concat(UserName,',',Userid,',',address,',',city,',',state,',',zip,'
')"/>
</xsl:for-each>
</xsl:template>
The way you have it now, the entire text node is written to the output - including the line break between <xsl:template match="/"> and UserName.

Mapping two sources to one output

I want to performs XSLT on two sources.
Sourch_1.xml:
<Root>
<record>
<PolicyNumber>1</PolicyNumber>
<FirdsName>aa</FirdsName>
<LastName>aa</LastName>
</record>
<record>
<PolicyNumber>2</PolicyNumber>
<FirdsName>bb</FirdsName>
<LastName>bb</LastName>
</record>
Sourch_2.xml:
<Root>
<record>
<policy>
<PolicyNumber>1</PolicyNumber>
<city>aaCity</city>
<street>aaStreet</street>
</policy>
<policy>
<PolicyNumber>2</PolicyNumber>
<city>bbCity</city>
<street>bbStreet</street>
</policy>
</record>
I want the output to create tags which combine data from two sources based on PolicyNumber.
I want my output to be:
output.xml:
<Root>
<record_data>
<PolicyNumber>1</PolicyNumber>
<FirdsName>aa</FirdsName>
<LastName>aa</LastName>
<city>aaCity</city>
<street>aaStreet</street>
</record_data>
<record_data>
<PolicyNumber>2</PolicyNumber>
<FirdsName>bb</FirdsName>
<LastName>bb</LastName>
<city>bbCity</city>
<street>bbStreet</street>
</record_data>
How can i accomplish that using XSLT?
To process more than one XML input file, use the document() function. Make sure that additional XML files are in the folder where you put the XSLT stylesheet, too.
The line that deserves your attention is the following:
<xsl:for-each select="document('double2.xml')/root/record/policy[./PolicyNumber=current()/PolicyNumber]">
To begin with, document('double2.xml') opens the second XML file (I have dubbed it "double2.xml"). For the policy elements in this second XML file it checks whether their PolicyNumber is equal to the PolicyNumber of the record element from the first XML file that is being processed
I have slightly modified your input to make it well-formed (lowercased the root element and added its closing tag). Note that there is also a typo in it and you probably meant to write "FirstName" instead of "FirdsName".
Stylesheet
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="record">
<record_data>
<xsl:copy-of select="*"/>
<xsl:for-each select="document('double2.xml')/root/record/policy[./PolicyNumber=current()/PolicyNumber]">
<xsl:copy-of select="city|street"/>
</xsl:for-each>
</record_data>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<record_data>
<PolicyNumber>1</PolicyNumber>
<FirdsName>aa</FirdsName>
<LastName>aa</LastName>
<city>aaCity</city>
<street>aaStreet</street>
</record_data>
<record_data>
<PolicyNumber>2</PolicyNumber>
<FirdsName>bb</FirdsName>
<LastName>bb</LastName>
<city>bbCity</city>
<street>bbStreet</street>
</record_data>
</root>
Something like below will work for you.
NOTE : I have not ran this code. where I check not(policy) you might do some change in xpath like not(./policy)
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="Root/record">
<xsl:choose>
<xsl:when test="not(policy)">
<record_data>
<PolicyNumber><xsl:value-of select="PolicyNumber"/></PolicyNumber>
<FirdsName><xsl:value-of select="FirdsName"/></FirdsName>
<LastName><xsl:value-of select="LastName"/></LastName>
<city><xsl:value-of select="city"/></city>
<street><xsl:value-of select="street"/></street>
</record_data>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
<record_data>
<PolicyNumber><xsl:value-of select="policy/PolicyNumber"/></PolicyNumber>
<FirdsName><xsl:value-of select="policy/FirdsName"/></FirdsName>
<LastName><xsl:value-of select="policy/LastName"/></LastName>
<city><xsl:value-of select="policy/city"/></city>
<street><xsl:value-of select="policy/street"/></street>
</record_data>
</xsl:choose>
</xsl:for-each>

XSLT dont add XMLNS to elements

I have a simple HTML fragment similar to this:
link
I need to transform it to
<abc:href var="123">link</abc:href>
I do it with XSLT, so I had to add the namespace in xsl:stylesheet
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abc="http://abc.ru">
It works almost fine, unfortunately the XSLT transform keeps on adding a XMLNS to the output, like here:
<abc:href var="123" xmlns:abc="http://abc.ru">link</abc:href>
I don't need the xmlns definition, can I remove it?
Although it really goes against the grain, and I advise strongly against it, if you need to produce this malformed XML, then you can use an instruction like...
<xsl:value-of disable-output-escaping="yes" select="
concat('<abc:href var="',$href,'">',$link,'</abc:href>')
"/>
... where $href and $link are place-markers for the appropriate expression.
Update
In response to the OP's comment, one could use a template like this...
<xsl:template match="a">
<xsl:value-of disable-output-escaping="yes" select="
concat('<abc:href var="',#href,'">',.,'</abc:href>')
"/>
</xsl:template>
This ugly solution should be used only as a last resort. A much better solution would be to use XSLT to produce your WHOLE document, not just an invalid fragment of it. This way you document would be well formed and you could bring to bear the full power and simplicity of XSLT.
It works almost fine, unfortunately the XSLT transform keeps on adding
a XMLNS to the output, like here:
<abc:href var="123" xmlns:abc="http://abc.ru">link</abc:href>
I don't need the xmlns definition, can I remove it?
The wanted removal of the namespace declaration would produce a (namespace-)non-well-formed XML document and for this reason the XSLT processor adds the namespace declaration -- as required by the W3C XSLT specifications.
You can cause these namespace declarations to "disappear" by placing the namespace declaration on a common ancestor (such as the top element of the generated XML document).
Here is a complete example:
<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:template match="/*">
<top xmlns:abc="http://abc.ru">
<xsl:apply-templates/>
</top>
</xsl:template>
<xsl:template match="a[#href]">
<xsl:element name="abc:href" namespace="http://abc.ru">
<xsl:attribute name="var">
<xsl:apply-templates/>
</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following document:
<html>
link1
link2
link3
link4
</html>
the wanted, correct result is produced:
<top xmlns:abc="http://abc.ru">
<abc:href var="link1"/>
<abc:href var="link2"/>
<abc:href var="link3"/>
<abc:href var="link4"/>
</top>
This is sad, but I really need an invalid xml
XSLT is designed to prevent you producing bad XML. If you want to produce bad XML, don't use XSLT.
Try it with exclude-result-prefixes, like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abc="http://abc.ru"
exclude-result-prefixes="abc">
<xsl:template match="/">
<xsl:apply-templates select="#* | node()"/>
</xsl:template>
<xsl:template match="a">
<href var="{#href}"><xsl:value-of select="."/></href>
</xsl:template>
</xsl:stylesheet>

Create xmlns attribute in the XML using XSLT Transformation

I am trying to add the xmlns attribute to the resulting XML with a value passed by parameter during XSLT transformation using JDK Transformer (Oracle XML v2 Parser or JAXP) but it always defaults to http://www.w3.org/2000/xmlns/
My source XML
<test/>
My XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://example.com">
<xsl:param name="myNameSpace" select="'http://neilghosh.com'"/>
<xsl:template match="/">
<process>
<xsl:attribute name="xmlns:neil">
<xsl:value-of select="$myNameSpace"/>
</xsl:attribute>
</process>
</xsl:template>
</xsl:stylesheet>
My Result
<?xml version="1.0"?>
<process xmlns="http://www.w3.org/2000/xmlns/" xmlns:neil="neilghosh.com">
</process>
My Desired Result
<?xml version="1.0"?>
<process xmlns="http://example.com" xmlns:neil="neilghosh.com">
</process>
Firstly, in the XSLT data model, you don't want to create an attribute node, you want to create a namespace node.
Namespace nodes are usually created automatically: if you create an element or attribute in a particular namespace, the requisite namespace node (and hence, when serialized, the namespace declaration) are added automatically by the processor.
If you want to create a namespace node that isn't necessary (because it's not used in the name of any element or attribute) then in XSLT 2.0 you can use xsl:namespace. If you're stuck with XSLT 1.0 then there's a workaround, that involves creating an element in the relevant namespace and then copying its namespace node:
<xsl:variable name="ns">
<xsl:element name="neil:dummy" namespace="{$param}"/>
</xsl:variable>
<process>
<xsl:copy-of select="$ns/*/namespace::neil"/>
</process>
Michael Kay provided you with the correct answer, but based on your comments, you aren't sure how to use it in your transformation.
Here is a complete transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNamespace" select="'neilghosh.com'"/>
<xsl:variable name="vDummy">
<xsl:element name="neil:x" namespace="{$pNamespace}"/>
</xsl:variable>
<xsl:template match="/*">
<xsl:element name="process" namespace="http://example.com">
<xsl:copy-of select="namespace::*"/>
<xsl:copy-of select="ext:node-set($vDummy)/*/namespace::*[.=$pNamespace]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<test/>
the wanted, correct result is produced:
<process xmlns="http://example.com" xmlns:neil="neilghosh.com" />
Namespace declarations in XML are not attributes even though they look like attributes. In XSLT 2.0 you can use <xsl:namespace name="neil" select="$myNameSpace" /> to add a namespace declaration to the result tree dynamically but that feature is not available in XSLT 1.0.
Don't try to create "xmlns" attributes yourself. Create the namespaces in the XSLT and they will be done automatically.
This XSLT works (tested with Saxon 9.4):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:neil="neilghosh.com"
xpath-default-namespace="http://example.com"
xmlns="http://example.com" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="myDynamicNamespace" select="'http://neilghosh.com'"/>
<xsl:template match="/">
<xsl:element name="process">
<xsl:namespace name="neil" select="$myDynamicNamespace"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
And gives the following output:
<?xml version="1.0" encoding="UTF-8"?>
<process xmlns="http://example.com" xmlns:neil="http://neilghosh.com"/>
Finally got an workaround which worked with my XSLT Processor (Oracle XML V2 Parser)
I had to transform it to a DOM Document and then persist that DOM to filesystem instead of outputting directly to StreamResult
I used DOMResult in the transform method
Following XSLT fragment worked but there was an extra xmlns:xmlns="http://www.w3.org/2000/xmlns/" which was probably absorbed by Document and did not appear in the final output when I persisted to file system.
<process>
<xsl:attribute name="xmlns">
<xsl:value-of select="'http://example.com'"/>
</xsl:attribute>
<process>
I know this is not the best way to do but given the parse constraint this is the only choice I have now.

Embedding XSL Stylesheet into XML

I have the following XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="http://www.fakedomain.com/sally.xsl"?>
And the following content in sally.xsl:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="documentcollection/document">
<p>
<xsl:for-each select="rss/channel/item">
<xsl:value-of select="title"/><br />
<xsl:value-of select="description"/><br />
<xsl:value-of select="link"/><br />
</xsl:for-each>
</p>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
However, the browser displays the XML as though the XSL line is not present. Do you know why the browser is ignoring the XSL stylesheet? Is the style sheet wrong?
Thanks
I have the following XML:
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml-stylesheet type="text/xsl" href="http://www.fakedomain.com/sally.xsl"?>
This isn't a well-formed XML document (no top element present), so it isn't much of a surprize the browser doesn't treat it as such.
Solution:
Update your "XML" to a really well-formed XML document, something like:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="file:///c:/temp/delete/xxx.xsl"?>
<t/>
With this stylesheet in c:\temp\delete\xxx.xsl:
<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:template match="/">
XXX
</xsl:template>
</xsl:stylesheet>
when the XML file is opened with IE, the browser displays the result of the transformation:
XXX
Looks like your not closing off one of your for-each tags?
the xsl:for-each loop of select="rss/channel/item" is not closed.
It could be same origin policy security restrictions. If your XML and XSLT are not hosted in the same location, then the browser may be refusing to fetch the XSLT and apply it to your XML file.

Categories