New to xslt, I wanted to set a value of string from java to this variable
<xsl:element name="input">
<xsl:attribute name="type">hidden</xsl:attribute>
<xsl:attribute name="name">trackId</xsl:attribute>
<xsl:attribute name="value"><xsl:value-of select="trackValue"/></xsl:attribute>
</xsl:element>
Is it in same manner as html or is it different apprach? Thanks for help and time.
Yes, you can pass values into your XSLT using parameters. What you would do is define a parameter near the top of your XSLT file:
<xsl:param name="trackValue" />
And then you would pass in a value for this when you run the transform:
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer xsltTransformer = transformerFactory.newTransformer(xsltSource);
xsltTransformer.setParameter("trackValue", parameterValue);
Then you can use it wherever you want to (note the use of the $ sign):
<xsl:attribute name="value"><xsl:value-of select="$trackValue"/></xsl:attribute>
XSL Transformation in Java with parameters
Related
I wish to compare two XML files and determine if they have the same structure i.e. The same type and number of tags with preferably the same attributes. The value of the tags and attributes may be different.
This code detects ALL the differences. Even if the structure is the same but values are different. I want to refine this to detect only the structural differences.
public static List compareXML(Reader source, Reader target) throws
SAXException, IOException{
//creating Diff instance to compare two XML files
Diff xmlDiff = new Diff(source, target);
//for getting detailed differences between two xml files
DetailedDiff detailXmlDiff = new DetailedDiff(xmlDiff);
return detailXmlDiff.getAllDifferences();
}
Try this XSLT 3.0:
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="text()"/>
<xsl:template match="#*">
<xsl:attribute name="name()"/>
</xsl:template>
<xsl:variable name="doc1">
<xsl:apply-templates select="doc('one.xml')"/>
</xsl:variable>
<xsl:variable name="doc2">
<xsl:apply-templates select="doc('two.xml')"/>
</xsl:variable>
<xsl:template name="xsl:initial-template">
<xsl:value-of select="deep-equal($doc1, $doc2)"/>
</xsl:template>
I have below xslt element which I am using for XML transformation.
<xsl:attribute name="{name()}" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" namespace="http://www.w3.org/2001/XMLSchema-instance">
<xsl:value-of select="." />
</xsl:attribute>
However after transformation the XML looks as below
<TEST xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" ns1:nil="true"/>
instead of xmlns:nsi it changes it to xmlns:ns1 and for other elements xmlns:ns0
Has anyone faced this issue before. when I transform though eclipse output is correct but if I transform it using java code it changes the xmlns prefix with ns0 ns1 etc...
Below is my java code for transformation.
StringWriter sw = new StringWriter();
javax.xml.transform.Result result = new javax.xml.transform.stream.StreamResult(sw);
TransformerFactory transFact = TransformerFactory.newInstance();
Transformer trans = transFact.newTransformer(xsltSource);
trans.transform(xmlSource, result);
After further analysis, when I transform using standalone java program the output is as below(as expected)
<TEST xmlns:nsi="http://www.w3.org/2001/XMLSchema-instance" nsi:nil="true"/>
but if I run it on server the output is as below
<TEST xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" ns1:nil="true"/>
xmlns:xsi is getting replaced with xmlns:xs1
You could try to create the attribute QName explicitely using the desired prefix instead of using the namespace attribute:
<xsl:attribute name="xsi:{local-name()}"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
select="."/>
In my xsl transform the filename is passed as a parameter to the stylesheet. I want to do a certain set of actions if it is in a certain filelist. Right now I'm doing it this way;
<xsl:param name="specialFiles" select="'|a.xml|b.xml|'"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="contains($specialFiles,concat('|',$FILENAME,'|'))" >
<xsl:apply-templates select="abc" />
</xsl:when>
.....
.....
This works, but quickly becomes messy when the specialFiles list grows. Is there a way to declare it like an array, and lookup quickly?
EDIT: This is the code I'm using to transform, I just print everything to stdout
TransformerFactory factory = TransformerFactory.newInstance();
Source xslt = new StreamSource(new File("1.xsl"));
Transformer transformer = factory.newTransformer(xslt);
File xmlFile = new File(args[0]);
String baseName = xmlFile.getName();
transformer.setParameter("FILENAME", baseName); // pass the basename of the file
transformer.transform(new StreamSource(xmlFile ), new StreamResult(System.out));
EDIT 2:
I just managed to do this in a slightly different way, I embed an xml fragment inside the stylesheet and use an xpath expression on it in
<specialFiles>
<name>abcdef.xml</name>
<name>sadfk32.xml</name>
</specialFiles>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="document('')/xsl:stylesheet/specialFiles/name/text()[contains(.,$fileName)]">
....
....
I used a xml fragment embedded within the stylesheet:
<specialFiles>
<name>abcdef.xml</name>
<name>sadfk32.xml</name>
</specialFiles>
So a simple xpath on it can be used to verify certain name is in the list or not,
<xsl:when test="document('')/xsl:stylesheet/specialFiles/name= $filename">
I have been reading through programming blogs about how to use Extended Functions with XSLT and Saxon, and can't seem to reference external Java functions in the XSLT successfully. I am getting the following error whenever I run the transform:
Cannot find a matching 0-argument function named {come.acme.javaxslt.business.CarBusiness}getModel()
at xsl:apply-templates (file:/C:/Users/Dave/workspace/acme-javaXSLT-demo/cars.xsl#18)
processing /cars/car[1]/model[1]
in built-in template rule
at xsl:apply-templates (file:/C:/Users/Dave/workspace/acme-javaXSLT-demo/cars.xsl#10)
processing /cars
EXCEPTION: net.sf.saxon.trans.XPathException: Cannot find a matching 0-argument function named {com.acme.javaxslt.business.CarBusiness}getModel()
; SystemID: file:/C:/Users/Dave/workspace/acme-javaXSLT-demo/cars.xsl; Line#: 30; Column#: -1
net.sf.saxon.trans.XPathException: Cannot find a matching 0-argument function named {com.acme.javaxslt.business.CarBusiness}getModel()
at net.sf.saxon.expr.ErrorExpression.evaluateItem(ErrorExpression.java:58)
at net.sf.saxon.expr.ErrorExpression.iterate(ErrorExpression.java:71)
at net.sf.saxon.expr.Atomizer.iterate(Atomizer.java:180)
...
Referenced Java Class:
package com.acme.javaxslt.business;
public class CarBusiness {
public static String getModel(){
return "/";
}
}
XSLT:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cars="com.acme.javaxslt.business.CarBusiness">
<xsl:template match="/">
<html>
<body>
<h2>My Car Collection</h2>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="car">
<p>
<xsl:apply-templates select="make"/>
<xsl:apply-templates select="model"/>
</p>
</xsl:template>
<xsl:template match="make">
Make: <span style="color:#ff0000">
<xsl:value-of select="."/></span>
<br />
</xsl:template>
<xsl:template match="model">
Artist: <span style="color:#00ff00">
<xsl:value-of select="cars:getModel()"/></span>
<br />
</xsl:template>
</xsl:stylesheet>
Use of Saxon in Java:
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer(new StreamSource(new File(xslID)));
transformer.transform(new StreamSource(new File(sourceID)), new StreamResult(new File("html/report.html")));
As soon as the above is run, the errors occur.
Even if I substitute my custom Java class for an inbuilt java method in the XSLT, I get the same error. So it's obvious that the XSLT is not hooking up with Java.
The recommended approach is to add -TJ to the JVM options, or do something with FeatureKeys.TRACE_EXTERNAL_FUNCTIONS, but I've searched high and low on the web to find out more information on how to do this so that I can see why my XSLT is not hooking up with external Java functions, to no avail.
Please, can someone help me figure out what my problem is?
This is happening for Saxon PE and HE, I haven't tried other versions yet.
Thanks!
I should have used an earlier version of Saxon, Saxon-B 9.1.0.8, as pointed out here:
http://sourceforge.net/p/saxon/discussion/94027/thread/33492ab5/
As soon as I put the older jar files in the path, then the above XSLT called the java function correctly.
I'm writing a dummy "MyAgenda" application in Java which has to allow maintenance of the XML file that stores the data.
Say I have a XML file like:
<myagenda>
<contact>
<name>Matthew Blake</name>
<phone>12345678</phone>
</contact>
</myagenda>
How can I add a new <contact> by using XSLT ?
Thanks.
Start with the identity transform, which transforms any XML document into itself.
The identity transform is a simple machine: given a tree, it copies every node it finds recursively. You're going to override its behavior for one specific node - the myagenda element - which it's going to copy in a different way.
To do this, add a template that matches the element that you want to update and duplicates it. In your case:
<xsl:template match="myagenda">
<xsl:copy-of select=".">
<xsl:apply-templates select="node() | #*"/>
</xsl:copy-of>
</xsl:template>
You might think, "wait isn't that the identity transform?" It is, but it's not going to stay that way.
Now decide on how you're going to get the new contact information into the transform. There are basically two ways: read it from a separate XML document using the document function, or pass the values into the transform using parameters. Let's assume that you're using parameters; in this case, you'd add the following to the top of your XSLT (right after the xsl:output element):
<xsl:param name="contactName"/>
<xsl:param name="contactPhone"/>
Now, instead of transforming myagenda into a copy of itself, you want to transform it into a copy of itself that has a new contact in it. So modify the template to do this:
<xsl:template match="myagenda">
<xsl:copy-of select=".">
<xsl:apply-templates select="node() | #*"/>
<contact>
<name><xsl:value-of select="$contactName"/></name>
<phone><xsl:value-of select="$contactPhone"/></phone>
</contact>
</xsl:copy-of>
</xsl:template>
If you wanted to get the name and phone out of a separate XML document in the file system, you'd start the XSLT with something like this:
<xsl:variable name="contact" value="document('contact.xml')"/>
<xsl:variable name="contactName" value="$contact/*/name[1]'/>
<xsl:variable name="contactPhone" value=$contact/*/phone[1]'>
That reads in contact.xml and finds the first name and phone element under the top-level element (using * in the pattern means that you don't care what the top-level element's name is).
use the xsl:param as a global parameter in the header of your xsl stylesheet.
<xsl:param name="newname"/>
<xsl:param name="newphone"/>
fill the new params with your xslt engine and then add the new item via a template:
(...)
<xsl:template match="myagenda">
<xsl:apply-templates select="contact"/>
<xsl:if test="string-length($newname)>0">
<xsl:element name="contact">
<xsl:element name="name">
<xsl:value-of select="$newname"/>
</xsl:element>
<xsl:element name="phone">
<xsl:value-of select="$newphone"/>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:template>
(...)
XSLT converts 1 xml file to another xml or text file.