0

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

我想我错过了“应用模板”功能的一些表达。有任何想法吗?

谢谢!

4

3 回答 3

1

第 1 部分 - 通常不正确的地方

如果您在 XML 文件中使用任何命名空间,则应该定义它,例如在主标记中。所以主要标签应该是:

<old xmlns:ns2="http://example.com">

要纠正的另一点是模板不能嵌套在另一个中。

第 2 部分 - 为什么您的脚本失败

在查看 XSLT 脚本时,仅阅读其内容是不够的。同样重要的是尚未编写但以某种方式包含在 XSLT 处理器中的内容,即默认模板规则

其中之一是元素的默认规则:

<xsl:template match="* | /">
  <xsl:apply-templates/>
</xsl:template>

请记住,select上面的默认内容apply-templatesnode(),这意味着此默认规则复制 源元素的内容,省略了开始和结束标记。

很多时候这不是我们想要的。如果我们想“复制”源的全部内容,脚本应该包含一个身份模板

<xsl:template match="node()|@*">
  <xsl:copy>
    <xsl:apply-templates select="node() | @*"/>
  </xsl:copy>
</xsl:template>

由于您的脚本(可能)不包含此类标识模板,因此 XSLT 处理器应用其默认规则,仅复制内容

第 3 部分 - 如何完成此任务

如我所见,您想做 2 处更改:

  • 删除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(),它实际上删除了任何源命名空间。

要将元素名称从 更改oldnew,我们需要以下模板:

<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以从输出中删除额外的空行。

于 2017-11-01T13:51:50.827 回答
0

问题是仅仅应用一个没有定义的模板并不会复制您选择的节点。在这种情况下会执行一些内置模板,其中之一是:

<xsl:template match="text()|@*">
   <xsl:value-of select="."/>
</xsl:template>

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

于 2017-11-01T11:59:49.907 回答
0

您可能不会丢失节点。如果您在浏览器中使用 w3c xslt 测试器显示转换后的 xml,那么它可能只是“UI”表示。这是我的有效代码。运行下面的脚本并单击转换。

这是我的 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>

于 2017-11-01T11:26:00.930 回答