xml到xml使用xslt:递归匹配&像树一样创建层次结构

时间:2013-02-09 21:03:44

标签: xslt xslt-1.0

我对xslt完全陌生。请帮我写样式表。我有这样的输入xml

输入XML:

    <elements>
     <e1>
       <pid>1</pid>
       <cid>2</cid>
     </e1>

     <e1>
      <pid>1</pid>
      <cid>3</cid>
     </e1>

     <e1>
      <pid>2</pid>
      <cid>4</cid>
    </e1>
    </elements>

所需的XML:

    <tree>
      <unit id="1">
        <unit id="2">
           <unit id="4">
             <data></data>
           </unit>
           <data></data>
        </unit>

        <unit id="3">
           <data></data>
        </unit>

        <data></data>

      </unit>
    </tree>

我觉得这应该很容易,但我很难找到有关如何做到这一点的信息。我的XSLT知识不是很好。

2 个答案:

答案 0 :(得分:3)

我不是100%确定你希望XSLT如何根据该输入确定top id是1(是因为它是唯一的pid值,没有相应的cid值,或者它总是1?)。尽管如此,这应该做的工作:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:key name="kItemsByC" match="e1" use="cid" />
  <xsl:key name="kItemsByP" match="e1" use="pid" />

  <xsl:template match="/">
    <tree>
      <xsl:call-template name="Unit">
        <!-- This will be the value of the <pid> that has no <cid> references to
             it (assuming there is only one top-level <pid>) -->
        <xsl:with-param name="id" 
                        select="string(/elements/e1/pid[not(key('kItemsByC', .))])" />
      </xsl:call-template>
    </tree>
  </xsl:template>

  <xsl:template match="e1" name="Unit">
    <xsl:param name="id" select="cid" />

    <unit id="{$id}">
      <xsl:apply-templates select="key('kItemsByP', $id)" />
      <data />
    </unit>
  </xsl:template>
</xsl:stylesheet>

当您对样本输入运行此操作时,会产生:

<tree>
  <unit id="1">
    <unit id="2">
      <unit id="4">
        <data />
      </unit>
      <data />
    </unit>
    <unit id="3">
      <data />
    </unit>
    <data />
  </unit>
</tree>

注意:上述XSLT具有尝试动态定位顶级ID的逻辑。如果可以假设顶级单元总是具有ID 1,则可以消除一个密钥和上述XSLT(某种程度上)复杂的公式:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:key name="kItemsByP" match="e1" use="pid" />

  <xsl:template match="/">
    <tree>
      <xsl:call-template name="Unit">
        <xsl:with-param name="id" select="1" />
      </xsl:call-template>
    </tree>
  </xsl:template>

  <xsl:template match="e1" name="Unit">
    <xsl:param name="id" select="cid" />

    <unit id="{$id}">
      <xsl:apply-templates select="key('kItemsByP', $id)" />
      <data />
    </unit>
  </xsl:template>
</xsl:stylesheet>

在样本输入上运行时,这也会产生请求的输出。

答案 1 :(得分:0)

啊,在阅读JLRishe后,我想我明白了:“pid”表示“父ID”,“cid”表示“子ID”,e1表示父子关系。精湛的侦探工作,我永远不会为自己解决这个问题。

基本模型是当您定位在父元素上时,您会对其子元素应用模板。如果父/子关系由主/外键表示,就像使用XML层次结构表示父/子关系一样,这也适用。所以本质是:

<xsl:template match="e1">
  <unit id="{pid}">
    <xsl:apply-templates select="//e1[pid=current()/cid]"/>
    <data/>
  </unit>
</xsl:template>

这实际上是JLRishe的解决方案,除了他已经使用密钥添加了优化。