Have been knocking my head trying to figure why the following XSLTProcessor call is not working.
The XSL-T
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xml:space="default">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:param name="include-header" select="'false'"/>
<xsl:template match="/">
<!--xsl:element name="row"-->
<xsl:text>{</xsl:text>
<xsl:apply-templates select="*"/>
<!--/xsl:element-->
<xsl:text>}</xsl:text>
</xsl:template>
<xsl:template match="*[count(descendant::*)=0]">
<xsl:param name="parent"/>
<xsl:variable name="quote" select="'"'"/>
<xsl:variable name="thisName" select="name()"/>
<xsl:variable name="precedingSibling" select="count(preceding-sibling::*[name()=$thisName])+1"/>
<xsl:variable name="parentChild" select="concat($parent, '.',$thisName, $precedingSibling)"/>
<xsl:value-of select="concat($quote,$parentChild, $quote, ': ', $quote, ., $quote, ',')"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="*">
<xsl:call-template name="recurse-descendents"/>
</xsl:template>
<xsl:template match="*[count(descendant::*)>0]">
<xsl:call-template name="recurse-descendents"/>
</xsl:template>
<xsl:template name="recurse-descendents">
<xsl:variable name="thisName" select="name()"/>
<xsl:apply-templates select="*[count(descendant::*)=0]">
<xsl:with-param name="parent" select="concat($thisName, count(preceding-sibling::*[name()=$thisName])+1)"/>
</xsl:apply-templates>
<xsl:apply-templates select="*[count(descendant::*)>0]"/>
</xsl:template>
</xsl:stylesheet>
Sample XML
<?xml version="1.0" encoding="UTF-8"?>
<foods>
<meats>
<meat>Beef</meat>
<meat>Chicken</meat>
<meat>Lamb</meat>
</meats>
<fruits>
<fruit>Orange</fruit>
<fruit>Apple</fruit>
<fruit>Banana</fruit>
<fruit>Avacado</fruit>
</fruits>
<vegetables>
<vegetable>Carrot</vegetable>
<vegetable>Cellery</vegetable>
<vegetable>Potato</vegetable>
</vegetables>
</foods>
Output from XML Spy (as expected)
{"meats1.meat1": "Beef",
"meats1.meat2": "Chicken",
"meats1.meat3": "Lamb",
"fruits1.fruit1": "Orange",
"fruits1.fruit2": "Apple",
"fruits1.fruit3": "Banana",
"fruits1.fruit4": "Avacado",
"vegetables1.vegetable1": "Carrot",
"vegetables1.vegetable2": "Cellery",
"vegetables1.vegetable3": "Potato",
}
Javascript code using XSLTProcessor (not working)
async function GenerateTestCaseResponse(xml, testCase) {
const domParser = new DOMParser();
const xsltProcessor = new XSLTProcessor();
const xslResponse = await fetch("transformer/generate-response-json.xslt");
const xslText = await xslResponse.text();
const xslStylesheet = domParser.parseFromString(xslText, "application/xml");
xsltProcessor.importStylesheet(xslStylesheet);
var responseDomParser = new DOMParser();
var responseDocument = responseDomParser.parseFromString(xml, "text/xml");
var result = xsltProcessor.transformToDocument(responseDocument);
console.log(result.body);
return result;
}
The call to xsltProcessor.transformToDocument
returns empty with no error or exception. At a loss how to resolve, and any thoughts gratefully received.
As already pointed out in a comment, there is no body in arbitrary XML documents, you seem to want to use output method text and the text content anyway so use a fragment result and access its textContent property e.g.
const xsltSource = `<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xml:space="default">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:param name="include-header" select="'false'"/>
<xsl:template match="/">
<!--xsl:element name="row"-->
<xsl:text>{</xsl:text>
<xsl:apply-templates select="*"/>
<!--/xsl:element-->
<xsl:text>}</xsl:text>
</xsl:template>
<xsl:template match="*[count(descendant::*)=0]">
<xsl:param name="parent"/>
<xsl:variable name="quote" select="'"'"/>
<xsl:variable name="thisName" select="name()"/>
<xsl:variable name="precedingSibling" select="count(preceding-sibling::*[name()=$thisName])+1"/>
<xsl:variable name="parentChild" select="concat($parent, '.',$thisName, $precedingSibling)"/>
<xsl:value-of select="concat($quote,$parentChild, $quote, ': ', $quote, ., $quote, ',')"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="*">
<xsl:call-template name="recurse-descendents"/>
</xsl:template>
<xsl:template match="*[count(descendant::*)>0]">
<xsl:call-template name="recurse-descendents"/>
</xsl:template>
<xsl:template name="recurse-descendents">
<xsl:variable name="thisName" select="name()"/>
<xsl:apply-templates select="*[count(descendant::*)=0]">
<xsl:with-param name="parent" select="concat($thisName, count(preceding-sibling::*[name()=$thisName])+1)"/>
</xsl:apply-templates>
<xsl:apply-templates select="*[count(descendant::*)>0]"/>
</xsl:template>
</xsl:stylesheet>`;
const xmlSource = `<?xml version="1.0" encoding="UTF-8"?>
<foods>
<meats>
<meat>Beef</meat>
<meat>Chicken</meat>
<meat>Lamb</meat>
</meats>
<fruits>
<fruit>Orange</fruit>
<fruit>Apple</fruit>
<fruit>Banana</fruit>
<fruit>Avacado</fruit>
</fruits>
<vegetables>
<vegetable>Carrot</vegetable>
<vegetable>Cellery</vegetable>
<vegetable>Potato</vegetable>
</vegetables>
</foods>`;
function GenerateTestCaseResponse(xmlSource, xsltSource) {
const domParser = new DOMParser();
const xsltProcessor = new XSLTProcessor();
const xsltStylesheet = domParser.parseFromString(xsltSource, "application/xml");
xsltProcessor.importStylesheet(xsltStylesheet);
var xmlDoc = domParser.parseFromString(xmlSource, "application/xml");
var result = xsltProcessor.transformToFragment(xmlDoc, xmlDoc).textContent;
console.log(result);
return result;
}
GenerateTestCaseResponse(xmlSource, xsltSource);