如何使用XSLT创建单个元素集的子集(其中元素名称是复杂的)?

时间:2009-07-02 16:34:20

标签: xml algorithm xslt

继续提出有关"How to create subsets of a single set of elements with XSLT?"

的问题

我希望我的问题更进一步: 我最初将以下XML作为原始XML:

<Set>
   <Element name="Superset1_Set1_Element1"/>
   <Element name="Superset1_Set1_Element2"/>
   <Element name="Superset1_Set2_Element1"/>
   <Element name="Superset2_Set1_Element1"/>
   <Element name="Superset2_Set2_Element1"/>
</Set>

并要求XSL Transformation产生以下输出:

<Superset name="Superset1">
   <Set name="Set1">
       <Element name="Element1"/>
       <Element name="Element2"/>
   </Set>
   <Set name="Set2">
       <Element name="Element1"/>
   </Set>
</Superset>
<Superset name="Superset2">
   <Set name="Set1">
       <Element name="Element1"/>
   </Set>
   <Set name="Set2">
       <Element name="Element1"/>
   </Set>
</Superset>

Tomalakannakata都给了我一个有效的解决方案。我选择了Tomalak,因为它使用的模板在我看来更具人性化。

问题是我的XML实际上是以下形式:

<Set>
   <Element name="Classic_Authors_Dante_Alighieri_The_Divine_Comedy"/>
   <Element name="Classic_Authors_Dante_Alighieri_Convivio"/>
   <Element name="Classic_Authors_Miguel_de_Cervantes_Saavedra_Don_Quixote"/>
   <Element name="Contemporary_Authors_Stephen_King_Just_After_Sunset"/>
   <Element name="Contemporary_Authors_Stephen_King_Desperation"/>
</Set>

Supersets,sets和elements在其中包含不同数量的下划线。 在上面的例子中有两个超集:'Classic_Authors'和'Contemporary_Authors'。这三组是'Dante_Alighieri','Miguel_de_Cervantes_Saavedra'和'Stephen_King'。

输出XML应为:

<Superset name="Classic_Authors">
   <Set name="Dante_Alighieri">
       <Element name="The_Divine_Comedy"/>
       <Element name="Convivio"/>
   </Set>
   <Set name="Miguel_de_Cervantes_Saavedra">
       <Element name="Don_Quixote"/>
   </Set>
</Superset>
<Superset name="Contemporary_Authors">
   <Set name="Stephen_King">
       <Element name="Just_After_Sunset"/>
       <Element name="Desperation"/>
   </Set>
</Superset>

那怎么样,我可以使用Tomalak的解决方案吗?也就是说,我应该如何准备我的xml来使用他的算法呢?可以在单个XSLT中完成吗?可能有另一种解决方案吗?

非常感谢!

3 个答案:

答案 0 :(得分:1)

正如我在上一个问题中对我的回答的评论中所说,在开始解决此问题之前,您需要一个包含固定和已知集合名称的文件。理想情况下,它的结构如下:

<!-- SetNames.xml --->
<names>
  <Superset name="Classic_Authors">
    <Set name="Dante_Alighieri" />
    <Set name="Miguel_de_Cervantes_Saavedra" />
  </Superset>
  <Superset name="Contemporary_Authors">
    <Set name="Stephen_King" />
  </Superset>
</names>

如果没有这样的文件,问题将无法解决。现在你有了很好的结构化名称,你可以根据它进行分组(实质上,它已经是输出格式,你需要做的只是匹配你的数据):

<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:param name="pSetFile" select="'SetNames.xml'" />
  <xsl:variable name="root" select="/" />

  <xsl:template match="/Set">
    <xsl:copy>
      <xsl:variable name="vSetDoc" select="document($pSetFile)" />
      <xsl:apply-templates select="$vSetDoc/names/Superset">
        <xsl:sort select="@name" />
      </xsl:apply-templates>
    </xsl:copy> 
  </xsl:template>

  <xsl:template match="Superset">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:apply-templates select="Set">
        <xsl:sort select="@name" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Set">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:variable name="vPrefix" select="
        concat(../@name, '_', @name, '_')
      " />
      <xsl:apply-templates select="
        $root/Set/Element[starts-with(@name, $vPrefix)]
      ">
        <xsl:sort select="@name" />
        <xsl:with-param name="pPrefix" select="$vPrefix" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Element">
    <xsl:param name="pPrefix" select="''" />

    <xsl:copy>
      <xsl:attribute name="name">
        <xsl:value-of select="substring-after(@name, $pPrefix)" />
      </xsl:attribute>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

当应用于您的输入时,会产生:

<Set>
  <Superset name="Classic_Authors">
    <Set name="Dante_Alighieri">
      <Element name="Convivio" />
      <Element name="The_Divine_Comedy" />
    </Set>
    <Set name="Miguel_de_Cervantes_Saavedra">
      <Element name="Don_Quixote" />
    </Set>
  </Superset>
  <Superset name="Contemporary_Authors">
    <Set name="Stephen_King">
      <Element name="Desperation" />
      <Element name="Just_After_Sunset" />
    </Set>
  </Superset>
</Set>

由于SetNames.xml基本上已经分组,因此不需要进一步(Muenchian)分组。上面最慢的表达方式是:

$root/Set/Element[starts-with(@name, $vPrefix)]

这种“表扫描”类型的表达式正是<xsl:key>有用的地方,但由于问题的性质,它不能在这里使用。

答案 1 :(得分:0)

问题是关于元素的所有信息都塞满了一个属性。您应该将数据的语义不同部分分成单独的元素或单独的属性,即:

<Set>
    <Element title="The Divine Comedy" author="Dante Alighieri" category="Classic Authors"/>
    ...

如果您对现有元素感到困惑,恐怕我没有一个好的解决方案。作为一个人,我甚至很难确定“名称”的哪些部分是标题,作者或类别。我想不出解析数据的简单方法。

答案 2 :(得分:0)

没有确定性的方法将书名与作者姓名分开。每个下划线的数量各不相同。

唯一的解决方案是让发件人的作者以某种方式更改格式,从而为您的输入添加更多信息。 (也许书和作者之间有两个下划线?)