【问题标题】:How can I wrap a group of adjacent elements using XSLT?如何使用 XSLT 包装一组相邻元素?
【发布时间】:2010-10-18 18:25:27
【问题描述】:

我有一些带有<ListItem> 元素的XML,我想用<List> 元素包装任何连续运行。因此,源 XML 看起来像这样:

<Section>
  <Head>Heading</Head>
  <Para>Blah</Para>
  <ListItem>item 1</ListItem>
  <ListItem>item 2</ListItem>
  <ListItem>item 3</ListItem>
  <ListItem>item 4</ListItem>
  <Para>Something else</Para>
</Section>

我想把它转换成这样的:

<Section>
  <Head>Heading</Head>
  <Para>Blah</Para>
  <List>
    <ListItem>item 1</ListItem>
    <ListItem>item 2</ListItem>
    <ListItem>item 3</ListItem>
    <ListItem>item 4</ListItem>
  </List>
  <Para>Something else</Para>
</Section>

使用 XSLT。我确定这很明显,但我不能在晚上的这个时候解决它。谢谢!


编辑:大多数人可以放心地忽略这一点。

这个 XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root>
  <Story>
    <Section id="preface">
      <ChapterTitle>Redacted</ChapterTitle>
      <HeadA>Redacted</HeadA>
      <Body>Redacted</Body>
      <BulletListItem>Item1</BulletListItem>
      <BulletListItem>Item2</BulletListItem>
      <BulletListItem>Item3</BulletListItem>
      <BulletListItem>Item4</BulletListItem>
      <HeadA>Redacted</HeadA>
      <Body>Redacted</Body>
      <HeadA>Redacted</HeadA>
      <Body>Redacted</Body>
      <Body>Redacted<Italic>REDACTED</Italic>Redacted</Body>
      <BulletListItem>Second list Item1</BulletListItem>
      <BulletListItem>Second list Item2</BulletListItem>
      <BulletListItem>Second list Item3</BulletListItem>
      <BulletListItem>Second list Item4</BulletListItem>
      <Body>Redacted</Body>
    </Section>
  </Story>
</Root>

有了这个 XSL:

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

 <xsl:key name="kFollowing" match="BulletListItem[preceding-sibling::*[1][self::BulletListItem]]"
  use="generate-id(preceding-sibling::BulletListItem
         [not(preceding-sibling::*[1][self::BulletListItem])])"/>

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

 <xsl:template match="BulletListItem
         [not(preceding-sibling::*[1][self::BulletListItem])]">
  <BulletList>
    <xsl:call-template name="identity"/>
    <xsl:apply-templates mode="copy" select="key('kFollowing', generate-id())"/>
  </BulletList>
 </xsl:template>

 <xsl:template match="BulletListItem[preceding-sibling::*[1][self::BulletListItem]]"/>

 <xsl:template match="BulletListItem" mode="copy">
  <xsl:call-template name="identity"/>
 </xsl:template>
</xsl:stylesheet>

当使用 Ruby REXML 和 XML/XSLT 处理时,会生成此 XML(输出漂亮打印):

<Root>
  <Story>
    <Section id='preface'>
      <ChapterTitle>
        Redacted
      </ChapterTitle>
      <HeadA>
        Redacted
      </HeadA>
      <Body>
        Redacted
      </Body>
      <BulletList>
        <BulletListItem>
          Item1
        </BulletListItem>
        <BulletListItem>
          Item2
        </BulletListItem>
        <BulletListItem>
          Item3
        </BulletListItem>
        <BulletListItem>
          Item4
        </BulletListItem>
        <BulletListItem>
          Second list Item2
        </BulletListItem>
        <BulletListItem>
          Second list Item3
        </BulletListItem>
        <BulletListItem>
          Second list Item4
        </BulletListItem>
      </BulletList>
      <HeadA>
        Redacted
      </HeadA>
      <Body>
        Redacted
      </Body>
      <HeadA>
        Redacted
      </HeadA>
      <Body>
        Redacted
      </Body>
      <Body>
        Redacted
        <Italic>
          REDACTED
        </Italic>
        Redacted
      </Body>
      <BulletList>
        <BulletListItem>
          Second list Item1
        </BulletListItem>
      </BulletList>
      <Body>
        Redacted
      </Body>
    </Section>
  </Story>
</Root>

您会看到两个列表卡在一起,中间的部分丢失了。不确定这是 Ruby 库还是 XSLT 中的错误。

【问题讨论】:

  • 我懒得搜索重复...如果有人发布链接,我会删除答案。
  • 好问题,+1。请参阅我的答案,了解基于密钥的高效解决方案。
  • 我收回我所说的很明显:P 我真的需要正确学习 XSLT ...

标签: xml xslt


【解决方案1】:

这种转变

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

 <xsl:key name="kFollowing" match="ListItem[preceding-sibling::*[1][self::ListItem]]"
  use="generate-id(preceding-sibling::ListItem
         [not(preceding-sibling::*[1][self::ListItem])][1])"/>

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

 <xsl:template match="ListItem
         [not(preceding-sibling::*[1][self::ListItem])]">
  <List>
    <xsl:call-template name="identity"/>
    <xsl:apply-templates mode="copy" select="key('kFollowing', generate-id())"/>
  </List>
 </xsl:template>

 <xsl:template match="ListItem[preceding-sibling::*[1][self::ListItem]]"/>

 <xsl:template match="ListItem" mode="copy">
  <xsl:call-template name="identity"/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<Section>
  <Head>Heading</Head>
  <Para>Blah</Para>
  <ListItem>item 1</ListItem>
  <ListItem>item 2</ListItem>
  <ListItem>item 3</ListItem>
  <ListItem>item 4</ListItem>
  <Para>Something else</Para>
</Section>

产生想要的结果

<Section>
    <Head>Heading</Head>
    <Para>Blah</Para>
    <List>
        <ListItem>item 1</ListItem>
        <ListItem>item 2</ListItem>
        <ListItem>item 3</ListItem>
        <ListItem>item 4</ListItem>
    </List>
    <Para>Something else</Para>
</Section>

【讨论】:

  • @Dimitre:+1 好答案。当我看到你的时候,我打算添加这种类型的解决方案。
  • @Dimitre - 我发现了一个错误。我已经在我的问题中重现了这个案例。
  • @Skilldrick:我看不到您的问题有任何变化,我的转换正好产生了所需的结果。 ???
  • @Dimitre - 抱歉,我一直在更新我的问题。试试看是否有同样的问题。
  • @Skilldrick:是的,有一个小问题。我编辑了我的解决方案并向&lt;xsl:key&gt; 添加了 4 个字符。它适用于您的新示例——请尝试一下。
【解决方案2】:

这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="ListItem">
        <List>
            <xsl:call-template name="ListItem"/>
        </List>
        <xsl:apply-templates select="following-sibling::node()
                                      [not(self::ListItem)][1]"/>
    </xsl:template>
    <xsl:template match="ListItem[preceding-sibling::node()[1]
                                              /self::ListItem]"
                  name="ListItem">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]
                                                 /self::ListItem"/>
    </xsl:template>
</xsl:stylesheet>

输出:

<Section>
    <Head>Heading</Head>
    <Para>Blah</Para>
    <List>
        <ListItem>item 1</ListItem>
        <ListItem>item 2</ListItem>
        <ListItem>item 3</ListItem>
        <ListItem>item 4</ListItem>
    </List>
    <Para>Something else</Para>
</Section>

编辑 3:使用 strip-space

【讨论】:

  • @Alejandro - 奇怪......当我在给定的输入上运行这个样式表时,我得到了一些非常不同的东西。特别是,您的 select="following-sibling::node()[1][self::ListItem]" 错过了以下 ListItem ,因为第一个后续兄弟节点是空白文本节点。然而,我看不出有什么理由 (w3.org/TR/xslt#strip) 没有剥离该节点。我很困惑?!
  • @Alejandro:PS 这发生在 Saxon 6.5.5 in Oxygen。任何地方都没有 xml:space="preserve" 属性。
  • @Alejandro:一如既往的好答案,+1。
  • @Skilldrick:没问题。我很高兴这可以帮助你。
  • @LasrH:这就是 Saxon 处理纯空白文本节点的方式。我会为你编辑这个。
猜你喜欢
  • 2013-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-07
  • 1970-01-01
  • 2015-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多