XSLT create CSV but maintain commas from XML node - excel

I have an XML file that I want to create a csv file to be opened in MS Excel. The csv file uses commas as its delimiter, but one of the XML nodes has commas in it, And Excel is reading those commas as if they're supposed to be in a seperate cell. So in my example Block 1, Block 1+, Block 5, Block 5 Exportable instead of keeping this content in one cell each comma is treated as a new cell for each comma, when opened in Excel. I've tried using tab characters to delimit the csv, but that doesn't work. I've also tried encloseing the node in quotes.
How can I make the string with multiple commas to fill just one cell?
XML:
<dmodule>
<title>ABC DEF</title>
<applic id="UHK97000-10_UHK97000-12_UHK97000-15_UHK97000-17_UHK97000-18_UHK97000-20_UHK97000-21">
<displayText>
<simplePara>Block 1, Block 1+, Block 5, Block 5 Exportable</simplePara>
</displayText>
</applic>
<tminfo>Text that should appear in the next column
</tminfo>
</dmodule>
XSLT:
<xsl:value-of select="title"/><!-- tab char --> <xsl:value-of select="simplePara> <xsl:value-of select="tminfo"><!-- new line char -->
I've also tried excaping the comma character and surrounding it in double quotes, but I wasn't able to get my code to run.
XSLT:
<xsl:variable name="keepCommas">
<xsl:for-each select="simplePara">
<xsl:text>,</xsl:text>
<xsl:value-of select="concat('"['simplePara,',']"')"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:variable>
This code didn't render any output at all.
I'm limited to XSLT version 1.0
Microsoft 365 - Excel

AFAICT all you need to do is simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="dmodule">
<xsl:text>"</xsl:text>
<xsl:value-of select="title"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="applic/displayText/simplePara"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="tminfo"/>
<xsl:text>"
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Related

Add '(apostrophe) as a valid character in XSLT template

I am working on a project there they have given a list of valid characters which should be passed as it is and any other characters other than them has to be replaced with ?(question mark).
My company software does not allows to use replace function, so i have to use translate method.
I am able to add all the valid characters except '(apostrophe). How to include apostrophe also as a valid character in variable vAllowedSymbols.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="vAllowedSymbols" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890/-?:(),.+ '"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="
translate(
.,
translate(., $vAllowedSymbols, ''),
''
)
"/>
</xsl:template>
Input:
<t>
<Name>#O'Niel</Name>
<Name>St: Peter</Name>
<Name>A.David</Name>
</t>
Output:
<t>
<Name>?O'Niel</Name>
<Name>St: Peter</Name>
<Name>A.David</Name>
</t>
Use
<xsl:variable name="vAllowedSymbols">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890/-?:(),.+ '</xsl:variable>
<xsl:variable name="vAllowedSymbols" select='"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890/-?:(),.+ &apos;"'/>
There is some advantage in having the variable contain a string instead of a text node. Or more precisely, be a string instead of a node-set that contains a text node.

Fix XSLT Numeric Cell Output to Excel as String

I am using an XSLT template to convert and format some output from FileMaker to Excel. It works perfectly for all but one column. The data is numeric, but must be output as a string in the format NN.NN. The data field is entered as, for example, 04.13, but when output to Excel, displays as 4.13. There are some cases where the user has input 04 13 (which oututs correctly as it cannot be parsed as a number) but where the user has entered the data correctly into filemaker (the field is Text), i.e. 04.13, I need to ensure the output data remains in the correct format.
I have tried the following, but it does not appear to work as expected - the output file still shows the column values as numbers, ie loses the leading zero:
<xsl:choose>
<xsl:when test="$xlFieldName='d_Oracle_Task_Code'">
<xsl:variable name="OracleTaskCode" select="fmp:DATA" />
<xsl:value-of select='format-number(OracleTaskCode, "00.00")' />
</xsl:when>
</xsl:choose>
I'm not worried about the cases where the input is missing the decimal point seperator, only ensuring the values are output as text.
Try defining a variable as:
<xsl:variable name="apos">'</xsl:variable>
then use:
<xsl:value-of select="concat($apos, format-number(OracleTaskCode, '00.00'))" />
Another option worth trying would be to define the cell contents as data type string:
<Data ss:Type="String">
....
</Data>
I am afraid I have no means to test either.
My issue was the order in which I checked. I could not get the XSLT to apply the formatting or append the apostrophe until I made my when block the first one in my choose block.
<Style ss:ID="xlTextStyle">
<NumberFormat ss:Format="#"/>
</Style>
<xsl:choose>
<xsl:when test="$xlFieldName='d_Scheme_Task_Code'">
<xsl:attribute name="ss:StyleID">xlTextStyle</xsl:attribute>
<Data>
<xsl:attribute name="ss:Type">String</xsl:attribute>
<xsl:value-of select="." />
</Data>
</xsl:when>
<xsl:when test="$xlFieldType='NUMBER'">
<xsl:choose>
<xsl:when test...
I'm now getting the column values correctly formatted using the Text format.

Schematron: Element within mixed-type element only with other content

My XML Data should look like this:
<mixed_type_parent>
...text...
<other_element1/>
<other_element2/>
<element_in_question>...some content...</element_in_question>
<other_element2>...text...</other_element2>
<other_element1>...text...</other_element1>
...text...
</mixed_type_parent>
What I want to make sure using Schematron is that the "element_in_question" may only appear within "mixed_type_parent" if there is some text outside of the "element_in_question".
That means
<mixed_type_parent>
<element_in_question>...some content...</element_in_question>
</mixed_type_parent>
is not allowed and should cause an error.
I tried to get the string-length of all text immediately within "mixed_type_parent"
string-length(replace(ancestor::mixed_type_parent[1]/text(),' ', ''))
But, again, there is one of the most annoying errors in XPath: "A sequence of more than one item is not allowed as the first argument of replace()"
In XSLT I have solved this problem by the simplest function you can think about:
<xsl:function name="locfun:make_string">
<xsl:param name="input_sequence"/>
<xsl:value-of select="$input_sequence"/>
</xsl:function>
(It is really a shame that there seams to be no such built-in function in XPath.)
But how can I use this function in Schematron? I didn't find a solution for this.
And other than that: How do I get all text form all other childs of "mixed_type_parent" except "mixed_type_parent"?
Try this:
string-join(ancestor::mixed_type_parent[1]/text(),'')=''
For your second question: How do I get all text form all other childs of "mixed_type_parent" except "mixed_type_parent"?
/mixed_type_parent/*[not(self::element_in_question)]/text()
considering this input XML,
<mixed_type_parent>
<element_in_question>...some content...</element_in_question>
</mixed_type_parent>
when this XSLT is applied:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="element_in_question">
<xsl:choose>
<xsl:when test="preceding-sibling::text() or following-sibling::text()">
true
</xsl:when>
<xsl:otherwise>
false
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
produces
false

XSLT finding matching node by comparing element text to substring result

I am processing this XML:
<Brand>
<Brand_Name>BLENDERM</Brand_Name>
<Brand_Code>1103</Brand_Code>
<Groups>
<Group>
<Group_Code>657</Group_Code>
<Parent_Code>0</Parent_Code>
<Group_Level>1</Group_Level>
<Group_Name>Brand Default</Group_Name>
<Product>
<Pip_code>0032359</Pip_code>
<Status>In Use</Status>
Using this XSLT:
<xsl:template match="Product" mode="phase-3">
<xsl:value-of select="document('rx_catmapping.xml')/descendant::mapping[source=substring(ancestor::Brand/Brand_Name,1,1)]/target"/>
</xsl:template>
Here is a sample of rx_catmapping.xml:
<Lookup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<mapping>
<source>a</source>
<target>788</target>
</mapping>
<mapping>
<source>B</source>
<target>789</target>
</mapping>
</Lookup>
So, I am processing the Product element, which is a descendant of Brand. The first letter of Brand/Brand_Name in this case is B, and I am trying to output the value 789 by looking it up in rx_catmapping.xml. This should be really simple but I am completely stumped! I have tried changing the first part of the XPath to refer to document('rx_catmapping.xml')/Lookup/mapping, or document('rx_catmapping.xml')//mapping. I have also tried changing the first half of the comparison to string(source), or to source/text(), but neither of these works either. (The reason for trying this was that using source='B', for example, did seem to work, so I wondered if I was trying to compare two incompatible data types.)
Thanks in advance for your help.
Define a key
<xsl:key name="k1" match="mapping" use="source"/>
then use
<xsl:variable name="map-doc" select="document('rx_catmapping.xml')"/>
and
<xsl:variable name="letter" select="substring(ancestor::Brand/Brand_Name,1,1)"/>
<xsl:for-each select="$map-doc">
<xsl:value-of select="key('k1', $letter)/target"/>
</xsl:for-each>
With XSLT 2.0 you can simplify that to
<xsl:value-of select="key('k1', substring(ancestor::Brand/Brand_Name,1,1), $map-doc)"/>
The problem is, I believe, that at the point where you do ancestor::Brand/Brand_Name, the context is the mapping element in the external file. This worked for me
<xsl:template match="/">
<xsl:apply-templates select=".//Product"/>
</xsl:template>
<xsl:template match="Product">
<xsl:variable name="x" select="substring(ancestor::Brand/Brand_Name,1,1)"/>
<xsl:value-of select="document('file:///c:/temp/rx_catmapping.xml')//mapping[source=$x]/target"/>
</xsl:template>

Sharepoint XSL - break string into componant parts

I'm using a "choice" site column with the multiple-check option enabled so that users can tag a list item with several choices from the column.
This column then powers a design feature in a content query webpart - where the column choice is appended to create an image filename.
Choice1
Choice2
Choice3
Choice4
becomes
<img src="http://mysite/content-Choice1.jpg />
The problem I've got is that the XSL parser is fed a string which has semicolons (;) and hashes (#) separating the choice values. If all 4 options were ticked, the string fed into the XSLT parser would be:
;#Choice1;#Choice2;#Choice3;#Choice4
How can I work through the string and separate each choice into its own XSL variable?
I've tried various substring-before functions, but I can't get anything working.
Since XPath 1.0 does not support the tokenize() function, you'll have to do all the work yourself. For instance, you can generate the <img> elements recursively from the choices:
<xsl:template name="RecurseConvertChoicesToImages">
<xsl:param name="choices" />
<xsl:variable name="token"
select="substring-before($choices, ';#')" />
<xsl:variable name="nextToken"
select="substring-after($choices, ';#')" />
<xsl:if test="$token">
<img src="http://mysite/content-{$token}.jpg" />
</xsl:if>
<xsl:if test="$nextToken">
<xsl:call-template name="RecurseConvertChoicesToImages">
<xsl:with-param name="choices" select="$nextToken" />
</xsl:call-template>
</xsl:if>
</xsl:template>
I would recommend to use JavaScript to parse the string and accordingly display the image using JavaScript.

Resources