【问题标题】:Losing xml tags during transformation with xslt在使用 xslt 进行转换期间丢失 xml 标签
【发布时间】:2018-04-13 15:28:46
【问题描述】:

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

我想我缺少一些关于“应用模板”函数的表达式。 有什么想法吗?

谢谢!

【问题讨论】:

  • 您输入的 XML 当前无效。它使用 ns2: 命名空间前缀,但没有用它声明命名空间 URL。您是否在输入 XML 中缺少xmlns:ns2='???'?谢谢。

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


【解决方案1】:

第 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 部分 - 如何完成此任务

如我所见,您想做 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 以从输出中删除额外的空行。

【讨论】:

    【解决方案2】:

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

    这是我的 xsl,在下面的测试 sn-p 中使用:

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2017-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多