如何使用xsl根据属性将xml的兄弟节点更改为父节点和子节点

Posted

技术标签:

【中文标题】如何使用xsl根据属性将xml的兄弟节点更改为父节点和子节点【英文标题】:How to change the siblings of xml into parent and child nodes according to their attributes using xsl 【发布时间】:2021-12-08 12:59:17 【问题描述】:

输入是

<p style="abc">hbfg</p>
<r style="cds">bhf</r>
<r style="cds"> bhfsh</r>
<p style="abc">pofj</p>
<r style="abc"> bchs</r>

预期的输出应该是

<p style="abc">hbfg
    <r style="cds">bhf</r>
    <r style="cds"> bhfsh</r></p>
<p style="abc">pofj
    <r style="abc"> bchs</r></p>

如何使用 xslt 进行转换。

【问题讨论】:

请编辑问题以将其限制为具有足够详细信息的特定问题,以确定适当的答案。 如果您format your code 并使用*** 的syntax highlighting,您的问题会更容易阅读/理解。 嗨@Douglas.P 我改变了问题希望你理解我的问题并回答我的问题。在此先感谢:) 【参考方案1】:

这里有两个版本的 XSLT 样式表,它们将处理 XML 你发布的文件,一个为xslt-2.0 介绍了一个方便的 xsl:for-each-group group-starting-with=pattern 这个元素 用例,为了最大的可移植性,一个用于xslt-1.0 使用 XPath 进行分组。两个版本都使用doc/text 作为逻辑 树的根和xsl:apply-templates 充分利用 内置模板规则。注意空格处理。

平面文件转换的更多示例在 SO 和 XSLT 1.0 FAQ,现在在 archive.org.


<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
>
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="doc/text">
    <chapter>
      <title>
        <xsl:apply-templates select="p[@style='TRH2']"/>
      </title>
      <research>
        <title>
          <xsl:apply-templates select="p[@style='TRRef']"/>
        </title>
        <reftext>
          <xsl:apply-templates select="p[@style='TRRefText']"/>
        </reftext>
      </research>
      <sections>
        <xsl:for-each-group 
          select="p[not(@style) or @style='TRH7']"
          group-starting-with="p[@style='TRH7']"
        >
          <title>
            <xsl:apply-templates select="self::p[1]"/>
          </title>
          <paragraphs>
            <xsl:for-each select="current-group()[self::p][position()>1]">
              <para-text>
                <xsl:apply-templates/>
              </para-text>
            </xsl:for-each> 
          </paragraphs>
        </xsl:for-each-group>
      </sections>
    </chapter>
  </xsl:template>


  <xsl:template match="p[@style='TRRefText']">
     <xsl:value-of select="."/><br/>
  </xsl:template>

  <xsl:template match="foot-note">
    <footnoteref>
      <id><xsl:value-of select="@id-rel"/></id>
      <xsl:apply-templates/>
    </footnoteref>
  </xsl:template>

</xsl:transform>

XSLT 1.0 版本(在第三个xsl:template)使用 XPath 将非标题 p 元素分组在当前和之间的表达式 下一个小节标题元素 (p[@style='TRH7']) 和 mode="para" 子句以避免将标题同时作为标题和段落处理。

<?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" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="doc/text">
    <chapter>
      <title>
        <xsl:apply-templates select="p[@style='TRH2']" />
      </title>
      <research>
        <title>
          <xsl:apply-templates select="p[@style='TRRef']" />
        </title>
        <reftext>
          <xsl:apply-templates select="p[@style='TRRefText'] "/>
        </reftext>
      </research>
      <sections>
        <xsl:apply-templates select="p[@style='TRH7']" />
      </sections>
    </chapter>
  </xsl:template>


  <xsl:template match="p[@style='TRRefText']">
     <xsl:value-of select="."/><br/>    
  </xsl:template>

  <xsl:template match="p[@style='TRH7']">
    <title><xsl:apply-templates/></title>
    <paragraphs>
      <xsl:apply-templates mode="para"
        select="following-sibling::p[not(@style='TRH7')]
               [generate-id(preceding-sibling::p[@style='TRH7'][1])
              = generate-id(current())]"
      />
    </paragraphs>
  </xsl:template>

  <xsl:template match="p" mode="para">
    <para-text><xsl:apply-templates/></para-text>
  </xsl:template>

  <xsl:template match="foot-note">
    <footnoteref>
      <id><xsl:value-of select="@id-rel"/></id>
      <xsl:apply-templates/>
    </footnoteref>
  </xsl:template>

</xsl:transform>

更新:评论中要求的其他解释。

您自己的代码与我发布的非常接近,因此我将详细介绍如何使用 XSLT 1.0 对元素进行分组。文档中的每个小节都由其标题的样式 (p[@style='TRH7']) 触发,激活第三个模板:

<xsl:template match="p[@style='TRH7']">
  <title><xsl:apply-templates/></title>
  <paragraphs>
    <xsl:apply-templates mode="para"
      select="following-sibling::p[not(@style='TRH7')]
             [generate-id(preceding-sibling::p[@style='TRH7'][1])
            = generate-id(current())]"
    />
  </paragraphs>
</xsl:template>

此模板发出一个小节标题(使用内置模板规则),然后收集以下非标题段落 (following-sibling::p[not(@style='TRH7')]) 有当前 title 作为最近的逻辑父级。回想一下preceding-sibling 是一个反向轴,所以p[…][1] 指的是反向文档顺序中最近的兄弟。由于following-sibling::p[…] 选择了所有后续的非标题段落,第二个谓词[generate-id(…)] 将选择限制为当前标题的逻辑子项。

【讨论】:

谢谢@urcodebetterznow 它的工作,你能清楚地解释第二个xsl转换吗? @NaveenKumar:我更新了我的回复。另请参阅我链接到的 XSLT 1.0 FAQ 页面中的第 1 项和第 4 项。 HI @urcodebetterznow 我们如何将没有属性的相似节点合并为单个节点。 @NaveenKumar:要选择所有没有属性的p 元素,您可以说&lt;xsl:apply-templates select="//p[not(@*)]"/&gt;。如果这不是您的想法,我建议您提出一个新问题,包括示例代码。 嗨@urcodebetterznow 我可以得到你的联系方式,以便我可以分享问题并想了解更多关于 xslt 的信息,比如你的邮件 ID 吗?

以上是关于如何使用xsl根据属性将xml的兄弟节点更改为父节点和子节点的主要内容,如果未能解决你的问题,请参考以下文章

jQuery获取父节点子节点兄弟节点

有没有办法使用 xsl 更改 xml 中元素的名称?

如何获取当前节点的父节点名?

如何使用 XSL 将 XML 属性值分配给下拉列表

尝试选择节点时如何在 xsl 中使用变量?

XSL:根据属性计数和匹配创建组