我编写了一个用于将XML转换为ePub的包。一切正常,除了某些情况,其中空白命名空间(xmlns=""
)节点被写入结果文档。在转换之前,我准备了用于保存主段的临时变量(即meta
,body
等),最后将节点(使用xsl:copy-of[@copy-namespaces='no']
指令)复制到结果文档。我还在@exclude-result-prefixes='ns_list_sep_by_space'
元素中使用了xsl:transform
,但仍然无法获得所需的结果。
oXygen IDE在弹出窗口中显示一条消息:
使用xsl时:copy-of new元素也将从原始元素节点复制命名空间节点,除非通过指定copy-namespaces =“no”排除它们。如果省略此属性或取值为yes,则原始元素的所有命名空间节点都将复制到新元素。如果取值为no,则不会复制任何命名空间节点:但是,仍然会根据命名空间修正过程的要求在结果树中创建命名空间节点。
以下是我的问题的更多细节:
主要样式表:
main.xsl:main caller
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:cylian="local-ns-for-extension-functions"
exclude-result-prefixes="xs xd cylian"
version="2.0">
<xsl:import href="modules/core.xsl"/>
<xsl:variable name="base" select="base-uri()" as="xs:anyURI"/>
<xsl:template match="/">
<xsl:call-template name="procA"/>
</xsl:template>
</xsl:transform>
主要样式表:
core.xsl: core processing unit
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:cylian="local-ns-for-extension-functions"
exclude-result-prefixes="xs xd cylian"
version="2.0">
<xsl:import href="sub1.xsl"/>
<xsl:import href="sub2.xsl"/>
<!--and more-->
<!-- variable to hold intermediate results for stage1 -->
<xsl:variable name="stage1">
<cylianz>
<xsl:copy-of select="$a" copy-namespaces="no"/>
<xsl:copy-of select="$b" copy-namespaces="no"/>
<!--and more-->
</cylianz>
</xsl:variable>
<!-- variable to hold intermediate results for stage2 -->
<xsl:variable name="stage2">
<cylianz>
<xsl:for-each select="$stage1//cylian">
<xsl:sort select="@pos"/>
<xsl:sequence select="."/>
</xsl:for-each>
</cylianz>
</xsl:variable>
<xsl:template name="procA">
<xsl:for-each select="$stage2//cylian">
<xsl:result-document href="{concat($outdir,@href)}" format="general">
<xsl:call-template name="procB">
<xsl:with-param name="context" select="."/>
<xsl:with-param name="title">
<xsl:value-of select="$book_title"/>
</xsl:with-param>
</xsl:call-template>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
<xsl:template name="procB">
<xsl:param name="context"/>
<xsl:param name="title"/>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<xsl:call-template name="header">
<xsl:with-param name="title" select="$title"/>
</xsl:call-template>
</head>
<body>
<div id="root">
<xsl:apply-templates select="."/>
</div>
</body>
</html>
</xsl:template>
<!--
1/ other rules are shortened for clarity
2/ declaration «xmlns:cylian='local-ns-for-extension-functions'» has to retain, some parts of transformation uses some extension functions from that namespace
-->
</xsl:transform>
这是输出:
a.html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta xmlns="" http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title xmlns="">BookTitle</title>
<!--
2012.04.16 - 18:27:36 [XSLT processor: SAXON 9.1.0.5 from Saxonica]
-->
<link xmlns="" href="isbn.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<div id="root">
<div xmlns="" id="a1">
<!--...-->
</div>
</div>
</body>
</html>
我希望能更容易理解问题是什么。欢迎所有建议。提前谢谢。
答案 0 :(得分:4)
我们需要确保您的代码确定,但我怀疑您有这样的代码。
<xsl:template match="/">
<foo xmlns="http://example.com/ns">
<xsl:apply-templates/>
</foo>
</xsl:template>
<xsl:template match="whatever">
<bar/>
</xsl:template>
然后你得到
<foo xmlns="http://example.com/ns">
<bar xmlns=""/>
</foo>
,而你想要
<foo xmlns="http://example.com/ns">
<bar/>
</foo>
要解决此问题,请确保使用例如xsl:stylesheet元素移动默认命名空间声明。
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://example.com/ns">
version="1.0">
<xsl:template match="/">
<foo>
<xsl:apply-templates/>
</foo>
</xsl:template>
<xsl:template match="whatever">
<bar/>
</xsl:template>
</xsl:stylesheet>
这种方式适用于在不同模板中创建的所有结果元素。
[编辑]
根据您现在提供的示例,我认为我的建议是正确的,只需要几个文件就可以确保所有样式表模块xmlns="http://www.w3.org/1999/xhtml"
分别放在xsl:stylesheet
xsl:transform
个元素上以便所有结果元素最终都在XHTML命名空间中。
[第二次编辑] 我想你想要
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:cylian="local-ns-for-extension-functions"
exclude-result-prefixes="xs xd cylian"
version="2.0">
<xsl:import href="sub1.xsl"/>
<xsl:import href="sub2.xsl"/>
<!--and more-->
<!-- variable to hold intermediate results for stage1 -->
<xsl:variable name="stage1" xmlns="">
<cylianz>
<xsl:copy-of select="$a" copy-namespaces="no"/>
<xsl:copy-of select="$b" copy-namespaces="no"/>
<!--and more-->
</cylianz>
</xsl:variable>
<!-- variable to hold intermediate results for stage2 -->
<xsl:variable name="stage2" xmlns="">
<cylianz>
<xsl:for-each select="$stage1//cylian">
<xsl:sort select="@pos"/>
<xsl:sequence select="."/>
</xsl:for-each>
</cylianz>
</xsl:variable>
<xsl:template name="procA">
<xsl:for-each select="$stage2//cylian">
<xsl:result-document href="{concat($outdir,@href)}" format="general">
<xsl:call-template name="procB">
<xsl:with-param name="context" select="."/>
<xsl:with-param name="title">
<xsl:value-of select="$book_title"/>
</xsl:with-param>
</xsl:call-template>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
<xsl:template name="procB">
<xsl:param name="context"/>
<xsl:param name="title"/>
<html >
<head>
<xsl:call-template name="header">
<xsl:with-param name="title" select="$title"/>
</xsl:call-template>
</head>
<body>
<div id="root">
<xsl:apply-templates select="."/>
</div>
</body>
</html>
</xsl:template>
</xsl:transform>
然后,如果您有任何其他模块应该生成XHTML元素,请确保将xmlns="http://www.w3.org/1999/xhtml"
放在模块的根元素上,或者如果您需要在其他命名空间中创建元素,那么应该在任何模板上创建输出XHTML。
答案 1 :(得分:3)
输出中可能出现两种“不需要的”命名空间声明:由于它们是冗余噪声(它们声明未使用的命名空间前缀)而不需要的声明,以及因为它们放置而不需要的声明与预期命名空间不同的命名空间中的元素。
在第一种情况下,XSLT提供诸如exclude-result-prefixes和copy-namespaces ='no'之类的功能来消除噪音。
在第二种情况下(我认为你是这样),名称空间声明是样式表作者首先在错误的命名空间中创建元素这一事实的症状,解决方案是查看创建元素并修复它的代码。例如,当您打算<foo>
在其他名称空间中创建元素时,您可能编写了一个文字结果元素<foo xmlns="something"/>
来创建无名称空间中的元素。
答案 2 :(得分:3)
我们有这个XML文档:
<x:t xmlns:x="some:x">
<a/>
<b/>
</x:t>
以下是可能类似于您的代码:
<xsl:stylesheet version="2.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="/*">
<t xmlns="some:x">
<xsl:copy-of select="*" copy-namespaces="no"/>
</t>
</xsl:template>
</xsl:stylesheet>
会产生不需要的结果:
<t xmlns="some:x">
<a xmlns=""/>
<b xmlns=""/>
</t>
为什么会产生这种结果?
因为您正在使用<xsl:copy-of>
节点“按原样”复制,所以元素不会更改它们所在的命名空间。属性copy-namespaces="no"
仅指定属于此的命名空间节点将从复制中跳过元素 - 而不是该元素将更改其自己的命名空间。
<强>解决方案强>:
当我们想要更改一个元素所在的命名空间(在这种情况下从“no namespace”到“some:x”)时,我们不应该复制这个元素节点。
相反,我们必须从这个元素创建一个新元素,并指定新元素应该在的新命名空间:
<xsl:stylesheet version="2.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="/*">
<t xmlns="some:x">
<xsl:apply-templates select="*"/>
</t>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}" namespace="some:x">
<xsl:apply-templates select="node() | @*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
当在同一个XML文档(上面)上应用此转换时,会生成所需的正确结果:
<t xmlns="some:x">
<a/>
<b/>
</t>