【问题标题】:XSLT: flat XML to nested hierarchy based on pathXSLT:平面 XML 到基于路径的嵌套层次结构
【发布时间】:2013-06-08 21:54:39
【问题描述】:

我正在尝试基于表示路径的level 元素从平面 XML 创建嵌套层次结构。每个level 元素及其所属的兄弟姐妹(名称和编号各不相同)应包装在一个“记录”元素中,从而创建一个树结构。

来自这个来源(简化):

<?xml version="1.0" encoding="UTF-8"?>
    
<record>
    
    <level>first</level>
    
    <unitid>0001</unitid>
    <a-few-more-siblings/>
    
    <level>first/second</level>
    
    <unitid>0002</unitid>
    
    <many-more-siblings/>
    <level>first/second/third</level>
    
    <unitid>0003a</unitid>
    <some-more-siblings/>
    
    <level>first/second/third</level>
    
<unitid>0003b</unitid>
    <many-more-siblings/>
    <level>first/second/third</level>
    
    <unitid>0003c</unitid>
    <some-more-siblings/>
 
    <level>first</level>
    
    <unitid>0004</unitid>

    <again-more-siblings/>
     
</record>

我想生成以下所需的输出

<Record level="first">

    <level>first</level>
    <unitid>001</unitid>
    <a-few-more-siblings/>
    <Record level="second">

        <level>second</level>
        <unitid>002</unitid>
        <many-more-siblings/>
        <Record level="third">
            <level>third</level>
            <unitid>003a</unitid>
            <some-more-siblings/>
        </Record>
        <Record level="third">

            <level>third</level>
            <unitid>003b</unitid>
            <many-more-siblings/>
        </Record>
        <Record level="third">

            <level>third</level>
            <unitid>003c</unitid>
            <some-more-siblings/>
        </Record>
    </Record>    
</Record>
<Record level="first">
    <level>first</level>
    <unitid>0004</unitid>
    <again-more-siblings/>
</Record>

到目前为止我能产生的最接近的是:

<record level="first">
   <level>first</level>
   <unitid>0001</unitid>
   <some-other-siblings/>
   <record level="second">
      <level>first/second</level>
      <unitid>0002</unitid>
      <some-other-siblings/>
      <record level="third">
             <level>first/second</level>
             <unitid>0002</unitid>
             <some-other-siblings/>
         <level>first/second/third</level>
         <unitid>0003a</unitid>
         <some-other-siblings/>
      </record>
      <record level="third">
             <level>first/second</level>
             <unitid>0002</unitid>
             <some-other-siblings/>
             <level>first/second/third</level>
             <unitid>0003a</unitid>
             <some-other-siblings/>
         <level>first/second/third</level>
         <unitid>0003b</unitid>
         <some-other-siblings/>
      </record>
      <record level="third">
         <level>first/second/third</level>
         <unitid>0003c</unitid>
         <some-other-siblings/>
      </Record>
   </record>
</record>

(第三层的不受欢迎的兄弟姐妹额外缩进;第一层的0004没有出现)

我尝试了针对类似问题提出的不同方法变体(“从平面到分层”、“跟随兄弟姐妹直到”等),但最终要么卡在某个位置打印了太多兄弟姐妹,要么只输出了第三层的第一条记录。

非常感谢任何帮助。

【问题讨论】:

    标签: xslt hierarchy siblings


    【解决方案1】:

    一种方法是使用密钥。要开始获取 level 元素的兄弟姐妹,您可以定义一个键来按最前面的 level 元素对元素进行分组(即该组将是所有兄弟姐妹)。

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

    您还可以定义一个键来获取 level 元素的直接“后代”(即,对于每个级别,将它们按最前面的第一个级别和短名称分组)。

    <xsl:key name="nextlevel" 
         match="level" 
         use="generate-id(preceding-sibling::level[starts-with(current(), concat(., '/'))][1])" />
    

    在您的 XSLT 中,您只需选择“第一”级元素即可开始

    <xsl:apply-templates select="level[. = 'first']" />
    

    然后,您将拥有一个匹配 level 元素的通用模板,您可以在其中利用这两个键来输出兄弟元素和下一级元素

    <xsl:template match="level">
        <Record level="{.}">
            <xsl:copy-of select="." />
            <xsl:apply-templates select="key('siblings', generate-id())" />
            <xsl:apply-templates select="key('nextlevel', generate-id())" />
        </Record>
    </xsl:template>
    

    试试下面的 XSLT

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
        <xsl:key name="siblings" match="*[not(self::level)]" use="generate-id(preceding-sibling::level[1])" />
    
        <xsl:key name="nextlevel" match="level" use="generate-id(preceding-sibling::level[starts-with(current(), concat(., '/'))][1])" />
    
        <xsl:template match="record">
            <xsl:apply-templates select="level[. = 'first']" />
        </xsl:template>
    
        <xsl:template match="level">
            <Record level="{.}">
                <xsl:copy-of select="." />
                <xsl:apply-templates select="key('siblings', generate-id())" />
                <xsl:apply-templates select="key('nextlevel', generate-id())" />
            </Record>
        </xsl:template>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    当应用于您的 XML 时,将输出以下内容

    <Record level="first">
        <level>first</level>
        <unitid>0001</unitid>
        <a-few-more-siblings/>
        <Record level="first/second">
            <level>first/second</level>
            <unitid>0002</unitid>
            <many-more-siblings/>
            <Record level="first/second/third">
                <level>first/second/third</level>
                <unitid>0003a</unitid>
                <some-more-siblings/>
            </Record>
            <Record level="first/second/third">
                <level>first/second/third</level>
                <unitid>0003b</unitid>
                <many-more-siblings/>
            </Record>
            <Record level="first/second/third">
                <level>first/second/third</level>
                <unitid>0003c</unitid>
                <some-more-siblings/>
            </Record>
        </Record>
    </Record>
    <Record level="first">
        <level>first</level>
        <unitid>0004</unitid>
        <again-more-siblings/>
    </Record>
    

    这不是您当前显示的预期输出,因为您的预期输出有两个“第一个”level 元素包裹在单个 Record 元素中(与单独的 Record 元素相比,'第三个'level 元素)。如果您的预期输出确实符合您的预期,请尝试将匹配 record 的模板替换为以下两个模板:

    <xsl:template match="record">
        <Record level="first">
            <xsl:apply-templates select="level[. = 'first']" />
         </Record>
    </xsl:template>
    
    <xsl:template match="level[. = 'first']">
        <xsl:copy-of select="." />
        <xsl:apply-templates select="key('siblings', generate-id())" />
        <xsl:apply-templates select="key('nextlevel', generate-id())" />
    </xsl:template>
    

    【讨论】:

    • 完美,非常感谢。并且对first 级别元素进行了很好的观察。确实我后来加了第二个是为了说明后面的兄弟姐妹可能属于更高的级别,但我自己却弄糊涂了。
    猜你喜欢
    • 1970-01-01
    • 2019-12-20
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    • 2017-12-17
    • 2018-08-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多