Applying XSLT to XML while exporting with ESTK/InDesign - javascript

Good afternoon Scripters,
I have had some help setting up this structure, which I am using to create a folder on my desktop dynamically based on certain XML elements in my XML strucure, then loop through the XML records, split them into separate files, and put them in their respective folders. So far, it is working perfectly, but I need to apply an XSL that I have which will convert certain attributes into elements. Is there a way in ESTK to apply XSLT with JavaScript upon export?
var root, records, f, n, doc;
doc = app.activeDocument;
root = doc.xmlElements[0];
records = root.evaluateXPathExpression ( "./record" );
n = records.length;
while ( n-- ) {
var ff = new Folder(Folder.desktop + "/" +app.activeDocument.xmlElements.item(0).xmlElements.item(n).xmlElements.item(0).xmlAttributes.item(0).value + "/data/" +
app.activeDocument.xmlElements.item(0).xmlElements.item(n).xmlElements.item(0).xmlElements.item(0).xmlElements.item(1).xmlAttributes.item(1).value + "/" +
app.activeDocument.xmlElements.item(0).xmlElements.item(n).xmlElements.item(0).xmlElements.item(0).xmlElements.item(2).xmlAttributes.item(1).value);
if (!ff.exists)
ff.create();
f = File ( ff +"/"+app.activeDocument.xmlElements.item(0).xmlElements.item(n).xmlElements.item(0).xmlElements.item(0).xmlElements.item(0).xmlAttributes.item(1).value);
records[n].exportFile ( ExportFormat.XML, f);
}
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="Root/record">
<record name="{#name}" type="{#type}">
<item name="{item/#name}">
<value>
<xsl:for-each select="item/value/item">
<item name="{#name}">
<value><xsl:value-of select="#value"/></value>
</item>
</xsl:for-each>
</value>
</item>
</record>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

See answer here:
https://forums.adobe.com/thread/1813381
insert before line 13:
app.activeDocument.xmlExportPreferences.allowTransform=true;
app.activeDocument.xmlExportPreferences.transformFilename=File('PATH_TO_YOUR_TRANSFROM_FILE');

Related

XSLTProcessor javascript in Chrome: how can I pass an XML node as a parameter?

I am calling XSLTProcessor in Chrome
function applyStylesheetUsingBuiltin() {
var parser = new DOMParser();
var source = parser.parseFromString(getSourceXML(), "text/xml");
var stylesheet = parser.parseFromString(style, "text/xml");
var transformer = new XSLTProcessor();
transformer.importStylesheet(stylesheet);
for(parameter in parameters) {
var paramdoc = parser.parseFromString(parameters[parameter], "text/xml");
transformer.setParameter("", parameter, paramdoc.firstElementChild);
}
var newdoc = transformer.transformToDocument(source);
document.replaceChildren();
document.appendChild(newdoc.firstElementChild);
}
and it doesn't work. My XSLT contains
style=`<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="testparam" select="/.."/>
<xsl:template match="/">
<xsl:value-of select="$testparam"/>
<xsl:value-of select="$testparam/text()"/>
</xsl:template>
</xsl:transform>`;
parameters = {
testparam: "<root>Hello</root>"
};
And when I just do
<xsl:value-of select="$testparam"/>
I get "[Object element]". But when I'm trying to XPath navigate in it
<xsl:value-of select="$testparam/text()"/>
the XSLT processor just fails returning null as the result document.
So how can I pass an XML to this darn XSLTProcessor? I am trying to make up for it's sheer refusal to execute the document($url) XPath function, nor xsl:import.

How do I declare then use a variable within a CDATA block?

I have an XSL/XML/JS file. It was written by someone who is not working here any more, and I normally only deal with SQL, so Im at a loss as to how to achieve what I need to do
Im trying to add some variables into the file within the existing CDATA block. I then use the variables within a function. However, I have tried the below and variations of this, but keep getting a syntax error within the application (Dynamics AX). Am I doing something obviously wrong here, with either how I am declaring the variables or how I am using them? These are the only changes I have made, and without these changes there are no syntax or any other issues/errors.
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:mxm="http://schemas.microsoft.com/dynamics/2008/01/documents/MxmServInterfaceOutboundAif"
xmlns:data="http://www.example.com/data" exclude-result-prefixes="xs xsi xsl">
<xsl:output method="text" encoding="UTF-8" indent="no" />
<msxsl:script language="JScript" implements-prefix="data">
<![CDATA[
//Minor Repairs email address
var MinorsEmail = xxx#domain.com
//Service Dept email address
var ServiceEmail = yyy#domain.com
//Major Repairs email address
var MajorsEmail = zzz#domain.com
//Select appropriate email to use
function EmailFrom(fault)
{
var type = fault.substr(0,2);
if (type == "MI")
{
var ret = MinorsEmail;
}
else
{
var ret = concat(ServiceEmail, "; ",MajorsEmail);
}
return ret;
}
Edit: Adding quotes around the variable values has solved part of the problem. The problem now is that the CONCAT does not function as intended. I get the following error now:
Variable concat has not been declared
Thanks to #Martin Honnen, the answer was to add quote to variable values, and to use + instead of CONCAT:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:mxm="http://schemas.microsoft.com/dynamics/2008/01/documents/MxmServInterfaceOutboundAif"
xmlns:data="http://www.example.com/data" exclude-result-prefixes="xs xsi xsl">
<xsl:output method="text" encoding="UTF-8" indent="no" />
<msxsl:script language="JScript" implements-prefix="data">
<![CDATA[
//Minor Repairs email address
var MinorsEmail = "xxx#domain.com"
//Service Dept email address
var ServiceEmail = "yyy#domain.com"
//Major Repairs email address
var MajorsEmail = "zzz#domain.com"
//Select appropriate email to use
function EmailFrom(fault)
{
var type = fault.substr(0,2);
if (type == "MI")
{
var ret = MinorsEmail;
}
else
{
var ret = ServiceEmail + "; " + MajorsEmail;
}
return ret;
}

JavaScript String replace second occurrence in XML

Hi have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<library>
<item>
<books>
<?xml version="1.0" encoding="UTF-8"?>
<Fiction>
<tt:Author>A</tt:Author>
<tt:BookName>45</tt:BookName>
</Fiction>
</books>
</item>
</library>
I want to basically replace the second occurence of the entire xml tag with blank space. So basically replace <?xml version="1.0" encoding="UTF-8"?> string which appears after the <books> opening tag with a space.
Any suggestions? I tried other links but could not get a working solution. The problem is there are ", ? and > in between the xml tag and the string replace function is considering that as an escape sequence character.
This is what I tried:
var stringToReplace = '<?xml version="1.0" encoding="UTF-8"?>';
var string = data.string;
//console.log(string);
var t=0;
var text = string.replace(/stringToReplace/g, function (match) {
t++;
return (t === 2) ? "Not found" : match;
});
console.log(text);
The above still prints both the xml tags
Assuming your XML always looks like this, you can use regular String methods to find the last occurrence of the string and remove it by creating substrings of the XML around it:
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<library>
<item>
<books>
<?xml version="1.0" encoding="UTF-8"?>
<Fiction>
<tt:Author>A</tt:Author>
<tt:BookName>45</tt:BookName>
</Fiction>
</books>
</item>
</library>`;
const strToReplace = '<?xml version="1.0" encoding="UTF-8"?>';
const index = xml.lastIndexOf(strToReplace);
// The new left- and right-sides of the string will omit the strToReplace
const newXml = xml.substring(0, index) + xml.substring(index + strToReplace.length);
console.log(newXml);

How to generate/write an ID (alpha-numeric) while adding a new contact in XML file

Is it possible to generate a alpha-numeric ID during the creation of new contact in the xml file.
I used XSLT function <xsl:value-of select="generate-id(.)"/> to generate a unique ID in my xml file in the beginning for serializing so i used the above to generate ID.
I can add a new record in xml file using JavaScript DOM in XUL.
function create()
{
var src = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
<CONTACT>\
<Customer-ID>"+document.getElementById("Customer-ID").value+"</Customer-ID>\
<FirstName>"+document.getElementById("FirstName").value+"</FirstName>\
<LastName>"+document.getElementById("LastName").value+"</LastName>\
<email>"+document.getElementById("email").value+"</email>\
<address>"+document.getElementById("address").value+"</address>\
<state>"+document.getElementById("state").value+"</state>\
<country>"+document.getElementById("country").value+"</country>\
</CONTACT>";
print(src);
var node = srcToNode(src);
if (objXMLDoc.childNodes && objXMLDoc.childNodes.length) {
for (var i = 0; i < objXMLDoc.childNodes.length; ++i) {
if (objXMLDoc.childNodes.item(i).childNodes && objXMLDoc.childNodes.item(i).childNodes.length) {
var x = objXMLDoc.childNodes.item(i).childNodes.length;
var lastNode = objXMLDoc.childNodes.item(i).childNodes.item(x-2);
}
}
objXMLDoc.getElementsByTagName("CONTACT");//objXMLDoc.childNodes.item(0).lastChild;
objXMLDoc.appendChild(node);
var serializer = new XMLSerializer();
var prettyString = serializer.serializeToString(objXMLDoc);
saveFile(prettyString, "C:\\contact.xml");
}
}
The above code works perfectly to create a new record in my xml file.
How can i generate a unique ID during the creation of new contact in my XML file? rather than typing the ID manually in the textbox ?
XML File:
<?xml version="1.0" encoding="UTF-8"?>
<CONTACTS>
<CONTACT>
<Customer-ID>N65539</Customer-ID>
<FirstName>Ben</FirstName>
<LastName>Foden</LastName>
<email></email>
<address></address>
<state>AZ</state>
<country>US</country>
</CONTACT>
<CONTACT>
<Customer-ID>N65539</Customer-ID>
<FirstName>Nimal</FirstName>
<LastName>Anup</LastName>
<email>nimal.anup#gmail.com</email>
<address></address>
<state>TN</state>
<country>IN</country>
</CONTACT>
<CONTACTS>
This XSLT copies the input XML as is and populate all Customer-ID element with a unique id:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Customer-ID">
<xsl:copy>
<xsl.value-of select="generate-id()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

using xsl:stream of XSLT 2.1

I have a javascript which converts which applies xsl to an xml.
I need to use XSLT 2.1 in order to use the feature xsl:stream.
Now the script is as follows :
main();
function main()
{
if ( WScript.Arguments.length != 3 )
{
WScript.Echo("Usage: runTransform.js <xslfilename> <xmlfilename> <outputfilename>");
WScript.Quit();
}
var xslfilename = WScript.Arguments.Item(0);
var xmlfilename = WScript.Arguments.Item(1);
var outputfilename = WScript.Arguments.Item(2);
var doc = LoadDOM(xmlfilename);
var xsl = LoadDOM(xslfilename);
var str = doc.transformNode(xsl);
var ado = new ActiveXObject("ADODB.Stream");
ado.Open();
ado.Position = 0;
ado.CharSet = "UTF-8";
ado.WriteText(str);
ado.SaveToFile(outputfilename, 2)
}
function LoadDOM(file)
{
var dom;
try {
dom = MakeDOM(null);
dom.load(file);
}
catch (e) {
alert("error" + e.description);
}
return dom;
}
function MakeDOM(progID)
{
if (progID == null) {
progID = "msxml2.DOMDocument.4.0";
}
var dom;
try {
WScript.Echo("ProgID is dom 4");
dom = new ActiveXObject(progID);
dom.async = false;
dom.validateOnParse = false;
//dom.resolveExternals = false;
}
catch (e) {
alert("MakeDom Error :" + e.description);
}
return dom;
}
function alert(str)
{
WScript.Echo(str);
}
The sample xml that I am trying is as follows :
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<transactions>
<transaction value="12.51"/>
<transaction value="3.99"/>
</transactions>
The xsl to be applied is as follows :
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="2.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:mode streamable="yes" />
<xsl:stream href="E:\test_folder_stream\transactions.xml">
<count>
<xsl:value-of select="count(transactions/transaction)" />
</count>
</xsl:stream>
</xsl:stylesheet>
The output which I get is as follows :
<?xml version="1.0"?>
Ideally I should get the output as :
<?xml version="1.0">
<count>2</count>
The script is executed as follows :
test.js transactions.xsl transactions.xml output.xml
The script does not throw any error .
Is the xsl that I have written incomplete ?
Any suggestions or links will be valuable .
Thanks in advance
Tazim.
I don't think there is any XSLT processor currently available which implements xsl:stream.
Saxon 9.3 implements many of the streaming features in the XSLT 2.1 (now XSLT 3.0) working draft, but not yet the xsl:stream instruction. I suspect you are using an XSLT 1.0 processor. If you specify version="2.1" in a stylesheet and throw it at an XSLT 1.0 processor, it will ignore any children of the xsl:stylesheet element that it doesn't understand.
In fact xsl:stream is defined in XSLT 3.0 as an instruction, so it can't appear as a child of xsl:stylesheet - it needs to go in a function or template.
If you need streamed processing, get yourself a copy of Saxon 9.3 Enterprise Edition, and read the documentation carefully to see what subset of the working draft is currently implemented. You won't be able to use it from JavaScript, unfortunately.

Categories