如何使用XSLT包装一组相邻元素?

时间:2010-10-18 18:25:27

标签: xml xslt

我有一些带有<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(输出prettyprint):

<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中的错误。

2 个答案:

答案 0 :(得分:5)

此转化

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

答案 1 :(得分:4)

此样式表:

<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获取内容。