Pass XSL template output to XSL function - json

I am using XSLTJSON to convert my XML to JSON. My raw XML is not in the format that I want, so I first pass it through an XSL stylesheet to clean it up and then pass the output of that stylesheet into XSLTJSON.
Right now I'm doing this by calling transformers serially. I'd like to streamline it and have only one call to the transformer necessary. Is there a way to write an XSL stylesheet that includes json.xsl, matches on "/", does it's thing and then passes its output to json:generate()?

This stylesheet:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:json="http://json.org/">
<xsl:import href="xml-to-json.xsl"/>
<xsl:template match="/">
<xsl:variable name="vFirstPass">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="json:generate($vFirstPass)"/>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Reference to undeclared namespace prefix: 'json'. while renaming xml attibute with xslt

Updated with Minimal, Complete and Verifiable information thanks #Kenneth for drawing my attention.
So, I'm trying to rename an xml attribute using xslt, but I keep getting this error message:
Reference to undeclared namespace prefix: 'json'.
This is my source xml:
<?xml version="1.0" encoding="utf-8"?>
<data>
<code>SO000009</code>
<businessPartner>70833A356B9A428CBDDCD2A76A49681F</businessPartner>
<startDateTime>2018-01-25T15:24:27Z</startDateTime>
<subject>Test</subject>
<equipments jsonArray="true">80202</equipments>
</data>
This is how I want the xml to be:
<?xml version="1.0" encoding="utf-16"?>
<data xmlns:json="http://james.newtonking.com/projects/json">
<code>SO000009</code>
<businessPartner>70833A356B9A428CBDDCD2A76A49681F</businessPartner>
<startDateTime>2018-01-25T15:24:27Z</startDateTime>
<subject>Test</subject>
<equipments json:Array="true">80202</equipments>
</data>
And this is my xslt:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/data">
<data xmlns:json="http://james.newtonking.com/projects/json">
<xsl:apply-templates select="node()|#*" />
</data>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="#jsonArray">
<xsl:attribute name="json:Array">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
When I'm trying to convert my xml, I keep getting this message:
Error occurred while compiling stylesheet 'CS_jsonArray.xslt'.
Code: 0x80004005
Reference to undeclared namespace prefix: 'json'.
After looking in the 'Related' sidebar, I did found the answer to my own question.
XSL transformation - Namespace prefix undeclared
So for those who have a same issue, be sure the namespace you want to change the attribute names for, is also in the xslt tag itself...
This is how my xslt started:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>...
This is how I changed it:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:json="http://james.newtonking.com/projects/json">
<xsl:output indent="yes"/>...

xml tags passed as arguments to xsl

How to modify the below xsl to process parameters whose value is tags. Instead of using w:p and w:pPr/w:pStyle/#w:val i will be passing them as args
Actual XSl:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"
>
<xsl:param name="styleName"/>
<xsl:output method="text"/>
<xsl:template match="w:p"/>
<xsl:template match="w:p[w:pPr/w:pStyle/#w:val[matches(., concat('^(',$styleName,')$'),'i')]]">
<xsl:value-of select="."/><xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Required XSL:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">
<xsl:param name="styleName" select="'articletitle'"/>
<xsl:param name="para" select="'//w:p[w:pPr/w:pStyle/#w:val[matches(.,concat('^(',$styleName,')$')]]'"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:for-each select="$para">
<xsl:value-of select="."/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
NOTE: You should get used to give information about which version of xslt and which processor you are using. In this case, the answer is valid only if you are using XSLT 2.0
This is not a complete answer, but you can start by looking at this approach:
Try using local-name and in-scope-prefixes (only available in XSLT 2.0) to match a node dynamically.
Here you have an example template to replace your empty template:
<xsl:template match="*[local-name()=substring-after($para,':') and in-scope-prefixes(.)[.=substring-before($para,':')]]"/>
For the second part of the expression ($parastyle), I can only think about writing your own function to evaluate it dynamically.
I'll try to post an example of such a function later on.
I fugured out the problem in my coding, is in second line,
<xsl:param name="para" select="'//w:p[w:pPr/w:pStyle/#w:val[matches(.,concat('^(',$styleName,')$')]]'"/>
Since i have given the quotes in select attribute the value has been considered as string instead of xpath expression.

How have xsl:function return a string value including html tags

I am attempting to transpose a java function to an xsl:function spec.
The function basically places html tags around substrings.
I now bump into difficulties: using the java inline code this works perfectly, but I am unable to figure out how to prevent output escaping when using the xsl:function.
How can I achieve the output to contain the wanted html tags?
A simplified example of what I am trying to achieve is the following:
input parameter value "AB" should lead to a string A<b>B</b>, shown in html browser as AB of course.
Example function I tried is the below; but then the resulting string is A&lt ;b&gt ;B&lt ;/b&gt ; (note that I had to add blanks to prevent the entities from getting interpreted in this editor), which of course shows up in browers as A<b>B</b>.
Note that xsl:element cannot be used in the xsl:function code, because that has no effect; I want the string result of the function call to contain < and > characters, and then add the string result to the output result file.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:custom="http://localhost:8080/customFunctions">
<xsl:output method="html" version="4.0" encoding="UTF-8" indent="yes"/>
<xsl:function name="custom:test">
<xsl:param name="str"/>
<xsl:value-of select="substring($str,1,1)"/>
<xsl:text disable-output-escaping="yes"><![CDATA[<b>]]></xsl:text>
<xsl:value-of select="substring($str,2)"/>
<xsl:text disable-output-escaping="yes"><![CDATA[</b>]]></xsl:text>
</xsl:function>
<xsl:template match="/">
<xsl:element name="html">
<xsl:element name="body">
<xsl:value-of select="custom:test('AB')"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Here is an example, use sequence instead value-of and make sure your function returns nodes (which is usually simply done by writing literal result elements):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:custom="http://localhost:8080/customFunctions"
exclude-result-prefixes="custom">
<xsl:output method="html" version="4.0" encoding="UTF-8" indent="yes"/>
<xsl:function name="custom:test">
<xsl:param name="str"/>
<xsl:value-of select="substring($str,1,1)"/>
<b>
<xsl:value-of select="substring($str,2)"/>
</b>
</xsl:function>
<xsl:template match="/">
<xsl:element name="html">
<xsl:element name="body">
<xsl:sequence select="custom:test('AB')"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

how to interpret HTML in XSL

I have the following xml
<results>
<first-name>Carl<first-name>
<data><b> This is carl's data </b></data>
</results>
How do I include the bold tags which is present in the <data> tag to be a part of the output but rendered as an HTML
When I say <xsl:value-of select="results/data"/> The output is
<b> This is carl's data </b>
I want to achieve "This is carl's data" as the output in bold.
Well <xsl:copy-of select="results/data/node()"/> is a start but if the requirement is part of a larger problem then you are better off writing a template for data elements which uses apply-templates to push the child nodes through some template(s) for copying HTML elements through to the output.
I am sure someone will let me know if I am being naive:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/results">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="first-name">
<xsl:value-of select="." />
<xsl:text>: </xsl:text>
</xsl:template>
<xsl:template match="data">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="b">
<b>
<xsl:value-of select="." />
</b>
</xsl:template>
</xsl:stylesheet>

XLSX- how to get rid of the default namespace prefix x:?

I'm generating XLSX spreadsheet using OOXML SDK, and I need to get rid of x: namespace prefix. How can I achieve this?
using (SpreadsheetDocument doc = SpreadsheetDocument.Open("template.xlsx", true))
{
//Save the shared string table part
if (doc.WorkbookPart.GetPartsOfType().Count() > 0)
{
SharedStringTablePart shareStringPart = doc.WorkbookPart.GetPartsOfType().First();
shareStringPart.SharedStringTable.Save();
}
//Save the workbook
doc.WorkbookPart.Workbook.Save();
}
Here, the original XLSX file is coming from Excel 2007 and doesn't have the prefix, however, after the save operation the prefix appears. How can I avoid that?
Here is a modified version of the stylesheet linked by divo that strips only a single namespace and copies the rest verbatim:
<xsl:stylesheet version="1.0" xmlns:x="namespace-to-strip"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" encoding="UTF-8"/>
<xsl:template match="/|comment()|processing-instruction()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#x:*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Unless I'm much mistaken the original file is namespaced as well - only in the form of a default namespace. What's wrong with the namespace in the first place?