【问题标题】:How to add <p> tags around text nodes using XSLT如何使用 XSLT 在文本节点周围添加 <p> 标记
【发布时间】:2014-02-06 11:01:44
【问题描述】:

我有以下 XML 文档

<body>
  <h2>title</h2>
  some text and a <a href="link">link</a> here.
</body>

我想使用 XSLT 将其转换为:

<body>
  <h2>title</h2>
  <p>some text and a <a href="link">link</a> here.</p>
</body>

因此我尝试了以下 XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" method="xml" cdata-section-elements="script"/>
  <xsl:template match="/ | node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="body/text()" >
    <p><xsl:copy/></p>
  </xsl:template>
</xsl:stylesheet>

但这似乎并没有给出预期的结果(如果文本节点不包含锚元素,它可以正常工作)。那么关于如何使用 XSLT 完成此任务的任何想法? (稍后我确实可以选择使用 C# 解析 XML,但我最初的想法是使用 XSLT)

更新

为了使整体要求更加清晰,输入 XML(或实际上是 XHTML)不是固定的,它可以是任何东西,因为它是用户输入。真的,我唯一能期待的是,它将是有效的 XML (XHTML),并且某些行可能不会包含在 &lt;p&gt; 标记中。

【问题讨论】:

  • 最终,我认为您无法避免指定需要自行处理的元素列表(body 的子代)(例如,您的示例中的 &lt;h2&gt;) ,或需要与周围的文本节点一起包装在&lt;p&gt; 元素中的元素列表(body 的子元素)(例如,您的示例中的&lt;a&gt;)。
  • @michael.hor257k 是的,我已经意识到这不是一个简单的问题,因此我在这里问它的原因;)。顺便说一句,我们非常感谢您的所有帮助和建议。

标签: xml xslt


【解决方案1】:

怎么样:

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

<xsl:template match="body">
    <xsl:copy>
        <xsl:apply-templates select=" @*|*[not(self::a)]"/>
        <p><xsl:copy-of select="text()|a"/></p>
    </xsl:copy>
</xsl:template>

【讨论】:

  • 我可以看到在我给定的输入上工作,但恐怕它不够通用,无法满足所有要求(我的问题确实不够清楚)。
  • @BartKoopman 我们只能使用您给我们的东西。无论如何,我认为您不能使这个真正通用-请参阅我在您的问题中添加的评论。但是,您可以通过向异常添加更多元素并从规则中减去它们来扩展第二个模板。
  • 很公平,您的回答确实表明我的问题不够清楚,而且我已经预料到它不会得到一个简单直接的答案(除非我自己错过了一些明显的东西)。跨度>
【解决方案2】:

这比“在文本节点周围添加 p 标签”稍微复杂一些,因为在您的示例中,您实际上是在尝试在一组三个节点周围添加一个 p 标签 - 两个文本节点和一个中间元素节点。对于您的具体示例,以下内容将起作用

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <body>
      <xsl:copy-of select="body/h2" />
      <p>
        <xsl:copy-of select="body/h2/following-sibling::node()" />         
      </p>
    </body>
  </xsl:template>
</xsl:stylesheet>

但这显然不是很通用。更一般地说,如果您想将一个 h2 和下一个之间的所有内容包装在一个 p 中,那么您可以使用“Muenchian 分组”方法的变体 - 使用 key 将每个非 h2 节点与其最近的前兄弟h2

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="groupByHeader" match="node()[not(self::h2)]"
           use="generate-id(preceding-sibling::h2[1])" />

  <xsl:template match="body">
    <xsl:copy>
      <!-- everything before the first h2 -->
      <xsl:copy-of select="key('groupByHeader', '')" />
      <xsl:apply-templates select="h2" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="h2">
    <!-- this h2 -->
    <xsl:copy-of select="."/>
    <!-- everything between this h2 and the next one (or the end of body) -->
    <p><xsl:copy-of select="key('groupByHeader', generate-id())" /></p>
  </xsl:template>
</xsl:stylesheet>

在您的示例输入中,这两个样式表应该都产生相同的输出:

<body><h2>title</h2><p>
  some text and a <a href="link">link</a> here.
</p></body>

如果您需要缩进来精确匹配您的“预期输出”,那么它会变得更加复杂,因为您基本上需要将第一个文本节点一分为二,将前导空格放在开头 p 标记之前,其余部分其后的文本节点,与最后一个文本节点类似。您不能在每个文本节点上简单地normalize-space(),因为这会去除您确实需要保留的空间 - 您不想以

结尾
<p>some text and a<a href="link">link</a>here.</p>

【讨论】:

  • 缩进并不重要,它是 XML(或者实际上是 XHTML),但棘手的部分确实是预期的输入,因为 h2 也可以是一堆不同的标签。更糟糕的是,我们的行周围已经有p 标签。不过我觉得你的两个模板可以让我走得更远,谢谢。
【解决方案3】:

试试这个:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" method="xml" cdata-section-elements="script"/>
  <xsl:template match="/ | node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="body" >
    <p><xsl:copy-of select="."/></p>
  </xsl:template>
</xsl:stylesheet>

【讨论】:

  • 这会将所有内容包装在一个 &lt;p&gt; 标记中并使输出无效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-28
  • 2019-11-27
  • 2016-12-02
  • 2017-04-02
  • 1970-01-01
相关资源
最近更新 更多