how to get 'excel' new lines in spreadsheetML (MSXSLT) - excel

I'm using ms xslt 1.0 engine.
I want to generate an raw xml output like this:
<Cell ss:StyleID="s27"><Data ss:Type="String">Catchup (Yes),
FVOD(No),
SVOD (No)</Data></Cell>
note the
embedded in the output.
how do I get this in xslt?
If i do this:
<Cell ss:StyleID="s27">
<Data ss:Type="String">
<xsl:text>Catchup (Yes), </xsl:text>
<xsl:text disable-output-escaping="yes"><![CDATA[
]]></xsl:text>
<xsl:text>SVOD (No)</xsl:text>
</Data>
</Cell>
I get this
<ss:Cell ss:StyleID="s27">
<ss:Data ss:Type="String">Catchup (Yes), &#10;SVOD (No)</ss:Data>
</ss:Cell>
which is wrong! (well, not what i want)
If I try
<xsl:text disable-output-escaping="yes">&#10;</xsl:text>
I get the same output
If I try the obvious
<xsl:text>Catchup (Yes),
SVOD (No)</xsl:text>
I get
<ss:Data ss:Type="String">Catchup (Yes),
SVOD (No)</ss:Data>
i.e. its a newline.
for others looking at this question, I'm not 100% sure what the question is, let alone the answer, and I'll try to clarify.
it seems that michael.hor257k's answer does work in some contexts.
(so in fact what im trying does work in some context)
<xsl:text disable-output-escaping="yes">&#10;</xsl:text>
this works if I hard code the output and run it in the XSLT engine used by VS2022
it doesnt work against my pretty 'vanilla' XSLT C# implementation against XSLTTransform and XSLTCompiledTransform.
I'm also not clear if it works with the VS2022 setup in my production code (which doesnt just hardcode some output, but does appytemplate and nodeset gynasmistics).

If you are really willing to resort to using disable-output-escaping to force the required result, consider the following example:
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="/">
<Cell>
<xsl:text>Alpha</xsl:text>
<xsl:text disable-output-escaping="yes">&#10;</xsl:text>
<xsl:text>Bravo</xsl:text>
<xsl:text disable-output-escaping="yes">&#10;</xsl:text>
<xsl:text>Charlie</xsl:text>
</Cell>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0"?>
<Cell>Alpha
Bravo
Charlie</Cell>
However, this depends on the processor's support for disable-output-escaping. Judging from the examples in your question, the processor that you use does not. You say it's a Microsoft engine - but I do get the wanted result with both MS processors here: https://xsltfiddle.liberty-development.net/6qLZFRw
Note also that disable-output-escaping is relevant only at the output stage. If you're not writing to the output, then it has no effect.

This is not an answer, but a clarification. I believe this question has got nothing to do with xslt, I added excel.
As michael.hor257k said, the two serializations (
and a line-feed) should be equivalent. But when I open the following XML file with Excel
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<Styles>
<Style ss:ID="wrap">
<Alignment ss:Horizontal="Left" ss:Vertical="Top"/>
</Style>
</Styles>
<Worksheet ss:Name="Summary">
<Table>
<Row>
<Cell ss:StyleID="wrap">
<Data ss:Type="String">Catchup (Yes),
FVOD (No),
SVOD (No)</Data>
</Cell>
<Cell ss:StyleID="wrap"><Data ss:Type="String">Catchup (Yes),
FVOD (No),
SVOD (No)</Data>
</Cell>
</Row>
</Table>
</Worksheet>
</Workbook>
the two cells behave differently, although only in the Formula Bar:

There was a follow up question with a marked answer.
how to get 'excel' new lines in spreadsheetML and the behaviour of nodeset() on disable-output-escaping (Saxon xslt 1.0)
Best read that, the summary is.
the issue seems to be well know during specification of XSLT.
the behaviour of "disable-output-escaping="yes"" changes between XSLT specs.
the behaviour may well change between XSLT engines of the same theoretical spec (it certainly does for me between different MSXSLT engines/configs)
"The issue is solvable in the XSLT language; however, you must apply the disable-output-escaping at the output stage and not before, and you must use a processor that supports disable-output-escaping" (michael.hor257k comment), for me the constaint on 'output stage' makes this correct solution unsuitable for my specific usage, but it is correct.
With an excel hat on, I've changed the implementation to export multiple rows rather than try to put multiple lines in a single cell.

Related

hello world xml -> json template

I'm trying to transform an xml input to a json output. My XSLT 1.0 is pretty proficient my XSLT 2.0/3.0 not so.
I thought I'd start with a hello world style template and build from there.
My belief is that you can simply create an output as map/array data structure and then some magic will map that into the desired output, so this is my first attempt (I've not defined an input, because any old xml will do in this example, it ignores it):
<?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"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="json" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="foo">
<map xmlns="http://www.w3.org/2005/xpath-functions">
<string key='desc'>Distances between several cities, in kilometers.</string>
<string key='updated'>2014-02-04T18:50:45</string>
<boolean key="uptodate">true</boolean>
<null key="author"/>
<map key='cities'>
<array key="Brussels">
<map>
<string key="to">London</string>
<number key="distance">322</number>
</map>
<map>
<string key="to">Paris</string>
<number key="distance">265</number>
</map>
<map>
<string key="to">Amsterdam</string>
<number key="distance">173</number>
</map>
</array>
</map>
</map>
</xsl:variable>
<xsl:value-of select="xml-to-json($foo)"/>
</xsl:template>
</xsl:stylesheet>
this almost works but I get a string output...(the '"' chars exist in the output file includeing all the escaping, so not a valid json output).
"{\"desc\":\"Distances between several cities, in kilometers.\",\"updated\":\"2014-02-04T18:50:45\",\"uptodate\":true,\"author\":null,\"cities\":{\"Brussels\":[{\"to\":\"London\",\"distance\":322},{\"to\":\"Paris\",\"distance\":265},{\"to\":\"Amsterdam\",\"distance\":173}]}}"
If there are any basic guides to do this, then please let me know, the web is awash with odd examples, out of date instruction based on XSLT 1.0/2.0 or hard to understand pdfs discussing more in depth scenarios.
The function you use already gives you a string with the JSON (see https://www.w3.org/TR/xpath-functions-31/#func-xml-to-json) so if you want to write that to a file just use <xsl:output method="text"/>.
The json output method mainly makes sense if you construct XDM/XPath 3.1 maps/arrays and want to serialize them as JSON.
For your sample I would also use <xsl:template name="xsl:initial-template"> instead of <xsl:template match="/">, then you don't need to provide any dummy input XML at all but can just start with that default named template using e.g. -it from the command line or callTemplate(null, ..) from the API.

How to map value of one xml to another one in Python

I need to make XML out of response in XML format.
For some fields, I need to be able to change the value. I also need to remove all fields that are not mapped.
I get responses that look like this
<response>
<DOCUMENTS>
<DOCUMENT>
<KODA>AA</KODA>
<ITEMS>
<ITEM>B</ITEM>
<ITEM>C</ITEM>
</ITEMS>
</DOCUMENT>
</DOCUMENTS>
</response>
and want to make of it something that looks like below
<EXPORT>
<DOCUMENTS>
<DOCUMENT>
<KODDZ>SS</KODDZ>
<VALUES>
<VALUE>B</VALUE>
<VALUE>C</VALUE>
</VALUES>
</DOCUMENT>
</DOCUMENTS>
</EXPORT>
I have been trying using the XSLT example from this question but without success.
Basically what would be best for me would be a solution that takes some kind of map of fields for example:
map_of_values = {"response/DOCUMENTS/DOCUMENT/KODA":"EXPORT/DOCUMENTS/DOCUMENT/KODZA",
"response/DOCUMENTS/DOCUMENT/ITEMS/ITEM":EXPORT/DOCUMENTS/DOCUMENT/VALUES/VALUE}
What would be the best way to achieve a static map kind of solution?
In terms of XSLT you seem to want something along the lines of
<xsl:template match="response">
<EXPORT>
<xsl:apply-templates/>
</EXPORT>
</xsl:template>
<xsl:template match="KODA">
<KODDZ>
<xsl:apply-templates/>
</KODDZ>
</xsl:template>
<xsl:template match="KODA/text()[. = 'AA']">
<xsl:text>SS</xsl:text>
</xsl:template>
<xsl:template match="ITEMS">
<VALUES>
<xsl:apply-templates/>
</VALUES>
</xsl:template>
<xsl:template match="ITEM">
<VALUE>
<xsl:apply-templates/>
</VALUE>
</xsl:template>
plus the identity transformation template, of course.

Does Saxon-js perform XML syntax checking on a xsl:param?

I run this XSL script thru Saxon-js. It updates a cost field on the main input XHTML using the XML received in the transform call using the stylesheetParams. All good. The problem is that no syntax checking is done on the param-XML (you can see what it looks like in the commented-out line). It is on the XHTML and the transform will generate an error but not on the param-XML. It just allows it to enter and then the key-function just doesn't update the XHTML. Is there a way to do the checking for properly formed XML parameter in the same transform call, or do I have to use 2 transform calls: call transform on the param-XSL to syntax-check, then call this main transform to update the XHTML?
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="html" omit-xml-declaration="yes" encoding="UTF-8" include-content-type="no"/>
<xsl:param name="cost-data"/>
<!-- <supplier><product><key>3</key><pcost uptype="1"><key>21341</key><cost>12.99</cost></pcost></product></supplier> -->
<!-- </xsl:param> -->
<xsl:key name="cost" match="product/pcost[#uptype = 1]/cost" use="'cost' || ancestor::product/key"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="td[#name][key('cost', #name, fn:parse-xml($cost-data))]/text()">{key('cost', ../#name, fn:parse-xml($cost-data))}%</xsl:template>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
</xsl:template>
</xsl:stylesheet>
If your stylesheet code is
<xsl:param name="cost-data">
<supplier><product><key>3</key><pcost uptype="1"><key>21341</key>
<cost>12.99</cost></pcost></product></supplier>
</xsl:param>
then that's well-formed XML and no error should be reported.
However, fn:parse-xml($cost-data) is wrong. The value of the parameter is a node tree, not a string, and fn:parse-xml() expects lexical XML in a string. The effect of calling fn:parse-xml() on this node tree will be to first atomize the node, producing the untyped atomic value "32134112.99", and then attempt to parse this string "32134112.99" as lexical XML, which should fail.
To avoid confusion like this it's good practice to always declare the expected type of your parameters, for example as="xs:string" or as="document-node()".
If you want the default value of $cost-data to be a string containing lexical XML, try
<xsl:param name="cost-data" as="xs:string"><![CDATA[
<supplier><product><key>3</key><pcost uptype="1"><key>21341</key>
<cost>12.99</cost></pcost></product></supplier>
]]></xsl:param>
all on one line without whitespace (or if you need whitespace for readability, put it before a ">" delimiter).
Note: the literal answer to your question is: No. Saxon-JS doesn't perform this checking. The XML parser performs it, long before Saxon-JS gets to see the data.

search and replace an element, using xslt 3, the replacement phrase is the same

while i have as input an xml file like:
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description>
</book>
</catalog>
and i try to find the best way to have the following info in a file, or in the xsl itself:
value to search for:
An in-depth look at creating applications with XML.
add location:
on the self
value to search for:
A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.
add location:
on the self
so if i made a comma separated input file, it would look like:
"An in-depth look at creating applications with XML.","on the self"
"A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.","on the self"
i have tried with xslt 2, but i keep getting errors like a sequence of more than one item is not allowed as the value of variable $search_phrase...
Desired output:
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>to be checked</description>
<location>on the self</location>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>to be checked</description>
<location>on the self</location>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description>
</book>
</catalog>
Could someone give me an example with xslt-3.0 where probably i could replace the above phrases, and add the needed elements as well, wherever there is a match?
What i need to do:
in the full xml file, there are many records that can have the same description. I also need to make an exact match on the description: The phrase
"An in-depth look at creating applications with XML, authored by ..."
should not be matched. And in my case, i have also a description where the difference is only the case for instance, "an in-depth look at creating applications with XML." should not be also matched. Since in my code i use lowercase, this may also be the problem, but not sure... Whenever there is a match, the location specified along the search term, must be added into the location element, which currently does not exist in any record in the xml.
Here is a suggestion on how to compare the description elements to a sequence of strings passed in as a parameter (but you could of well read it in from a file):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:param name="new" as="xs:string" select='"on the self"'/>
<xsl:param name="replace" as="xs:string" select="'to be checked'"/>
<xsl:param name="search" as="xs:string*"
select='"An in-depth look at creating applications with XML.",
"A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world."'/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="description[. = $search]">
<xsl:copy>{$replace}</xsl:copy>
<location>{$new}</location>
</xsl:template>
</xsl:stylesheet>
Works fine at http://xsltfiddle.liberty-development.net/eiQZDbk, but only after editing the sample to have all description data on one line.
If that is not the case then changing the template to
<xsl:template match="description[normalize-space() = $search]">
<xsl:copy>{$replace}</xsl:copy>
<location>{$new}</location>
</xsl:template>
should help: http://xsltfiddle.liberty-development.net/eiQZDbk/1
If you have several terms to relate to each other than some XML format seems be more appropriate to structure the data, so in
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:param name="data-url" as="xs:string" select="'data.xml'"/>
<!-- if you want to load from a file use xsl:param name="replacement-doc" select="doc($data-url)" -->
<xsl:param name="replacement-doc">
<root>
<search>
<term>An in-depth look at creating applications with XML.</term>
<replacement>to be checked</replacement>
<new>on the self</new>
</search>
<search>
<term>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</term>
<replacement>whatelse</replacement>
<new>something</new>
</search>
</root>
</xsl:param>
<xsl:key name="search" match="search" use="term"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="description[key('search', normalize-space(), $replacement-doc)]">
<xsl:variable name="search" select="key('search', normalize-space(), $replacement-doc)"/>
<xsl:copy>{$search/replacement}</xsl:copy>
<location>{$search/new}</location>
</xsl:template>
</xsl:stylesheet>
I have made some suggestion to do that and have adapted the template. Online sample is at http://xsltfiddle.liberty-development.net/eiQZDbk/2. As indicated there in a comment you can adapt that approach to load the data from a separate file instead of keeping it inline in the XSLT.

Use XML schema to specify default namespace in XML instance

I'm not sure if it matters, but I'm using BizTalk 2009 to generate the XML.
Is there a way to specify in my XML schema that the generated XML instance should use the target namespace as the default namespace?
If I have an xsd file like this:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/">
<xs:element name="example">
<xs:complexType>
<xs:attribute name="value" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:schema>
It creates an XML file like this:
<ns0:example value="something" xmlns:ns0="http://example.com/" />
But I want it to create an XML file like this:
<example value="something" xmlns="http://example.com/" />
I know that they're technically equivalent, but the consumers (vendor APIs) are poorly implemented and I'd like to give them what they expect.
I would expect that it depends on the software generating the instance, not the schema. XSD Schema was developed for validation of XML instances against a schema, not for generating instances from it. So it is unlikely to be present explicitly in XMLSchema. The generating tools might, however, use the fact that elements were (un)qualified
elementFormDefault="(un)qualified"
to trigger the prefixing.
Not completely in scope, but the following is worth reading for schema design: http://www.xfront.com/HideVersusExpose.html
One way would be to define a schema without the namespace. Map the BizTalk schema to the newly defined schema without namespace. From a BizTalk viewpoint, you would have a schema which represents the actual contract with the consumers. (i.e. without namespaces) Also, BizTalk uses namespace#rootnodename to define messageTypes. In this example, you would have two schemas
somenamespace#somerootnodename
#somerootnodename
The possible drawbacks of this approach are this would limiting the usage of this schema (#rootnodename) to 1 instance with the BizTalk group.
This is the default behaviour of BizTalk working this XML schemas and, as far as I know, there is no builtin way to change this.
What you really want, however, is that outbound messages conform to a cleaner and more liberal format than what is used by BizTalk. You can do this by using a custom pipeline component (and a custom send pipeline) to process the outgoing message before it leaves BizTalk.
The idea is to change the namespace prefix as part of sending the message outside BizTalk. The transformation happens during the processing of the send pipeline.
Nic Barden has blogged and provided some source code about this here. You can use his sample as the basis for performing replacement of namespace prefixes, rather than replacing namespaces themselves.
I strongly encourage you to check out the whole series of posts he's done about Developing Streaming Pipeline Components. Nic has made an extensive and thorough job of describing all that's needed to author robust and enterprise-class pipeline components.
Part 1
Part 2
Part 3
Part 4
Part 5
The ns0 prefix is added whenever a BizTalk btm maps a message. It shouldn't matter as this is still valid xml, however this could be a problem when sending messages to partners with legacy or incomplete xml parsers.
You can remove the ns0 prefix and instead make ns0 your default namespace on the output message by changing your btm from a visual map to an .xslt map.
e.g. Once you have converted your map to xslt, change the xslt from:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl s0"
version="1.0"
xmlns:ns0="http://targetns"
xmlns:s0="http://sourcens"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="s0:FromRoot" />
</xsl:template>
<xsl:template match="s0:FromRoot">
<ns0:ToRoot>
<xsl:for-each select="s0:FromElement">
<ns0:ToElement>
<xsl:value-of select="text()"/>
</ns0:ToElement>
</xsl:for-each>
</ns0:ToRoot>
</xsl:template>
</xsl:stylesheet>
To:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var"
exclude-result-prefixes="msxsl s0"
version="1.0"
xmlns="http://targetns"
xmlns:s0="http://sourcens"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="s0:FromRoot" />
</xsl:template>
<xsl:template match="s0:FromRoot">
<ToRoot>
<xsl:for-each select="s0:FromElement">
<ToElement>
<xsl:value-of select="text()"/>
</ToElement>
</xsl:for-each>
</ToRoot>
</xsl:template>
</xsl:stylesheet>
i.e. change the default xmlns and then remove the ns0 prefixes automatically.
A more generic solution is also possible (e.g. similar to Firras' answer here), which could be useful e.g. to place as a send port map to strip out all prefixes from elements. However, one needs to be wary if there are more than one xmlns on the output message!

Resources