I am trying to convert xml into pdf using xsl for some business requirement(can't share that code here). My requirement is to add a page break after completion of xml reading. Here we are reading that same xml two times and adding that result into a single pdf. For example , after reading the xml for the first time we get a pdf of 2 pages, So on reading the same xml second time , I want my result to start from third page of pdf and not on the second page. Any help would be appreciated.
Sample xml:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artit>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
</cd>
</catalog>
Say Using loop, I am reading this same xml twice and I want the result of each iteration of loop on separate pages.
Generate an fo:page-sequence (https://www.w3.org/TR/xsl11/#fo_page-sequence) for each catalog. By definition, each fo:page-sequence starts a new page.
Alternatively, the second time that you process the XML, add break-before="page" (https://www.w3.org/TR/xsl11/#break-before) or page-break-before="always" (https://www.w3.org/TR/xsl11/#page-break-before, and a shorthand for break-before="page") to the FO generated from the appropriate element.
Related
I am writing my first XSLT code to convert XML file.
Below is the sample code just to explain the issue here.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="urn.abc.xyz" xmlns:ns2="abc:cde">
<xsl:template match="/catalog">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
<th>Price</th>
</tr>
<xsl:for-each select="catalog/cd">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
<td><xsl:value-of select="price"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
above code works well when all the namespace is having prefix in input XML. but fails to work when namespace prefix is missing in XML , i.e., execution just shows xml content without tag names.
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="urn.abc.xyz" xmlns:ns2="abc:cde">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
</catalog>
output for above XML:
Title Artist Price
with prefix:
<catalog xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="urn.abc.xyz" xmlns:ns2="abc:cde">
without prefix
<catalog xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="urn.abc.xyz" xmlns:ns2="abc:cde">
if i run the XSL without namespace prefix (abc)
then output looks like below
Empire Burlesque Bob Dylan USA Columbia 10.90 1985
can anyone help why my xsl is not working when namespace prefix is empty? and what changes needed in my xsl so that it will work even though namespace prefix is missing.
A declaration of a namespace without a prefix defines a default namespace, and it places all unprefixed elements within the scope of such declaration in that namespace.
In order to address such elements in your stylesheet, you must declare the same namespace, assign it a prefix and use that prefix for all unprefixed elements in the source XML.
For example, if the input XML is:
<catalog xmlns="urn.abc.xyz">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
</catalog>
then your stylesheet needs to be:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abc="urn.abc.xyz"
exclude-result-prefixes="abc">
<xsl:template match="/abc:catalog">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
<th>Price</th>
</tr>
<xsl:for-each select="abc:cd">
<tr>
<td><xsl:value-of select="abc:title"/></td>
<td><xsl:value-of select="abc:artist"/></td>
<td><xsl:value-of select="abc:price"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
(you can use any prefix you want).
However, this stylesheet is NOT suitable for processing an input whose elements are in no-namespace:
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
</catalog>
It is technically possible to construct a stylesheet that would ignore the namespace and process both XMLs by using only the local-names of the elements. However, this is not recommended unless absolutely necessary. The assumption is that namespaces are there for a reason, and ignoring them may lead to incorrect results.
Note also that namespace declarations that do use a prefix, such as xmlns:ns2="abc:cde" in your example, are applied only to nodes that have the same prefix. In the absence of such nodes, such declaration has no effect and is entirely redundant (and irrelevant to your question).
In XSLT 1.0, if the elements in your source document are in a namespace, you must use namespace prefixes in the XPath expressions in your XSLT.
In your source document you declare "urn.abc.xyz" as the default namespace URI. (Incidentally, "urn.abc.xyz" is not really a proper namespace URI; it should be an absolute URI, including a URI scheme, e.g. "urn:abc.xyz" or something, but that's not your problem here).
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="urn.abc.xyz">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
</catalog>
(NB I removed the unused XSLT and "ns2" namespace declarations since they weren't used in the file anywhere).
In your XSLT you have a template with an XPath match expression = /catalog, but because catalog has no namespace prefix, it would only match a catalog element in no namespace. To match a catalog element in the urn.abc.xyz namespace you would need to first associate a namespace prefix (which could be anything, e.g. my-namespace) with the urn.abc.xyz namespace URI, and then use the XPath expression "/my-namespace:catalog" in your template's match attribute. To bind the urn.abc.xyz namespace URI to a prefix my-namespace, add a namespace declaration to the stylesheet element: xmlns:my-namespace="urn.abc.xyz"
Then within the template, your xsl:for-each statement would refer to the child elements using the expression my-namespace:cd, and similarly you'd need to use my-namespace:title, etc. to refer to the child elements of the cd element.
XSLT Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="catalog/cd[artist='Bob Dylan']">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
</tr>
</xsl:for-each>
**<xsl:value-of select="sum(catalog/cd[artist='Bob Dylan' and extra[not(contains(tests/test,'CD'))]]/price)"/>**
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
XML Code:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<extra>
<tests>
<test>CDEF</test>
</tests>
<tests>
<test>QWER</test>
</tests>
<tests>
<test>UIOP</test>
</tests>
</extra>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Red</title>
<artist>Bob Dylan</artist>
<country>UK</country>
<extra>
<tests>
<test>CDEF</test>
</tests>
<tests>
<test>QWER</test>
</tests>
<tests>
<test>UIOP</test>
</tests>
</extra>
<company>London</company>
<price>7.80</price>
<year>1987</year>
</cd>
<cd>
<title>Unchain my heart</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<extra>
<tests>
<test>ABXY</test>
</tests>
<tests>
<test>QWER</test>
</tests>
<tests>
<test>CDOP</test>
</tests>
</extra>
<company>EMI</company>
<price>8.5</price>
<year>1987</year>
</cd>
</catalog>
In the XSLT, I want to take the sum of all that have artists as Bob Dylan, and I want to make sure that I only retrieve test that don't contain CD in their string.
Based on the result, I am taking 8.5. From what I found, my query ignores values from 2-n. What do I need to do to account for them?
I've tried extra[not(contains(tests/test,'CD'))] not(contains(extra/tests/test))], extra/tests[not(contains(tests,CD))]
I've been grasping at straws, so I'd love to have someone enlighten me. Thanks in advance!
In your original XSLT you have this in your expression...
extra[not(contains(tests/test,'CD'))]
contains is a string function, which takes 2 strings as arguments. If you pass it a node, it will convert that to a string first. In this case though, you are passing a node-set. In XSLT 1.0, it will convert only the first node in that to a string which is why it doesn't pick up your last cd. (In XSLT 1.0, passing in a node-set consisting of more than one node throws an error).
You need to do this, so that contains is applied as a condition to each test:
<xsl:value-of select="sum(catalog/cd[artist='Bob Dylan' and extra[not(tests/test[contains(., 'CD')])]]/price)"/>
Or you could slightly simplify it to this...
<xsl:value-of select="sum(catalog/cd[artist='Bob Dylan' and not(extra/tests/test[contains(., 'CD')])]/price)"/>
I want to modify processing instructions in a source xml with XSLT, for example:
XML INPUT
<?xml version="1.0" encoding="UTF-8"?>
<root>
</root>
XML OUTPUT
<?xml version="1.0" encoding="WINDOWS-1252"?>
<root>
</root>
Can I do this with XSLT?
thanks in advance.
That is the XML declaration, it is not a processing instruction. If you want a particular output encoding then use e.g. <xsl:output encoding="Windows-1252"/>. But any XML parser is required to support UTF-8 so using an 8-bit code page in the age of Unicode and XML does not improve interoperability.
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
XSLT for replace attribute in XML element if it exists in another XML?
I have two XML:
First XML:
<FirstXML>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
</catalog>
</FirstXML>
Second XML:
<SecondXML>
<catalog>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
</SecondXML>
I want to Transform my First XML using XSLT. The cd node of first xml should be replaced with cd node of second xml.
Resulting XML from XSLT transformation:
<FirstXML>
<catalog>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
</FirstXML>
Please help me with XSLT for this. I think we would need to pass the Second XML as a parameter to the XSLT, I am trying to do that. I am very new to XSLT so might not be coding it correctly.
Any inputs on how we can do this would be helpful. Thanks in Advance.
I think we would need to pass the Second XML as a parameter to the
XSLT.
This is possible, but not necessary at all.
A more convenient way is to pass to the transformation just the filepath to the document and use the XSLT document() function for parsing and accessing it:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pDocLink" select=
"'file:///c:/temp/delete/SecondXml.xml'"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="document($pDocLink)/*/*[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided "first" document:
<FirstXML>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
</catalog>
</FirstXML>
and the "second" document is placed at: c:\temp\delete\SecondXml.xml :
<SecondXML>
<catalog>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
</SecondXML>
the wanted, correct result is produced:
<FirstXML>
<catalog>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
</FirstXML>
There is a document function, use it to work with several input sources.
The description of your problem is too vague to make more detailed recommendations. Try to search some tutorials, e.g. http://www.ibm.com/developerworks/xml/library/x-tipcombxslt/
You should be looking at the document() function. Passing an XML tree in as a parameter works is some XML systems, not others.
If you have a variable such as
<xsl:variable name="source" select="document('filename.xml')//catalog"/>
then accessing the variable $source would give you the root of your CD branch
EDIT
Just for clarity of my answer to Dimitre Novatchev, and as I said you could do it, although in 90% of times you would want to use the document function, it is possible to pass a node fragment in through to a stylesheet from a C# application with AddParam, and could be used when you are generating that data from your own application, and don't need to write it (or where it isn't practical).
So for a stylesheet such as:
<?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="xml" indent="yes"/>
<xsl:param name="bonnie_tyler"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="cd[ancestor::FirstXML]">
<xsl:copy>
<xsl:apply-templates select="msxsl:node-set($bonnie_tyler)//cd/*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
the First and Second XML as per your original question, you could use the pass the nodes in such as:
try
{
XslCompiledTransform xt = new XslCompiledTransform();
Assembly ass = Assembly.GetEntryAssembly();
Stream strm = ass.GetManifestResourceStream("ConsoleApplication1.testparam.xslt");
XmlTextReader xmr = new XmlTextReader(strm);
xt.Load(xmr);
xmr.Close();
XmlDocument originalDocument = new XmlDocument();
strm = ass.GetManifestResourceStream("ConsoleApplication1.FirstXML.xml");
originalDocument.Load(strm);
XmlDocument replacementNode = new XmlDocument();
strm = ass.GetManifestResourceStream("ConsoleApplication1.SecondXML.xml");
replacementNode.Load(strm);
XsltArgumentList arg = new XsltArgumentList();
arg.AddParam("bonnie_tyler", "", replacementNode);
StringBuilder htmlOutput = new StringBuilder();
XmlWriter writer = XmlWriter.Create(htmlOutput);
xt.Transform(originalDocument, arg, writer);
Console.Out.Write(htmlOutput.ToString());
}
which would give you your desired output (although obviously you wouldn't be loading the files from disk as here)
Below is the input xml :
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="value-of.xsl"?>
<MemeberDetails>
<Employee>
<Name>Madhu</Name>
<Sex>Male</Sex>
<DOB>2/10/1982</DOB>
<Address>JP Nagar ,Bangalore</Address>
<MemberId>094631</MemberId>
<Designation>SSE</Designation>
<Department>SG</Department>
</Employee>
</MemeberDetails>
where, i am referring value-of.xsl file using HREF in above xml. and this file is residing in same folder.
Below is the value-of.xslt file:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
<CGIEmployeeDetails>
<PersonalDetails>
<Name>
<xsl:value-of select="/MemeberDetails/Employee/Name"/>
</Name>
<Gender>
<xsl:value-of select="/MemeberDetails/Employee/Sex"/>
</Gender>
<ResidentialAddress>
<xsl:value-of select="/MemeberDetails/Employee/Address"/>
</ResidentialAddress>
</PersonalDetails>
<WorkingDetails>
<PSAID>
<xsl:value-of select="//MemberId"/>
</PSAID>
<Designation>
<xsl:value-of select="/MemeberDetails/Employee/Designation"/>
</Designation>
<Department>
<xsl:value-of select="/MemeberDetails/Employee/Department"/>
</Department>
</WorkingDetails>
</CGIEmployeeDetails>
</xsl:template>
</xsl:stylesheet>
When i run above xml in browser , the output will result as text but not as XML .
If i use editor like Oxygen and transform the same xml file , the output will be XML.
I am not getting why browser is failing to transform a XML output ?
Is there anything to do with browser ?
In browsers, the "XML format" view is mostly a stylesheet adding syntax highlighting and Emacscript event handlers (show and hide chlids nodes, etc.).
So, when the document has a XSLT stylesheet PI, browsers don't run that "XML format" stylesheet but they try to render the transformation result. This intent is not the same for each browser. Only one thing is guaranteed: if it's proper XHTML or HTML, is render as is.
If the transformation result is not proper XHTML nor HTML (plain text, other XML vocabulary), the render mechanism varies from one to another: i.e Chrome is the only one showing nothing for unknown XML vocabulary, others render this as HTML anyway (rendering only text).
Are you using internet explorer? That is the only browser I know of that would completely ignore your XSL stylesheet
#Alejandro provided a good explanation.
Using IE, you can see the result of the XSLT transformation by right-clicking on the IE window and selecting "View Source"