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

时间:2017-11-01 10:27:06

标签: xml xslt xslt-1.0 transformation apply-templates

在使用.xsl样式表帮助转换xml文件期间重命名子节点时遇到问题。问题是,只处理值而不是标记。我想要两者兼得。

转型前的原创:

powershell -File ps1file.ps1 -arg1 "val1" -arg2 "val2"

预期结果:

<old>
 <ns2:Header>
  <EntityId>yxc</EntityId>
  <Application>11</Application>
  <Version>354</Version>
  <User>
    <Id>user1</Id>
  </User>
</ns2:Header>
 ....
 </old>

到目前为止我得到的是像这样的.xsl:

<new>
  <Header>
  <EntityId>yxc</EntityId>
  <Application>11</Application>
  <Version>354</Version>
  <User>
    <Id>user1</Id>
  </User>
 </Header>
 ...
</new>

转换后,&#34; Header&#34;的子节点迷路了,只剩下价值观了:

<?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>

我想我错过了&#34; apply-templates&#34;功能。 有什么想法吗?

谢谢!

3 个答案:

答案 0 :(得分:0)

您可能没有丢失节点。如果你在浏览器中使用w3c xslt测试器显示转换的xml,那么很可能它只是&#34; UI&#34;表示。这是我的代码有效。运行下面的脚本并单击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>
  

下面的工作示例:

&#13;
&#13;
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;
}
&#13;
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>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

第1部分 - 通常不正确的

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

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

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

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

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

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

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

请注意,上述select的默认apply-templates内容为 node(),表示此默认规则仅复制内容 源元素,省略开始和结束标记。

通常不是我们想要的。 如果我们想“复制”源代码的整个内容,那么脚本 应该包含身份模板

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

要将元素名称从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以从输出中删除其他空行。