使用XSLT替换节点文本?

3
以下是我的XML文件,用于存储数据 -
<Locations>
   <location>
      <place>Newyork</place>
      <dt>01-Dec-2011</dt>
   </location>
   <location>
      <place>Berlin</place>
      <dt>02-Dec-2011</dt>
   </location>
   <location>
      <place>Tokyo</place>
      <dt>04-Dec-2011</dt>
   </location>
</Location>

我想要实现的目标是-

如果参观时间被重新安排,我希望替换<dt>标签日期值。例如- 如果柏林的参观日期发生变化,存储在<dt>标签中,那么如何使用XSLT在XML文件中编辑/替换相同的内容?谢谢 - 约翰


好问题,+1。覆盖身份规则并将更新作为外部参数传递到转换中 - 这是解决此问题的简短而最有效的方法。 - Dimitre Novatchev
@Dimitre- 说得对,非常感谢 :) - John
3个回答

5

这个转换展示了如何使用全局参数(在此处用内联元素建模)来指定(可能是多个)更新:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:updates>
  <update place="Berlin" dt="11-Dec-2011"/>
 </my:updates>

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

 <xsl:template match=
  "location
     [place = document('')/*/my:updates/update/@place]
       /dt/text()
  ">
  <xsl:value-of select=
    "document('')/*/my:updates/update
                      [@place = current()/../../place]
                        /@dt
    "/>
 </xsl:template>
</xsl:stylesheet>

当应用于提供的XML文档(已更正为使其格式正确)时:

<Locations>
   <location>
      <place>Newyork</place>
      <dt>01-Dec-2011</dt>
   </location>
   <location>
      <place>Berlin</place>
      <dt>02-Dec-2011</dt>
   </location>
   <location>
      <place>Tokyo</place>
      <dt>04-Dec-2011</dt>
   </location>
</Locations>

所需的正确结果已生成:

<Locations>
   <location>
      <place>Newyork</place>
      <dt>01-Dec-2011</dt>
   </location>
   <location>
      <place>Berlin</place>
      <dt>11-Dec-2011</dt>
   </location>
   <location>
      <place>Tokyo</place>
      <dt>04-Dec-2011</dt>
   </location>
</Locations>

解释:

  1. 身份规则会原封不动地复制每个节点。

  2. 只有一个覆盖模板 -- 匹配任何具有相应my:updates/update元素的dt的文本节点子代。在此模板中,我们输出此相应的my:updates/update元素的dt属性值。

请注意: 在真实的转换中,内联的my:updates元素最好由外部全局参数替换。阅读您的XSLT处理器文档,了解如何将外部参数传递给转换--这取决于具体实现。

更新: 鉴于原帖作者发现难以将此解决方案转换为使用全局、外部传递的xsl:param,这里提供转换后的解决方案:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pUpdates">
  <update place="Berlin" dt="11-Dec-2011"/>
 </xsl:param>

 <xsl:variable name="vUpdates" select=
     "ext:node-set($pUpdates)/*"/>

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

 <xsl:template match="dt/text()">
  <xsl:choose>
      <xsl:when test="../../place=$vUpdates/@place">
       <xsl:value-of select=
           "$vUpdates[@place = current()/../../place]/@dt"/>
      </xsl:when>
      <xsl:otherwise>
       <xsl:value-of select="."/>
      </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

应用此转换到相同的XML文档(如上),将会产生相同的正确且期望的结果。
<Locations>
   <location>
      <place>Newyork</place>
      <dt>01-Dec-2011</dt>
   </location>
   <location>
      <place>Berlin</place>
      <dt>11-Dec-2011</dt>
   </location>
   <location>
      <place>Tokyo</place>
      <dt>04-Dec-2011</dt>
   </location>
</Locations>

请注意:在此解决方案中,xsl:param仍然具有硬编码的值,这是我们使用ext:node-set()扩展函数的唯一原因。如果参数确实是外部传递的,则不需要将RTF转换为常规树,应直接引用参数。

此外,在XSLT 1.0中,我们必须进行更不精确的匹配,并在模板正文中使用比较(xsl:choose)。这是因为在XSLT 1.0中不允许在匹配模式中引用变量/参数。

XSLT 2.0已经消除了这种限制,因此我们可以使用更简单的转换:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pUpdates">
  <update place="Berlin" dt="11-Dec-2011"/>
 </xsl:param>

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

 <xsl:template match=
   "location[place=$pUpdates/*/@place]/dt/text()">
       <xsl:value-of select=
           "$pUpdates/*[@place = current()/../../place]/@dt"/>
 </xsl:template>
</xsl:stylesheet>

非常感谢您提供如此清晰简洁的解释。向您致敬 :) - John
因此,在传递参数时,我将不得不使用以下标签 - <xsl:param name="paraPlace" select=""/><xsl:param name="paraDate" select=""/>,并且将在<my:update...>标签中使用这些参数。对吗..? - John
我在尝试更新同一XML文件时遇到以下错误 - --------------------配置:'<Default>-------------------- 错误:'文件过早结束。' 错误:'com.sun.org.apache.xml.internal.utils.WrappedRuntimeException:文件过早结束。' 但是,当转换后的XML文件不同时,则没有错误!! - John
@John:我已经更新了答案,并提供了与全局xsl:param一起使用的相应转换。如果您可以使用XSLT 2.0,我还提供了一个更简单和更短的XSLT 2.0解决方案。希望这回答了你的新问题。 - Dimitre Novatchev

2
身份模板将复制文档:
<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

然后你可以只为想要更改的部分创建其他模板。

例如(未经测试):

<xsl:template match="//location/dt[preceding-sibling::place='Berlin']">
    <dt>Your date</dt>
</xsl:template>

1
实际问题是,你将如何检查访问是否已重新安排?在我看来,你有三个选择:
  • 将重新安排的日期与位置存储在辅助 XML 中,并使用 XPath document 函数从中读取;
  • 通过编程方式生成已经进行了更正的 XSLT 样式表;或者
  • 使用 XSLT 扩展函数或扩展元素(函数可能已经足够)以其他语言执行检查。例如 Java。

编辑 - 或者遵循 Krab 的建议:使用 XSLT 参数以一种方式传递数据到静态样式表中。


1
嘿,样式表参数怎么样 - 只需传递它的位置和新日期。 - Krab
@Krab 当然... 我真的应该记得你可以传递参数。谁知道我做了些什么疯狂的样式表生成本可以避免的事情。我会进行编辑。 - G_H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接