如何用XSLT为toc.ncx添加playOrder?批量重排navpoint序号问询
用XSLT自动为toc.ncx生成/重排playOrder的解决方案
刚好我之前处理过类似的NCX文件问题,用XSLT就能完美解决自动生成和重排playOrder的需求,不用再手动修改编号。下面是具体的实现方法:
核心XSLT代码
创建一个名为update-playorder.xsl的文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ncx="http://www.daisy.org/z3986/2005/ncx/" exclude-result-prefixes="ncx"> <!-- 身份模板:复制所有原有节点和属性,保留原文件结构 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 专门处理navpoint元素,自动生成连续的playOrder --> <xsl:template match="ncx:navpoint"> <xsl:copy> <!-- 生成playOrder:按深度优先顺序为所有navpoint连续编号 --> <xsl:attribute name="playOrder"> <xsl:number count="ncx:navpoint" level="any"/> </xsl:attribute> <!-- 保留原navpoint的其他属性(排除旧的playOrder避免冲突) --> <xsl:apply-templates select="@*[not(local-name()='playOrder')]"/> <!-- 处理子节点,确保嵌套的navpoint也能被编号 --> <xsl:apply-templates select="node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
代码说明
- 命名空间处理:因为你的toc.ncx使用了DAISY NCX的默认命名空间,所以XSLT里用
ncx前缀来匹配对应的元素,避免命名空间冲突。 - 身份模板:保证原文件里的所有内容(比如
<head>里的meta信息、<navlabel>、<content>等)都原封不动保留,只修改navpoint的playOrder属性。 - 自动编号逻辑:
<xsl:number count="ncx:navpoint" level="any"/>会遍历所有navpoint元素(包括嵌套的子节点),按照深度优先的顺序生成连续的整数,完全符合NCX规范里的线性阅读顺序要求。
使用方法
你可以用任意XSLT处理器来运行这个转换,比如:
- 命令行工具(Saxon):
下载Saxon的JAR包后,执行命令:
其中java -jar saxon9he.jar -s:toc.ncx -xsl:update-playorder.xsl -o:updated-toc.ncxtoc.ncx是你的原文件,updated-toc.ncx是生成的带正确playOrder的新文件。 - XML编辑器:很多XML编辑器(比如Oxygen XML、XMLSpy)都自带XSLT转换功能,直接导入原文件和XSLT文件,执行转换即可。
解决新增navpoint后的重排问题
以后不管你在navmap的任何位置新增、删除或移动navpoint元素,只要重新运行一次这个XSLT转换,所有playOrder都会自动重新生成连续的编号,完全不用手动逐个修改,彻底解决大量元素时的繁琐操作。
举个简单例子,原文件里的嵌套navpoint会被自动编号成这样:
<navmap> <navpoint playOrder="1" id="navpoint-1"> <navlabel><text>第一章</text></navlabel> <content src="chapter1.html"/> <navpoint playOrder="2" id="navpoint-1-1"> <navlabel><text>1.1 小节</text></navlabel> <content src="chapter1-1.html"/> </navpoint> </navpoint> <navpoint playOrder="3" id="navpoint-2"> <navlabel><text>第二章</text></navlabel> <content src="chapter2.html"/> </navpoint> </navmap>
内容的提问来源于stack exchange,提问作者user9702614
相关产品推荐
相关产品推荐

