XSLT: Test the contents of a result-document in XSpec? - xslt

I'm writing a program that uses XSLT and need to test the contents of a result-document call in Xspec. In the example below, I would like to test the contents of result.xml. If this is possible, how do you do this?
XML: test.xml
<?xml version="1.0" encoding="UTF-8"?>
<root></root>
XSLT: result-document.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="2.0">
<xsl:template match="/root">
<xsl:result-document href="result.xml">
<my-result></my-result>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
XSpec:
<x:description xmlns:x="http://www.jenitennison.com/xslt/xspec" stylesheet="result-document.xsl">
<x:scenario label="Test result document">
<x:context href="test.xml"></x:context>
<!-- How do you test the result.xml file here? -->
<x:expect label="test result">
<my-result></my-result>
</x:expect>
</x:scenario>
</x:description>

I was looking for the same answer, but I suspect this is an inherent limitation in how XSpec is implemented.
I suspect the work-around is to have one untestable template that uses the result-document, then use a match or call template within the result-document that can be tested:
XSLT
<xsl:template match="/root">
<xsl:result-document href="result.xml">
<xsl:call-template name="my-result"/>
</xsl:result-document>
</xsl:template>
<xsl:template name="my-result">
<!-- content -->
</xsl:template>
XSPEC
<x:scenario label="when calling my-result">
<x:call template="my-result"/>
<x:expect label="test something" pending="add your tests here"/>
</x:scenario>
So you can't really test that the result documents are being made in the right way, but you can check their results.
Note that XSPEC won't literally "test the contents of result.xml"; just the behaviour of the XSLT on the inputs that you specify in the XSPEC itself.

Related

XSLT3 can disable-output-escaping be used in same template as expand-text=yes?

I noticed when trying to use disable-output escaping in XSLT3 in Saxon that it would not work if expand-text was set to yes on the stylesheet or even on the given match template
The following code (when run on itself) shows the issue (in Saxon 9.8.0.12).
I know this is an unusual combination and that disable-output-escaping in normally to be avoided at all costs but just trying to ascertain correct behavior.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:template match="/">
<out>
<xsl:apply-templates/>
</out>
</xsl:template>
<xsl:template match="xsl:stylesheet" expand-text="true">
<expandtext>
<count>{count(*)}</count>
<xsl:text disable-output-escaping="true"><test/></xsl:text>
</expandtext>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="xsl:template" expand-text="false">
<notexpandtext>
<count>{count(*)}</count>
<xsl:text disable-output-escaping="true"><test/></xsl:text>
</notexpandtext>
</xsl:template>
</xsl:stylesheet>
produces
<?xml version="1.0" encoding="UTF-8"?>
<out>
<expandtext><count>3</count><test/></expandtext>
<notexpandtext><count>{count(*)}</count><test/></notexpandtext>
<notexpandtext><count>{count(*)}</count><test/></notexpandtext>
<notexpandtext><count>{count(*)}</count><test/></notexpandtext>
</out>
Indeed there is a bug here, which I have logged at
https://saxonica.plan.io/issues/4412
An xsl:text instruction within the scope of expand-text="yes" is implemented internally as a different kind of expression from a "plain old" xsl:text element, and the new expression overlooked the need to support d-o-e.
I have added a test case disable-output-escaping/doe-0201 to the XSLT 3.0 test suite at https://github.com/w3c/xslt30-test

How to add namespace and xsi to the Incoming XML with no namespace

I have requirement where I have to add Namespace and xsi to the
element from the source xml with No Namespace.
In Source XML I am just getting the Nodes and there is No namespace
and another program needs BizTalk to add Namespace and XSI to the XML for its processing.
I tried:
Used add namespace pipeline component. (It just added
namespace and not the xsi bits)
Used Map for putting up the desired format and yes no luck as got
just the namespace.
Need your help around this.
My source XML is like
<?xml version="1.0" encoding="UTF-16"?>
<Document>
<CstmrPmtStsRpt>
<GrpHdr>
<MsgId></MsgId>
<CreDtTm></CreDtTm>
<InitgPty>
<Id>
<OrgId>
<BICOrBEI></BICOrBEI>
</OrgId>
</Id>
</InitgPty>
</GrpHdr>
<OrgnlGrpInfAndSts>
<OrgnlMsgId></OrgnlMsgId>
<OrgnlMsgNmId></OrgnlMsgNmId>
<OrgnlNbOfTxs></OrgnlNbOfTxs>
<OrgnlCtrlSum></OrgnlCtrlSum>
<GrpSts>ACCP</GrpSts>
</OrgnlGrpInfAndSts>
</CstmrPmtStsRpt>
</Document>
My Required format is as below:
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="MyNamespace">
<CstmrPmtStsRpt>
<GrpHdr>
<MsgId></MsgId>
<CreDtTm></CreDtTm>
<InitgPty>
<Id>
<OrgId>
<BICOrBEI></BICOrBEI>
</OrgId>
</Id>
</InitgPty>
</GrpHdr>
<OrgnlGrpInfAndSts>
<OrgnlMsgId></OrgnlMsgId>
<OrgnlMsgNmId></OrgnlMsgNmId>
<OrgnlNbOfTxs></OrgnlNbOfTxs>
<OrgnlCtrlSum></OrgnlCtrlSum>
<GrpSts>ACCP</GrpSts>
</OrgnlGrpInfAndSts>
</CstmrPmtStsRpt>
</Document>
Use the namespace attribute of xsl:element like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*">
<xsl:element name="{local-name()}" namespace="MyNamespace">
<xsl:namespace name="xsi" select="'http://www.w3.org/2001/XMLSchema-instance'"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Edit: Since you need to work with XSLT-1.0. Use following stylesheet:
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Document">
<Document xmlns="MyNamespace"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:apply-templates/>
</Document>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}" namespace="MyNamespace">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note, that you need to know your rootnode's name for this (in this case Document).
BizTalk Answer:
First, it's a good thing the incoming document has no namespace. Xml Namespaces are far, far, far more trouble than they're worth and should be avoided/removed whenever possible.
Second the output format is not valid Xml. "MyNamespace" is not a valid URI and can't be used for a Namespace. If this is what they are asking for, they need to fix that first.
But, if you must, your process should not be "add a namespace". What you're really doing is Transforming from SysA's Document to SysB's Document. For that, use a Map. You will use to practially identical Schemas, one with and one without the Target Namespace.
The Mapper will handle xsi for you as well, if it's needed.

Replace xsi:nil=“true” with open and close tags

I need to do the following transformation in order to get a message pass through a integration broker which does not understand xsi:nil=“true”. I know that for strings having some thing like <abc></abc> is not same as <abc xsi:nil=“true”/> but I have no option.
My input XML:
<PART>
<LENGTH_UOM xsi:nil="1"/>
<WIDTH xsi:nil="1"/>
</PART>
Expected outcome:
<PART>
<LENGTH_UOM><LENGTH_UOM>
<WIDTH></WIDTH>
</PART>
Please let me know your suggestions.
To remove all xsi:nil attributes combine the identity template with an empty template matching xsi:nil.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://xsi.com">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="node()|#*"> <!-- identity template -->
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="#xsi:nil" /> <!-- empty template -->
</xsl:stylesheet>
If you only want to remove those whose value is true use the following empty template instead.
<xsl:template match="#xsi:nil[.='1' or .='true']" />
Concerning the opening and closing tag topic I suggest reading this SO question in which Martin Honnen states that (in the comments of the answer):
I am afraid whether an empty element is marked up as or or is not something that matters with XML and is usually not something you can control with XSLT processors.

XSL query inner xml

Say this is my xml :
<History>
<University>TSU</University>
<Payload>
<Attrib Order="0">OVERSEA</Attrib>
<Attrib Order="1">GRADE2</Attrib>
<Attrib Order="2"><Person><ID>TQR344</ID></Person></Attrib>
<Attrib Order="3">3566644</Attrib>
</Payload>
</History>
And I want to query the inner XML inside Order=2 tag and read ID of the person.
I have created this so far :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="no" />
<xsl:template match="/History">
<xsl:apply-templates select="/History" />
</xsl:template>
<xsl:template name="Person" match="//History">
<Event>
<Uni><xsl:value-of select="University" /></Uni>
<ID><xsl:value-of select="Payload/Attrib[#Order='2']/Person/ID" disable-output-escaping="yes" /></ID>
</Event>
</xsl:template>
</xsl:stylesheet>
But as you can see it is not working.
Also I assigned the inner XML into a variable and tried to query that variable and It didn't work too.
Is it possible to do that via xsl ?
Limitations : I cannot change xml format. But maybe I was able to move from xsl ver 1 to new versions.
I want to query the inner XML inside Order=2 tag
The tag in question does not contain any XML; its content is a string and needs to be manipulated using string functions. Try:
XSLT 1.0
<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="/History">
<Event>
<Uni>
<xsl:value-of select="University" />
</Uni>
<ID>
<xsl:value-of select="substring-before(substring-after(Payload/Attrib[#Order='2'], '<ID>'),'</ID><')"/>
</ID>
</Event>
</xsl:template>
</xsl:stylesheet>
Note:
1. This:
<xsl:template match="/History">
<xsl:apply-templates select="/History" />
</xsl:template>
creates an infinite loop and will crash your processor.
2. Alternatively, you could serialize the string back into XML and process the result as XML; in XSLT 1.0, this can be done only by outputting the string with the escaping disabled, saving the result as a new document, then processing the new document with another XSLT stylesheet. Using XSLT 3.0 (or a processor that supports serializing as an extension) this can be all done during the same transformation.

dyn:evaluate() inside <xsl:for-each> yields strange results

Consider the following XSL transformation:
<?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"
xmlns:dyn="http://exslt.org/dynamic"
extension-element-prefixes="dyn">
<xsl:variable name="_raw">
<test>1</test>
<test>2</test>
</xsl:variable>
<xsl:variable name="list" select="exsl:node-set($_raw)"/>
<xsl:template match="/">
<result>
<xsl:for-each select="$list/test">
<loop>
<xsl:value-of select="dyn:evaluate('exsl:node-set($list)/test')"/>
</loop>
</xsl:for-each>
</result>
</xsl:template>
</xsl:transform>
Executing this on any input gives:
<?xml version="1.0" encoding="UTF-8"?>
<result xmlns:exsl="http://exslt.org/common">
<loop>1</loop>
<loop/>
</result>
What I don't understand is:
The esxl:note-set() inside dyn:evaluate() is necessary if I want to reference $list in the XPath string. Otherwise, the first <loop> is also empty. Why? $list is already a node set.
It's exactly the same code executed twice. Why does it yield no result the second time?
This works if I don't take the values from $list, but from the XML input instead. Where's the difference?
If I remove the <xsl:for-each>, dyn:evaluate() works without the exsl:node-set() in it. Why? There's no reference to the context in the expression that is evaluated, it shouldn't make a difference.
My XSLT processor is Xalan 2.7.2.
I believe what you want to do is:
<?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"
xmlns:dyn="http://exslt.org/dynamic"
extension-element-prefixes="exsl dyn">
<xsl:variable name="_raw">
<test>1</test>
<test>2</test>
</xsl:variable>
<xsl:variable name="list" select="exsl:node-set($_raw)"/>
<xsl:template match="/">
<result>
<xsl:for-each select="$list/test">
<loop>
<xsl:value-of select="dyn:evaluate(.)"/>
</loop>
</xsl:for-each>
</result>
</xsl:template>
</xsl:transform>
Which will result in:
<?xml version="1.0" encoding="UTF-8"?>
<result><loop>1</loop><loop>2</loop></result>
Edit:
I am still not sure what you are after here, but let me point out a few details:
It's exactly the same code executed twice. Why does it yield no result
the second time?
That may be a bug in Xalan. If you run your code with libxslt, the result will be:
<?xml version="1.0"?>
<result xmlns:exsl="http://exslt.org/common"><loop>1</loop><loop>1</loop></result>
The esxl:note-set() inside dyn:evaluate() is necessary if I want to
reference $list in the XPath string. Otherwise, the first is
also empty. Why? $list is already a node set.
I don't think it's necessary. Or perhaps I don't understand what it's necessary for. In any case, changing this:
<loop>
<xsl:value-of select="dyn:evaluate('exsl:node-set($list)/test')"/>
</loop>
to:
<loop>
<xsl:value-of select="dyn:evaluate('$list/test')"/>
</loop>
will result in:
<?xml version="1.0" encoding="UTF-8"?>
<result xmlns:exsl="http://exslt.org/common"><loop>1</loop><loop>1</loop></result>
and this time the result is same for both Xalan and libxslt.