Split element within xslt file - xslt

I have the following input
UK/006/10
US/004/12
And wanted to get the following output.
Country: UK
Code:006
Line: 10
Country: US
Code:004
Line:12
I tried to use following, but I need something simple, like split function. Can someone help on this?
<xsl:value-of select="substring-before(substring-after($User_def_type_4, '/'), '/')" />

Using Invisible XML, you could define a grammar for your text data to map it to XML, then an extension function library like the CoffeeSacks library to Saxon Java can be used in XSLT to parse and post-process the text so that with e.g. the XML input being
<data>UK/006/10
US/004/12</data>
and the XSLT being
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cs="http://nineml.com/ns/coffeesacks"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:template match="data">
<xsl:apply-templates select="cs:parse-string(cs:grammar-string($grammar), .)/node()"/>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
<xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:template>
<xsl:param name="grammar" as="xs:string" expand-text="no">Countries = Country*.
Country = Name, -'/', Code, -'/', Line, #A?.
Name = ['A'-'Z'],['A'-'Z'].
Code = ['0'-'9'],['0'-'9'],['0'-'9'].
Line = ['0'-'9'],['0'-'9'].</xsl:param>
</xsl:stylesheet>
you get e.g.
<?xml version="1.0" encoding="UTF-8"?>
<Countries>
<Country>
<Name>UK</Name>
<Code>006</Code>
<Line>10</Line>
</Country>
<Country>
<Name>US</Name>
<Code>004</Code>
<Line>12</Line>
</Country>
</Countries>
<!--Run with SAXON HE 11.3 -->
Online sample using Saxon HE 11 Java and the named CoffeeSacks library.

Related

Can't read the namespaces and attribute in xslt

I know that this is simple problem. I'm still learning and getting familiarize with the XSLT coding. I have a problem in my XSLT and I don't know if I did it correctly. I need to get the value from the input file and store it in the new element tag name and that I don't need to populate the namespaces and attributes what's on the parent root element. I did a research about this and I saw many references but I can't apply it. The XSLT(v02) that I made is working fine (just copy from the references) if the root element doesn't have any namespaces and attributes. But, when I put a namespaces and attribute, no output populated.
Input file
<Root xmlns="http://abcd.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" releaseID="9.2" versionID="2.12.3" xsi:schemaLocation="abcd.com abcd.xsd">
<Element>
<Field>AAAAA</Field>
</Element>
<Element>
<Field>BBBBB</Field>
</Element>
<Element>
<Field>CCCCC</Field>
</Element>
xslt file
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRecord>
<xsl:for-each select="Root/Element">
<NewTransaction>
<Position>
<xsl:value-of select="position()"/>
</Position>
<TransactionID>
<xsl:value-of select="Field"/>
</TransactionID>
</NewTransaction>
</xsl:for-each>
</NewRecord>
</xsl:template>
output generated
<NewRecord/>
My expected output should look like this:
<NewRecord>
<NewTransaction>
<Position>1</Position>
<TransactionID>AAAAA</TransactionID>
</NewTransaction>
<NewTransaction>
<Position>2</Position>
<TransactionID>BBBBB</TransactionID>
</NewTransaction>
<NewTransaction>
<Position>3</Position>
<TransactionID>CCCCC</TransactionID>
</NewTransaction>
I think the problem is in the <xsl:template match="/">, I'm still confused on the nodes that I need to put. Thank you for your help.
If you are really using XSLT 2.0, you only need to add:
xpath-default-namespace="http://abcd.com"
to the stylesheet tag, and leave everything else as is.
If you're using xslt 1.0, you'll have to declare the same namespace in the stylesheet, and use the prefix you map to the namespace to qualify the names of the elements:
The prefix can be whatever you want. I picked abcd to match your example, but it could be any legal identifier.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abcd="http://abcd.com">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRecord>
<xsl:for-each select="abcd:Root/abcd:Element">
<NewTransaction>
<Position>
<xsl:value-of select="position()"/>
</Position>
<TransactionID>
<xsl:value-of select="abcd:Field"/>
</TransactionID>
</NewTransaction>
</xsl:for-each>
</NewRecord>
</xsl:template>
</xsl:stylesheet>

XPath 3.0 Serialize without Namespaces in Scope

While answering this question, it occurred to me that I know how to use the XSLT 3.0 (XPath 3.0) serialize() function, but that I do not know how to avoid serialization of namespaces that are in scope. Here is a minimal example:
XML Input
<?xml version="1.0" encoding="UTF-8" ?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
<cichlid id="1">
<name>Zeus</name>
<color>gold</color>
<teeth>molariform</teeth>
<breeding-type>lekking</breeding-type>
</cichlid>
</ci:cichlids>
XSLT 3.0 Stylesheet
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization"
xmlns:ci="http://www.cichlids.com">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/ci:cichlids/cichlid">
<xsl:variable name="serial-params">
<output:serialization-parameters>
<output:omit-xml-declaration value="yes"/>
</output:serialization-parameters>
</xsl:variable>
<xsl:value-of select="serialize(., $serial-params/*)"/>
</xsl:template>
</xsl:stylesheet>
Actual Output
<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
<cichlid xmlns:ci="http://www.cichlids.com" id="1">
<name>Zeus</name>
<color>gold</color>
<teeth>molariform</teeth>
<breeding-type>lekking</breeding-type>
</cichlid>
</ci:cichlids>
The serialization process included the namespace declaration that is in scope for the cichlid element, although it is not used on this element. I would like to remove this declaration and make the output look like
Expected Output
<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
<cichlid id="1">
<name>Zeus</name>
<color>gold</color>
<teeth>molariform</teeth>
<breeding-type>lekking</breeding-type>
</cichlid>
</ci:cichlids>
I know how to modify the cichlid element, removing the namespaces in scope, and serialize this modified element instead. But this seems a rather cumbersome solution. My question is:
What is a canonical way to serialize an XML element using the serialize() function without also serializing unused namespace declarations that are in scope?
Testing with Saxon-EE 9.6.0.7 from within Oxygen.
Serialization will always give you a faithful representation of the data model that you are serializing. If you want to modify the data model, that's called transformation. Run a transformation to remove the unwanted namespaces, then serialize the result.
Michael Kay already gave the correct answer and I have accepted it. This is just to flesh out his comments. By
Run a transformation to remove the unwanted namespaces, then serialize the result.
he means applying a transformation like the following before calling serialize():
XSLT Stylesheet
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization"
xmlns:ci="http://www.cichlids.com">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="cichlid-without-namespace">
<xsl:copy-of copy-namespaces="no" select="/ci:cichlids/cichlid"/>
</xsl:variable>
<xsl:template match="/ci:cichlids/cichlid">
<xsl:variable name="serial-params">
<output:serialization-parameters>
<output:omit-xml-declaration value="yes"/>
</output:serialization-parameters>
</xsl:variable>
<xsl:value-of select="serialize($cichlid-without-namespace, $serial-params/*)"/>
</xsl:template>
</xsl:stylesheet>
XML Output
<?xml version="1.0" encoding="UTF-8"?>
<ci:cichlids xmlns:ci="http://www.cichlids.com">
<cichlid id="1">
<name>Zeus</name>
<color>gold</color>
<teeth>molariform</teeth>
<breeding-type>lekking</breeding-type>
</cichlid>
</ci:cichlids>

How to print < and > symbols which are part of text..?

I just can't figure out a way to output string something like :
<xml version="1.0" encoding="UTF-8">
this is what i tried:
<xsl:variable name="lessThan" select="<"/>
<xsl:variable name="GreaterThan" select=">"/>
<xsl:value-of select="$lessThan"/>
<xsl:text>xml version="1.0" encoding="UTF-8"</xsl:text>
<xsl:value-of select="$GreaterThan"/>
but this is the output i'm getting:
<xml version="1.0" encoding="UTF-8">
I also tried doin something like this:
<xsl:text><xml version="1.0" encoding="UTF-8"></xsl:text>
but the editor simply doesn't let me do this.It throws an error to match with end tag
PS:I am not well versed in xslt so Do please reply even if the question sounds naive.
try this:
<xsl:text disable-output-escaping="yes"><xml version="1.0" encoding="UTF-8"></xsl:text>
To make your test xslt working you can use disable-output-escaping = "yes"
Changed xlst:
<xsl:variable name="lessThan" select="'<'"/>
<xsl:variable name="GreaterThan" select="'>'"/>
<xsl:value-of disable-output-escaping = "yes" select="$lessThan"/>
<xsl:text>xml version="1.0" encoding="UTF-8"</xsl:text>
<xsl:value-of disable-output-escaping = "yes" select="$GreaterThan"/>
Update:
Only a guess you try to generate a xml declaration.
<?xml version="1.0" encoding="utf-8"?>
This should be done with xsl:output
<xsl:output method="xml" encoding="utf-8"/>
You should not be trying to produce the XML declaration manually. It should be generated automatically by the XSLT as long as you specify the output method as XML and do not specify omit-xml-declaration="yes":
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="utf-8" />
<xsl:template match="/">
<root />
</xsl:template>
</xsl:stylesheet>
When this XSLT is run on any input, the result is:
<?xml version="1.0" encoding="utf-8"?>
<root />
Put this <xsl:text disable-output-escaping="yes"><</xsl:text>

XSLT transform error

I have the following xml:
<RootNode xmlns="http://someurl/path/path/path">
<Child1>
<GrandChild1>Value</GrandChild1>
<!-- Lots more elements in here-->
</Child1>
</RootNode>
I have the following xslt:
<xsl:stylesheet version="1.0" xmlns="http://someurl/path/path/path" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="RootNode/Child1">
<NewNodeNameHere>
<xsl:value-of select="GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode >
</xsl:template>
</xsl:stylesheet>
The problem: this is the my result:
<?xml version="1.0" encoding="utf-8"?>
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1 />
</NewRootNode>
I am expecting to see:
<?xml version="1.0" encoding="utf-8"?>
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<NewNodeNameHere>Value</NewNodeNameHere>
<!-- Other new elements with values from the xml file -->
</NewChild1>
</NewRootNode>
I am missing of the information inside of NewChild1 that should be there.
I think my for-each select is correct, so the only thing I can think of is that there is a problem with the namespace in the Xml and the namespace in the xslt. Can anybody see what I'm doing wrong?
The problem is caused by the namespaces.
Since the xml defines xmlns="http://someurl/path/path/path", it is not in the default namespace anymore.
You can define that namespace with an name like xmlns:ns="http://someurl/path/path/path" in the xsl, and then use that name in the XPath expression.
The following works for me:
<xsl:stylesheet version="1.0" xmlns:ns="http://someurl/path/path/path" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="ns:RootNode/ns:Child1">
<NewNodeNameHere>
<xsl:value-of select="ns:GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode >
</xsl:template>
</xsl:stylesheet>
The stylesheet namespace should be http://www.w3.org/1999/XSL/Transform instead of http://someurl/path/path/path.
Also, since the input XML uses a namespace all your XPath expressions should be namespace-qualified:
<xsl:template match="/" xmlns:ns1="http://someurl/path/path/path">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="ns1:RootNode/ns1:Child1">
<NewNodeNameHere>
<xsl:value-of select="ns1:GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode>
</xsl:template>

xpath-function max doesn't work

For unknown reason max function doesn't work.
XML input file:
test.xml
<?xml version="1.0" encoding="UTF-8"?>
<numbers>
<number>3</number>
<number>5</number>
<number>10</number>
<number>1</number>
</numbers>
XSL input file
test.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/02/xpath-functions"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >
<xsl:output method="xml" indent="yes" />
<xsl:template match="/numbers">
<numbers>
<xsl:value-of select="/numbers/number" />
fn:max(2, 3)
</numbers>
</xsl:template>
</xsl:stylesheet>
Output.xml
<?xml version="1.0" encoding="UTF-8"?>
<numbers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:fn="http://www.w3.org/2005/02/xpath-functions">3
fn:max(2, 3)
</numbers>
Input file is not important here, but I would like to have '3' instead of fn:max(2, 3). How to do it?
for this XSL file:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/02/xpath-functions"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >
<xsl:output method="xml" indent="yes" />
<xsl:template match="/numbers">
<numbers>
<xsl:value-of select="/numbers/number" />
fn:max(2, 3)
<xsl:value-of select="max(/numbers/number)"/>
</numbers>
</xsl:template>
</xsl:stylesheet>
the following error occurs:
SystemId Unknown; Line #13; Column #49; Could not find function: max
SystemId Unknown; Line #13; Column #49; function token not found.
(Location of error unknown)java.lang.NullPointerException
(Location of error unknown)XSLT Error (javax.xml.transform.TransformerException)
: No xml-stylesheet PI found in: test.xml
Exception in thread "main" java.lang.RuntimeException: No xml-stylesheet PI foun
d in: test.xml
at org.apache.xalan.xslt.Process.doExit(Process.java:1155)
at org.apache.xalan.xslt.Process.main(Process.java:1128)
I used Xalan - Version Xalan Java 2.7.1, Command: java org.apache.xalan.xslt.Process -in test.xml -xsl test.xsl -out output.xml
There are several problems: max() needs to be in a value-of, and that you've said xsl:stylesheet version="2.0" for Xalan, which only supports XSLT 1.0. For 2.0, you'd need Saxon 9.x.
Since max() isn't part of XSLT 1.0, you need to invoke the EXSLT extension support, which Xalan does have:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math">
<xsl:template match="/numbers">
<xsl:value-of select="math:max(number)"/>
</xsl:template>
</xsl:stylesheet>
or
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math">
<xsl:template match="/">
<xsl:value-of select="math:max(numbers/number)"/>
</xsl:template>
</xsl:stylesheet>
You've put fn:max(2,3) in a text block. Nothing is going to interpret that. You need to put functions in value-of expressions if you want them to be evaluated.
Lavino,
Thanks for the response. I don't know why but I was pretty sure that Xalan supports 2.0... I've tested it and it works for Saxon 9.
You can use
<xsl:value-of select="max(number)" />
to get the max of all numbers.
Soln:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/numbers">
<numbers>
<max>
<xsl:value-of select="max(number)"/>
</max>
<xsl:apply-templates/>
</numbers>
</xsl:template>
<xsl:template match="number">
<number>
<xsl:value-of select="."/>
</number>
</xsl:template>
</xsl:stylesheet>
You can omit the number template and <xsl:apply-templates/> if its not reqd. This will be the output with the above xslt:
<?xml version="1.0" encoding="UTF-8"?>
<numbers xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<max>10</max>
<number>3</number>
<number>5</number>
<number>10</number>
<number>1</number>
</numbers>