How to add playOrder in toc.ncx using XSLT? - xslt

If I decide to add a navpoint element in toc.ncx at the beginning of an existing toc.ncx in navmap, there is no way to reorder playOrder number but by hand. That could be really tedious if there are many navpoint elements.
Input
<?xml version="1.0" encoding="UTF-8"?>
<ncx version="2005-1" xmlns="http://www.daisy.org/z3986/2005/ncx/">
<head>
<meta name="dtb:uid" content="9781315348674" />
<meta name="dtb:depth" content="1" />
<meta name="dtb:totalPageCount" content="144" />
<meta name="dtb:maxPageNumber" content="144" />
</head>
<docTitle>
<text>Making Choices for Health Care</text>
</docTitle>
<navMap>
<navPoint id="nav-1">
<navLabel>
<text>Cover</text>
</navLabel>
<content src="xhtml/A01_cover.xhtml"/>
</navPoint>
<navPoint id="nav-2">
<navLabel>
<text>Half Title</text>
</navLabel>
<content src="xhtml/A02_halftitle.xhtml"/>
</navPoint>
</navMap>
</ncx>
Assuming Output Like:
<navPoint id="nav-1" playOrder="1">
<navLabel>
<text>1</text>
</navLabel>
<content src="Text/Section0002.xhtml"/>
</navPoint>
<navPoint id="nav-2" playOrder="2">
<navLabel>
<text>2</text>
</navLabel>
<content src="Text/Section0003.xhtml"/>
</navPoint>
XSLT code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- Recursive copy template -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="navPoint">
<xsl:copy>
<xsl:attribute name="playOrder">1</xsl:attribute>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="#playOrder">
<xsl:attribute name="playOrder"><xsl:number count="*[#playOrder]" level="any"/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
This code is not working and can you please tell me the correct code

Use this:
<xsl:attribute name="playOrder"><xsl:number count="navPoint" level="any"/>/xsl:attribute>
instead of
<xsl:attribute name="playOrder">1</xsl:attribute>
and remove template
<xsl:template match="#playOrder">
<xsl:attribute name="playOrder"><xsl:number count="*[#playOrder]" level="any"/></xsl:attribute>
</xsl:template>
See transformation at https://xsltfiddle.liberty-development.net/94hvTyV/2

Assuming you just want normal 1,2,3 numbering, you could count() preceding nodes.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Recursive copy template -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="navPoint">
<xsl:copy>
<xsl:attribute name="playOrder">
<xsl:value-of select="count(preceding-sibling::navPoint) + 1" />
</xsl:attribute>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT modify attribute value at EXACT element by passing the original value to a template

I'm struggling to get this abomination called XSLT to work. I need to get an EXACT attribute at EXACT path, pass its original value to a template and rewrite this value with the result from the template.
I'm having a file like this:
<?xml version="1.0" encoding="windows-1251"?>
<File>
<Document ReportYear="17">
...
...
</Document>
</File>
So I made an XSLT like this:
<?xml version="1.0" encoding="windows-1251"?>
<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" encoding="windows-1251" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template name="formatYear">
<xsl:param name="year" />
<xsl:value-of select="$year + 2000" />
</xsl:template>
<xsl:template match="File/Document">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear">
<xsl:with-param name="year" select="#ReportYear" />
</xsl:call-template>
</xsl:attribute>
</xsl:copy>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
This works fine except it closes the <Document> tag immediately and places its content immediately after itself.
Also, can I address the ReportYear attribute value without repeating it twice? I tried current() but it didn't work.
If you're closing <xsl:copy> before applying templates to the remainder of the content of <Document>, then of course <Document> will be closed before the remainder of the content of <Document> appears in the output.
<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" encoding="windows-1251" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Document">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:attribute name="ReportYear">
<xsl:value-of select="#ReportYear + 2000" />
</xsl:attribute>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
outputs
<?xml version="1.0" encoding="windows-1251"?>
<File>
<Document ReportYear="2017">
...
...
</Document>
</File>
I don't think an extra template just for adding 2000 to #ReportYear is necessary. But if you must, you can streamline the whole thing like so
<xsl:template name="formatYear">
<xsl:param name="year" select="#ReportYear" /> <!-- you can define a default value -->
<xsl:value-of select="$year + 2000" />
</xsl:template>
and
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear" /> <!-- ...and can use it implicitly here -->
</xsl:attribute>
If you need to process the contents of the Document element with apply-templates and want to keep the result of the applied templates as the children then you need to move the apply-templates inside of the copy:
<xsl:template match="File/Document">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="ReportYear">
<xsl:call-template name="formatYear">
<xsl:with-param name="year" select="#ReportYear"/>
</xsl:call-template>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
Not sure why you haven't simply used
<xsl:template match="File/Document/#ReportYear">
<xsl:attribute name="{name()}">
<xsl:value-of select=". + 2000"/>
</xsl:attribute>
</xsl:template>
together with the identity transformation template.

how to make XHTML?

i have code *xsl :
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xhtml" indent="yes" />
<xsl:variable name="with" select="'File2.xml'" />
<xsl:template match="/">
<html>
<body>
<h1>WEB Service</h1><br/>
</body>
</html>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
<xsl:variable name="info" select="document($with)/Xml/data[ClassId=current()/ClassId]/." />
<xsl:for-each select="$info/*">
<xsl:if test="name()!='ClassId'">
<xsl:copy-of select="." />
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:transform>
im trying make XHTML, when i running just showing html not include xml fragment..
anyone can help me please ?
thanks
when i running just showing html not include xml fragment..
The reason for this is that the XSLT processor starts by applying the template matching the / root node. This template contains no xsl:apply-templates instructions, so the processing ends here.

Remove namespaces for all except for one node

Given the following source xml:
<?xml version="1.0" encoding="UTF-8"?>
<Test xmlns="http://someorg.org">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>Some text</p>
<p>Some text</p>
</div>
</text>
</Test>
I would like to have the same output as the above source xml (the source xml contains many other xml nodes but for this section, I want to output it as it is, with no changes.) I have the following xslt (see below) which strips the elements of their namespaces as desired. Unfortunately, it also strips the div elements of their name spaces but I want to retain them. The closest I got to achieving my aim is the following xslt but it outputs the div element twice because of the apply-templates but I only want the div element once with its namespace.
This is my xslt:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://someorg.org"
xmlns="http://someorg.org"
exclude-result-prefixes="f xsl">
<xsl:template match="*">
<xsl:element name="{local-name(.)}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match = "f:text/f:status">
<status value ="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<xsl:apply-templates/>
</div>
</xsl:template>
</xsl:stylesheet>
Instead of trying to remove namespaces for all elements (as handled by your <xsl:template match="*"> template, you can only target elements in the "http://someorg.org" namespace. Simply change the template match to this
<xsl:template match="f:*">
For the elements in the "http://www.w3.org/1999/xhtml" namespace, you could use the identity template to pick up everything else
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="http://someorg.org">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="f:*">
<xsl:element name="{local-name(.)}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
add a template
<xsl:template match="*[local-name()='div'
and namespace-uri() = 'http://www.w3.org/1999/xhtml']">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
to perform a copy instead of creating a namespace-stripped element.

How do I build an id and count at the same time?

In the following code, I need, in the place of the two xxxx's, to have the name of the element (name()), so h1, h2 or h3, whatever the match may be. So the second xxxx must be the count of the h1/h2/h3 in that file. The attribute will then look like "h1_4", or h3_15" etc.
How do I do that ?
<xsl:template match="h1[not(#id)] | h2[not(#id)] | h3[not(#id)]" >
<xsl:element name="{name()}" >
<xsl:attribute name="id">xxxx_<xsl:value-of><xsl:number count="xxxx" /></xsl:value-of></xsl:attribute>
</xsl:element>
<xsl:apply-templates/>
</xsl:template>
As I said, the request is ambiguous. The following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="h1[not(#id)] | h2[not(#id)] | h3[not(#id)]" >
<xsl:variable name="name" select="name()" />
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="$name"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="count(preceding::*[name()=$name]) + 1"/>
</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied to the following test input:
<root>
<h1 id="h1_1"/>
<h2 type="abc"/>
<h3 type="xyz"/>
<h1>content</h1>
<h3 id="h3_2" type="efg"/>
<h2/>
</root>
will produce:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<h1 id="h1_1"/>
<h2 id="h2_1" type="abc"/>
<h3 id="h3_1" type="xyz"/>
<h1 id="h1_2">content</h1>
<h3 id="h3_2" type="efg"/>
<h2 id="h2_2"/>
</root>
You're on the right track using xsl:number, how about:
<xsl:template match="h1 | h2 | h3" >
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="name()"/>
<xsl:text>_</xsl:text>
<xsl:number level="any" />
</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
I'm assuming an identity template to copy the rest of the document as-is, and that being the case I've simplified the match pattern - you don't need to check for not(#id) as where there is an id attribute in the input it will overwrite the one being created by the xsl:attribute.

xsl match template by attribute except one

I've a global match on an attribut in my stylesheet but I want to exclude the f - element. How can I do that?
Example XML:
<a>
<b formatter="std">...</b>
<c formatter="abc">...</c>
<d formatter="xxx">
<e formatter="uuu">...</e>
<f formatter="iii">
<g formatter="ooo">...</g>
<h formatter="uuu">...</h>
</f>
</d>
</a>
Current solution:
<xsl:template match="//*[#formatter]">
...
</xsl:template>
I've tried something like this, but that didn't worked.
<xsl:template match="f//*[#formatter]">
...
</xsl:template>
<xsl:template match="//f*[#formatter]">
...
</xsl:template>
Either //f[#formatter] or f[#formatter] would have worked (the // is not necessary). When this XSLT is run on your example input:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[#formatter]">
<xsl:element name="transformed-{local-name()}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="f[#formatter]">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
The result is:
<a>
<transformed-b formatter="std">...</transformed-b>
<transformed-c formatter="abc">...</transformed-c>
<transformed-d formatter="xxx">
<transformed-e formatter="uuu">...</transformed-e>
<transformed-g formatter="ooo">...</transformed-g>
<transformed-h formatter="uuu">...</transformed-h>
</transformed-d>
</a>
As you can see, the f is excluded. Does this answer your issue, or have I misunderstood what you want to do?