在使用.xsl样式表转换xml文件的过程中,重命名子节点时遇到问题。问题是,只处理值而不处理标记。我想两者都有。
改造前的原件:
<old>
<ns2:Header>
<EntityId>yxc</EntityId>
<Application>11</Application>
<Version>354</Version>
<User>
<Id>user1</Id>
</User>
</ns2:Header>
....
</old>
预期结果:
<new>
<Header>
<EntityId>yxc</EntityId>
<Application>11</Application>
<Version>354</Version>
<User>
<Id>user1</Id>
</User>
</Header>
...
</new>
到目前为止,我得到的是这样的.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
<new xmlns:ns2="http://example.com">
<xsl:template match="//ns2:Header">
<xsl:element name="Header">
<xsl:apply-templates select="//ns2:Header" />
</xsl:element>
</xsl:template>
.....
</new>
</xsl:template>
</xsl:stylesheet>
转换后,"Header"的子节点丢失,只有值仍然存在:
<new xmlns:ns2="http://example">
<Header>
yxc
11
354
user1
</Header>
...
</new>
我想我缺少了一些关于"应用模板"函数的表达式。有什么想法吗?
谢谢!
第1部分-通常不正确的地方
如果在XML文件中使用任何名称空间,则应定义,例如在主标签中。所以主标签应该是:
<old xmlns:ns2="http://example.com">
需要纠正的另一点是,模板不能嵌套在另一个模板中。
第2部分-脚本失败的原因
在查看XSLT脚本时,仅仅读取其内容是不够的。同样重要的是中没有的内容,但以某种方式包含了哪些内容在XSLT处理器中,即默认模板规则。
其中之一是元素的默认规则:
<xsl:template match="* | /">
<xsl:apply-templates/>
</xsl:template>
请记住,上述apply-templates
的默认select
内容为node()
,这意味着此默认规则只复制源元素的内容,同时省略开始和结束标记。
很多时候这不是我们想要的。如果我们想"复制"源的整个内容,脚本应包含身份模板:
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
由于您的脚本(可能)不包含这样的标识模板,XSLT处理器应用其默认规则,只复制内容。
第3部分-如何完成此任务
正如我所看到的,你想做两个改变:
- 删除
Header
标记中包含的命名空间 - 将
old
标记名称更改为new
要删除任何命名空间,我们必须替换标识模板具有以下2个模板,处理元素和属性:
<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>
注意local-name()
的用法,它实际上删除了任何源命名空间。
要将元素名称从old
更改为new
,我们需要以下内容模板:
<xsl:template match="old">
<new>
<xsl:apply-templates/>
</new>
</xsl:template>
所以整个脚本可以写如下:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Change element name -->
<xsl:template match="old">
<new>
<xsl:apply-templates/>
</new>
</xsl:template>
<!-- Copy elements w/o namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<!-- Copy attributes w/o namespace -->
<xsl:template match="@*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:transform>
我添加了strip-space
以从输出中删除额外的空行。
您可能没有丢失节点。如果您使用w3cxslt-tester在浏览器中显示转换后的xml,那么它可能只是"UI"表示。这是我的有效代码。运行下面的脚本,然后单击transform。
这是我的xsl,用于下面的测试片段:
<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:template match="/">
<new xmlns:ns2="http://example.com">
<Header>
<xsl:copy-of select="//*[local-name()='ns2:Header']/*" />
</Header>
</new>
</xsl:template>
</xsl:stylesheet>
下面的工作示例:
var parser = new DOMParser();
var xsltText = document.getElementById('xsl').value;
var xslt = parser.parseFromString(xsltText, "text/xml");
var xmlText = document.getElementById('xml').value;;
var xml = parser.parseFromString(xmlText, "text/xml");
document.getElementById('transform').onclick = function() {
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslt);
var processed = xsltProcessor.transformToDocument(xml);
var result = new XMLSerializer().serializeToString(processed);
console.log(result);
document.getElementById('result').value = result;
}
XML to be transformed:<br/> <textarea id="xml" cols="70" rows="5">
<old>
<ns2:Header>
<EntityId>yxc</EntityId>
<Application>11</Application>
<Version>354</Version>
<User>
<Id>user1</Id>
</User>
</ns2:Header>
</old>
</textarea><br/>XSL:<br/>
<textarea id="xsl" cols="70" rows="5">
<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:template match="/">
<new xmlns:ns2="http://example.com">
<Header>
<xsl:copy-of select="//*[local-name()='ns2:Header']/*" />
</Header>
</new>
</xsl:template>
</xsl:stylesheet>
</textarea>
<input type="button" id="transform" value="Transform!" />
<br/>Tranformed xml:<br/>
<textarea id="result" cols="70" rows="5" readOnly="true"></textarea>
问题是,在没有定义模板的情况下仅应用模板不会复制所选节点。有一些内置的模板在这种情况下执行,其中之一就是这个:
<xsl:template match="text()|@*">
<xsl:value-of select="."/>
</xsl:template>
这就是为什么您在输出中看到的是文本值,而不是它们周围的标记。