XSLT to nest one schema within another - xslt

I use an XSLT crosswalk to transform our standard in-house metadata XML to PBCore.
My prologue at the top of the xslt looks like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="MediaAsset">
<pbcoreCollection
xmlns="http://www.pbcore.org/PBCore/PBCoreNamespace.html"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.pbcore.org/PBCore/PBCoreNamespace.html
http://pbcore.org/xsd/pbcore-2.0.xsd">
Now I have a client asking for our PBCore metadata to be added to their own MODS metadata that they provide to us. I know that to properly next our PBCore within their MODS, all my tags have to have a pbcore: prefix. So my question is: how do I adapt my PBCore XSLT that I run our standard XML through to include the prefix in the output file? If I can do that then I can just copy the PBCore metadata and paste it into their MODs metadata, and just add
xmlns:pbcore="http://www.pbcore.org/PBCore/PBCoreNamespace.html"
xsi:schemaLocation="http://www.loc.gov/mods/v3
http://www.loc.gov/standards/mods/v3/mods-3-6.xsd
http://www.pbcore.org/PBCore/PBCoreNamespace.html
http://pbcore.org/xsd/pbcore-2.0.xsd">
to the prologue of the final output XML.

Rather than modifying your existing XSLT code, why not add another step to the pipeline that modifies the output of the existing stylesheet to meet the new requirements?
It's not entirely clear to me what the new requirements are. You talk about namespace prefixes needing to be changed, but generally speaking, namespace prefixes are purely cosmetic: it's namespace URIs that matter.

Related

How to preserve processing instructions whilst producing XSL-FO in eXist-db?

Is there a way to make eXist to return processing instruction as it is? It seems it somehow ignores it in the output.
Processing instructions are very useful if I use XEP as my rendering engine, hence it would be great to be able to preserve them before the root of the XSL-FO document or immediately after its start.
If I have in the template:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:tei="http://www.tei-c.org/ns/1.0" version="2.0">
<xsl:template match="/">
<fo:root>
<?xep-pdf-page-layout two-columns-right?>
It simply returns:
<fo:root xmlns:tei="http://www.tei-c.org/ns/1.0" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
I’ve tried to touch preserving of processing instructions with:
declare option exist:serialize "method=xhtml media-type=text/xml process-xsl-pi=yes"; in the prolog of my query but to no avail.
UPDATE I
The steps of my scenario are:
loading the document from the DB
passing it to the transform:transform() function
whilst calling the transform:transform() function, I pass to it one basic stylesheet, which includes other stylesheets
I need to add the processing instruction to a stylesheet for pages-masters, which covers the root of the FO document later used by the rendering engine. This stylesheet is included (<xsl:include/>) into the basic one (gathering all the stylesheets) and passed to the function.
With XSLT, to output a processing instruction use https://www.w3.org/TR/xslt/#creating-processing-instructions, that is <xsl:processing-instruction name="xep-pdf-page-layout">two-columns-right</xsl:processing-instruction> to have the pi <?xep-pdf-page-layout two-columns-right?> in the transformation result.

Copy elements with Namespaces using XSLT

I want to copy the namespaces in the elements. The namespace attribute and its value may vary and can occur in any element. But I want to copy the namespace as it is. Also I should not include any attribute as additional to copy the namespace. I am using Saxon 9(he) XSLT processor for transformations
In the below XML file, I am getting the element <ct-ext:case> with the "xmlns:ct-ext" attribute missing. I tried copy-namespaces="yes", yet I am not getting the correct output. I am writing a common XSLT for various DTDs.
Sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<ct:doc identifier="GPPCIA702661235" xsi:schemaLocation="http://test.com/test test.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ct="http://test.com/test" xmlns="http://www.w3.org/1998/Math/MathML" xmlns:ct-ext="http://test.com/test-ext">
<ct:doc-meta identifier="EHIXRW383636159">
<ct:para><ct:inline-math identifier="RCSNDD453018159"><math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"><mrow><mi>$</mi><mn>1.65</mn></mrow></math></ct:inline-math></ct:para>
<ct-ext:case identifier="CDVOXU875594216" xmlns:ct-ext="http://test.com/test-ext">
<ct:simple-meta identifier="HNKRFT326435269">
<ct:title identifier="CGSVLX990515344">This is title</ct:title>
</ct:simple-meta>
</ct-ext:case>
</ct:doc-meta>
</ct:doc>
Output Required:
<?xml version="1.0" encoding="UTF-8"?>
<ct:doc identifier="GPPCIA702661235" xsi:schemaLocation="http://test.com/test test.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ct="http://test.com/test" xmlns="http://www.w3.org/1998/Math/MathML" xmlns:ct-ext="http://test.com/test-ext">
<ct:doc-meta identifier="EHIXRW383636159">
<ct:para><ct:inline-math identifier="RCSNDD453018159"><math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"><mrow><mi>$</mi><mn>1.65</mn></mrow></math></ct:inline-math></ct:para>
<ct-ext:case identifier="CDVOXU875594216" xmlns:ct-ext="http://test.com/test-ext">
<ct:simple-meta identifier="HNKRFT326435269">
<ct:title identifier="CGSVLX990515344">This is title</ct:title>
</ct:simple-meta>
</ct-ext:case>
</ct:doc-meta>
</ct:doc>
XSLT tried:
<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ct="http://test.com/test" xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:ct-ext="http://test.com/test-ext">
<xsl:output method="xml" encoding="UTF-8" indent="no"/>
<xsl:template match="#*|node()">
<xsl:copy copy-namespaces="yes">
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Whether you use copy-namespaces or not does not matter (yes is the default anyway), if the input XML has a duplicated namespace declaration on an inner element node then the data model the XSLT processor operates on is not different from the one produced if the inner namespace declaration were not present.
So
<root xmlns:ct-ext="http://test.com/test-ext">
<ct-ext:case xmlns:ct-ext="http://test.com/test-ext">...</ct-ext:case>
</root>
does not differ from
<root xmlns:ct-ext="http://test.com/test-ext">
<ct-ext:case>...</ct-ext:case>
</root>
Consequently when serializing the copied result tree the originally duplicated namespace declaration is lost. I don't think there is a way with XSLT to prevent that.
To elaborate on what Martin said: you are putting requirements on XML tools (like XSLT) that they're not designed to fulfill.
It will help if we use more precise terminology. What you're asking to copy are not namespaces, but rather namespace declarations.
XML tools are designed to be able to produce specified XML elements (and other nodes) in the namespaces that you specify them to be in. This is part of the XML information model.
XML tools are not required to let you specify what namespace prefixes to use, or where to put namespace declarations, so long as the output XML has the right elements and attributes in the right namespaces.
So the requirements you're specifying should not be necessary for any downstream XML consumer. Maybe if you explain why you want namespace declarations to come out in a certain way, we can help you find ways to achieve those purposes, ways that are compatible with how XML namespaces are designed to work.

declare global variable in project and use it in xslt

I would seek this your guidance for doubt in XSLT. In my current project there is a requirement to create many XSLT files. In these transformations, there are few common steps performed; for.eg. changing uppercase of an element value from input xml. I'm currently using the below code in an XSLT, so if there are 50 XSLT being created then this code will be duplicated.
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:message>UPPERCASE is <xsl:value-of select="translate($MsgType, $smallcase, $uppercase)" /></xsl:message>
Requesting your advice on how to avoid code duplication. Can I create a common XML file such as utility and declare the variables uppercase and smallcase and shall I call these variables inside the xslt. Similar to other prog. lang like java where I can declare a common function globally and use it in different classes. Basically I would like to know whether it is possible to declare globally and use it in all the xslt.
I would use <include/> to include the XSLT file with all your global variables defined.
See also http://www.w3.org/TR/xslt#element-include
Put all your variables into the file "my_global_variables.xsl":
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:variable name="myVariable" select="'xyz'"/>
<!-- more variables to add -->
</xsl:stylesheet>
Your main stylesheet looks like this then, including the "my_global_variables.xsl":
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:include href="my_global_variables.xsl"/>
<xsl:template match="/">
</xsl:template>
</xsl:stylesheet>
There is also the <import> element with which you can import stylesheets. An imported style sheet has lower precedence than the importing style sheet though - so in your case I would use <include>.
Requesting your advice on how to avoid code duplication. Can I create
a common XML file such as utility and declare the variables uppercase
and smallcase and shall I call these variables inside the xslt.
<xsl:import> and <xsl:include> are the two XSLT instructions especially designed for this task.
Global variables (children of an xsl:stylesheet element) in a stylesheet module are accessible in the stylesheet that includes this stylesheet. The rules with importing are a little bit more complicating, but if there are no naming conflicts between global variables from imported stylesheets, they are all accessible from the importing stylesheet.
Finally, I recommend not to use www.w3schools.com -- see why at: http://www.w3fools.com

DocBook XSL - ok to edit the original stylesheets?

I would like to make certain lines of a DocBook table of contents bold based on whether the particular <section> has a certain attribute defined. This is easy by adding an <xsl:if test="..."> statement to the DocBook XSL (fo/autotoc.xsl lines 187-230 -- this is for output to PDF using XMLMind).
I'm wondering, though, if it's bad practice to edit the DocBook XSLs themselves. I have other customizations in a separate XSL of my own, mostly setting parameters, but I can't imagine how I would introduce this conditional logic--based on which line of the TOC is currently being processed--without putting some sort of code in the originals. Any thoughts? How do you upgrade to a newer DocBook XSL after making changes?
Import the docbook stylesheets from your own XSLT. Then, (re)define the Docbook template that you want to "override".
Since your template will be the highest in the import tree, it will take precedence.
By doing it that way you don't have to modify any of the core docbook XSLT files. It will make upgrades of the Docbook stylesheets easier in the future.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="xsl/fo/docbook.xsl"/>
<xsl:template match="template-that-you-need-to-redefine">
...
</xsl:template>
</xsl:stylesheet>

How do I get the current folder path within an XSLT file?

Is there a way to get the current folder path from within a XSLT file?
I need it to locate other XML and XSLT files. We have different customer folders and will need to successfully find the correct files.
You can send it into the style-sheet from outside using xsl:param. Then you need to determine what the current path is when invoking the from the outside ;)
In MSXSL on Windows, you can use a script extension like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://tempuri.org/msxsl"
>
<msxsl:script language="JScript" implements-prefix="user">
<![CDATA[
var fso = new ActiveXObject("Scripting.FileSystemObject");
function getCurrentPath(){
return fso.GetFolder(".").Path
}
]]>
</msxsl:script>
<xsl:template match="/">
<xsl:value-of select="user:getCurrentPath()"/>
</xsl:template>
</xsl:stylesheet>
Other XSL processors support similar methods to use external resources (scripting languages, function libraries etc.), so this is just an example.
Is there a way to get the current
folder path from within a xslt file?
Need it to locate other xml and xslt
files
No need for any extension functions or even parameters to do that!
Any relative URLs used in the href attribute of an <xsl:import> or <xsl:include>
instruction are resolved based on the URL of the current XSLT stylesheet -- it only needs to have an URL, which is vlearly stated as true in the question above. This is very convenient in importing/including XSLT stylesheets.
The document() function also will resolve a relative URL in a similar way, thus making any additional XML document accessible using anrelative URL.
Lastly, here is an example how this facilities are massively used in a big library of XSLT functions and templates (FXSL 2.x):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://fxsl.sf.net/"
exclude-result-prefixes="xs xdt f"
>
<!--
This module contains the FXSL versions of the "standard" XPath functions
These are intended as convenience functions, so that they can be passed
as parameters to other functions (e.g. to f:zipWith())
or curried and passed as parameters (e.g. to f:map())
-->
<xsl:import href="func-curry.xsl"/>
<xsl:import href="func-compose-flist.xsl"/>
<xsl:import href="func-standardArithmeticXpathFunctions.xsl"/>
<xsl:import href="func-standardBooleanXpathFunctions.xsl"/>
<xsl:import href="func-standardStringXpathFunctions.xsl"/>
<xsl:import href="func-standardNodesXpathFunctions.xsl"/>
<xsl:import href="func-standardSequencesXpathFunctions.xsl"/>
<xsl:import href="func-standardAggregateXpathFunctions.xsl"/>
<xsl:import href="func-standardDateTimeXpathFunctions.xsl"/>
<xsl:import href="func-standardXSLTXpathFunctions.xsl"/>
<xsl:import href="func-standardAxisXpathFunctions.xsl"/>
</xsl:stylesheet>
This may work for your setup:
<xsl:value-of select="system-property('user.dir')"/>
For example,
<xsl:value-of select="document(concat(system-property('user.dir'),'/',filename,'.xml'))//title[1]"/>
no...
but you could maybe workaround the problem by using relative URLs and/or passing parameters into the stylesheet.
In most XSLT processor, you can add custom functions as extensions. For example here is Saxon's documentation how to do that.
Not AFAIK (though you could always pass it as a param to the transform), but I'm not clear why relative paths won't work for you here.