I'm trying to chain named templates in XSLT 3.0 using the "xsl:original" feature, but I get a reserved-name namespace error.
As an example, I use a named template add-atts that is defined in two included stylesheets:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="3.0">
<xsl:template match="/">
<root>
<xsl:apply-templates select="root/line"/>
</root>
</xsl:template>
<xsl:template match="line">
<xsl:copy>
<xsl:call-template name="add-atts"/> <!-- USED HERE -->
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:import href="add-foo.xsl"/> <!-- DEFINED HERE -->
<xsl:import href="add-bar.xsl"/> <!-- DEFINED HERE -->
</xsl:stylesheet>
The first included stylesheet has a named template that adds a #foo attribute:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="3.0">
<xsl:template name="add-atts">
<xsl:attribute name="att1">foo</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
The second included stylesheet has a named template of the same name (which takes precedence due to ordering) that adds a #bar attribute, then attempts to call the overridden template from the previous file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="3.0">
<xsl:template name="add-atts">
<xsl:attribute name="att2">bar</xsl:attribute>
<xsl:call-template name="xsl:original"/> <!-- HERE -->
</xsl:template>
</xsl:stylesheet>
When I run this on the following input file
<root>
<line>1</line>
<line>2</line>
<line>3</line>
</root>
I get the following error:
$ java -jar .../Saxon-HE-9.9.1-4.jar in.xml trans.xsl
Static error in xsl:call-template/#name on line 8 column 46 of add-bar.xsl:
XTSE0080: Namespace prefix xsl refers to a reserved namespace
Errors were reported during stylesheet compilation
The xsl: namespace is declared, the XSLT version is set to 3.0, and the usage is the same as what's shown in this example from the deprecated XSL-LIST mailing list. What am I missing?
(The actual intent is to chain named templates when creating plugins for the DITA Open Toolkit.)
Thanks in advance!
The functionality exists in the context of the new XSLT 3 feature of packages, see https://www.w3.org/TR/xslt-30/#refer-to-overridden. Neither the spec nor that thread you have linked to seem to introduce this functionality in the context of simply xsl:imports.
To give you an example on how this can be used in XSLT 3 with packages, suppose you have a first package
<?xml version="1.0" encoding="UTF-8"?>
<xsl:package
name="http://example.com/packages/ex1"
package-version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:template name="add-atts" visibility="public">
<xsl:attribute name="att1">foo</xsl:attribute>
</xsl:template>
</xsl:package>
then you can write a second package overriding that named template and making use of <xsl:call-template name="xsl:original"/>:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:package
name="http://example.com/packages/ex2"
package-version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:use-package name="http://example.com/packages/ex1">
<xsl:override>
<xsl:template name="add-atts" visibility="public">
<xsl:attribute name="att2">bar</xsl:attribute>
<xsl:call-template name="xsl:original"/>
</xsl:template>
</xsl:override>
</xsl:use-package>
</xsl:package>
Finally you can use the second package:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="3.0">
<xsl:use-package name="http://example.com/packages/ex2"></xsl:use-package>
<xsl:output indent="yes"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="root/line"/>
</root>
</xsl:template>
<xsl:template match="line">
<xsl:copy>
<xsl:call-template name="add-atts"/> <!-- USED HERE -->
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
With Saxon from the command line, you would need to make sure you give the list of file names of the two packages with the -lib option e.g. -lib:package1.xsl;package2.xsl on Windows and -lib:package1.xsl:package2.xsl on Linux, when you run the main stylesheet.
Related
I'm dealing with a problem on how to map repeating xml elements, the moment I imported the XML below as an XML map in excel, I see only 1 for the 4 records i need "<club_LIST>" of course this doesn't produce the 4 entries in the output XML.
Any idea how can this be solved in Excel ?
I from Microsoft support:
Additionally, the contents of an XML mapping cannot be exported if the contents contain one of the following XML schema constructs:
List of lists One list of items contains a second list of items.
Any way around you could suggest to produce the xml?
Here below the sample of my data:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<PartnersProfile xmlns:xsi="http://www.w3.org/2001/xmlschema-instance">
<ID>10</ID>
<NAME>Table 10</NAME>
<Record>
<PARTNER_ID>1</PARTNER_ID>
<DESCRIPTION>customer</DESCRIPTION>
<subscription>2004</subscription>
<club_LIST>1</club_LIST>
<club_LIST>4</club_LIST>
<club_LIST>6</club_LIST>
<club_LIST>9</club_LIST>
</Record>
<Record>
<PARTNER_ID>1</PARTNER_ID>
<DESCRIPTION>customer</DESCRIPTION>
<subscription>2004</subscription>
<club_LIST>1</club_LIST>
<club_LIST>4</club_LIST>
<club_LIST>6</club_LIST>
<club_LIST>9</club_LIST>
</Record>
</PartnersProfile>
Consider transforming the XML to an itemized version by each club_LIST with repeating values for ancestor elements. You can run the below XSLT 1.0 by many tools and programming languages including Perl (from your profile) or Excel VBA.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/PartnersProfile">
<xsl:copy>
<xsl:apply-templates select="descendant::club_LIST"/>
</xsl:copy>
</xsl:template>
<xsl:template match="club_LIST">
<Record>
<xsl:copy-of select="ancestor::PartnersProfile/*[name()!='Record']"/>
<xsl:copy-of select="ancestor::Record/*[name()!='club_LIST']"/>
<club_LIST><xsl:apply-templates select="node()"/></club_LIST>
</Record>
</xsl:template>
</xsl:stylesheet>
Online Demo
I'm using Azure Logic App to transform a CSV file to XML, everything was initially set up in BizTalk first to generate the relevant XSDs and XSL which worked perfectly fine. But when I use Azure Logic App the output XML file is all in one line even though I made sure it has indent="yes" in the XSL file.
I know I can use notepad++ to pretty print the result and save the file, but surely there's a way to automatically do that in Logic App?
For those interested, I've found a setting within the Logic App, simply select Apply XSLT output attributes and that's it, no validation needed either!
I manage to get indentation when using XSLT 3.0 with e.g. the stylesheet/map doing
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
<xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:template>
</xsl:stylesheet>
then a request of e.g.
<root><item>a</item><item>b</item></root>
is transformed to the output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>a</item>
<item>b</item>
</root>
<!--Run with SAXON HE 9.8.0.8 -->
I don't know how they run the XSLT 1.0 processor to ignore the xsl:output settings, seems a flaw or quirk in the pipeline.
DITA 3.6
Oxygen XML Editor 23.1
The "DITA for Print" book nor any other source, so far, has helped me produce a pdf where the topics are consecutive rather than having a blank page after them. To each topicref in the ditamap, I added outputclass="page-break-avoid". To each topic element in each file, I added outputclass="page-break-avoid".
Should I add something in an xsl file? Can you point me to the answer?
The blank page is generated from the definition fo:page-sequence/#force-page-number property.
To change not to generate blank-page, you can override org.dita.pdf2/cfg/fo/attrs/commons-attr.xsl.
<xsl:attribute-set name="__force__page__count">
<xsl:attribute name="force-page-count">
<xsl:choose>
<xsl:when test="/*[contains(#class, ' bookmap/bookmap ')]">
<xsl:value-of select="'even'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'auto'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:attribute-set>
Change the value even to auto will satisfy your needs.
Steps to customize
I'm not familiar with PDF2 plug-in. So this may not the standard way. But I could customize it by following steps.
Make customization XSL file
[DITA-OT]/plugins/org.dita.pdf2/Customization/fo/attrs/commons-attr.xsl
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:rx="http://www.renderx.com/XSL/Extensions"
version="2.0">
<xsl:attribute-set name="__force__page__count">
<xsl:attribute name="force-page-count">
<xsl:choose>
<xsl:when test="/*[contains(#class, ' bookmap/bookmap ')]">
<xsl:value-of select="'auto'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'auto'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:attribute-set>
</xsl:stylesheet>
Include it into [DITA-OT]/plugins/org.dita.pdf2/Customization/fo/attrs/custom.xsl
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="2.0">
<xsl:include href="commons-attr.xsl"/>
</xsl:stylesheet>
Customize [DITA-OT]/plugins/org.dita.pdf2/Customization/catalog.xml
<?xml version="1.0" encoding="utf-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
<!-- Custom attributes entry -->
<uri name="cfg:fo/attrs/custom.xsl" uri="fo/attrs/custom.xsl"/>
</catalog>
Specify customization.dir property with [DITA-OT]\plugins\org.dita.pdf2\Customization in Oxygen transformation scenario.
Apply transformation scenario from Oxygen.
I've got the following temp/topic.fo. The fo:page-sequence/#force-page-count became auto and in the result PDF, the redundant blank pages are removed.
I have below xml file which is coming form my vendor.
<?xml version="1.0" encoding="UTF-8"?>
<nm:MT_employee xmlns:nm="http://firstscenario.com"xmlns:tl="http://secondscenario.com">
<EMployeeDetails>
<Name>Janardhan</Name>
<id>1234</id>
<Address>India</Address>
</EMployeeDetails>
<tl:Extension>
<tl:Number>5678</tl:Number>
<tl:Salary>2345678</tl:Salary>
</tl:Extension>
</nm:MT_employee>
In the above xml I want to ignore the entire tl:Extension node. the final output should be like below
<?xml version="1.0" encoding="UTF-8"?>
<nm:MT_employee xmlns:nm="http://firstscenario.com"xmlns:tl="http://secondscenario.com">
<EMployeeDetails>
<Name>Janardhan</Name>
<id>1234</id>
<Address>India</Address>
</EMployeeDetails>
</nm:MT_employee>
I tried to with different XSLT codes but it's not working. Could you please suggest how can I achieve this?
Regards,
Janardhan
The general rule to "ignore" an element from the source XML
is to write an "empty" template for this element, in your case:
<xsl:template match="tl:Extension"/>
As this template refers to tl namespace, it must be specified
in the xsl:transform tag.
Of course, to copy the rest of the source content, your script
must include the identity template.
Below you have an example script:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tl="http://secondscenario.com" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="tl:Extension"/>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
</xsl:transform>
I have some XML this looks like this:
<region class="TableInfo">
text
</region>
<region>
text
</region>
I want to write XSL that only preserves that part without the class="TableInfo".
I've tried a number of different ways, including:
<xsl:for-each select="region[class!='TableInfo']">
</xsl:for-each>
and
<xsl:for-each select="region">
<xsl:if test="not(class='TableInfo')">
</xsl:if>
</xsl:for-each>
and several variations thereof. it seems like it's somehow evaluating as a value rather than a string, because when I set it up as an != test, all the content gets deleted, and when I set it up as a not(), nothing gets deleted. any help?
thanks!
<xsl:for-each select="region[not(#class='TableInfo')]">
</xsl:for-each>
You forgot the # on class, so you were trying to check for class elements instead of the attributes. And apparently the != is not working as well, so I swapped in the not() function instead.
From a stylistic point, I would also suggest looking into using templates that match the region elements so you can use apply-templates instead of a for-each.
The identity rule is your friend (and of course, you need to specify the attribute class, not a "class" element):
<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:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*"><xsl:apply-templates/></xsl:template>
<xsl:template match="region[#class='TableInfo']"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML (fragment wrapped into a single top element to make it a well-formed XML document):
<region>
text
</region>