在使用xslt进行转换期间丢失xml标记



在使用.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>

这就是为什么您在输出中看到的是文本值,而不是它们周围的标记。

相关内容

  • 没有找到相关文章

最新更新