当前位置: 首页 > 知识库问答 >
问题:

XSLT:基于路径的嵌套层次结构的平面XML

厉坚
2023-03-14

我试图基于表示路径的元素从平面XML创建嵌套层次结构。每个级别元素及其所属的同级元素(名称和数字不同)应包装在“record”元素中,从而创建树结构。

从该来源(简化):

<?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未能出现)

我尝试了不同的方法来解决类似的问题(“从平面到层次”,“跟随兄弟姐妹直到”,等等),但最终要么在某个位置打印了太多兄弟姐妹,要么在第三个级别上只输出第一条记录。

非常感谢您的帮助。


共有1个答案

彭海阳
2023-03-14

一种方法是使用钥匙。为了开始获取元素的同级,您可以定义一个键,通过最前面的元素对元素进行分组(即组将是所有同级)。

<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])" />

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

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

然后,您将有一个通用模板匹配级别元素,您可以使用这两个键来输出同级元素和下一级别元素

<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 html" target="_blank">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>

这并不完全是您当前显示为预期输出的内容,因为您的预期输出有两个“第一级”元素,它们包装在单个记录元素中(与“第三级”的单独记录元素相比)。如果您的预期输出确实符合您的预期,请尝试用这两个模板替换与录制匹配的模板:

<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>
 类似资料:
  • 我们正在创建一个REST API,目前我们有两种方法来定义资源。 基本上,我们有、和,其中一个有n个和一个有n个。 分层方法 该层次结构在URI中可见 要搜索所有图像,我们需要一个搜索资源 平进路 我们还需要考虑移动和修改。

  • 问题内容: 使用MSSQL 2008和XQUERY 考虑以下存储在表中的XML: 我想将此xml分解为类似 等等。 注意:在此示例中,每个ParentElement可能有许多ChildElements。我一直在尝试使用xquery,但是我无法像这样浏览复杂的元素。 基本上,我正尝试与FOR XML对表进行完全相反的操作,只是要使用一组更为简单的数据。 关于下一步或如何实现此目标的任何想法? 谢谢

  • 问题内容: 是否可以基于层次结构/ cte创建视图? 我看过一个有关如何基于链接递归查询生成结果集的示例。 我已经附上了ddl和声明。 谢谢你, 埃尔默 问题答案: 后已被指定(超出右括号),您需要选择从CTE的所有值: 这是构成视图的实际选择查询。 这是一个完整的工作示例,其中选择了视图的最终输出,以及一些其他语句,以允许这些语句在SQL Server Management Studio中一次执

  • 问题内容: 我敢肯定有一种非常简单而优雅的方法可以做到这一点,但是我不太清楚。我有一些看起来像这样的输入数据: 如果可能,我想使用d3.js嵌套运算符来获取要在层次结构布局中使用的结构。像这样: 问题答案: 您不能在此处使用nest运算符,因为嵌套会产生固定的层次结构:输出层次结构中的级别数与您指定的键函数的数量相同。 也就是说,您可以编写自己的函数来生成树。假设根节点是输入数组中的第一个节点,则

  • 我正在尝试使用Jolt将平面结构映射到层次结构http://jolt-demo.appspot.com/#andrewkcarter2: 我的输入是一个包含客户详细信息的平面结构:下面是我的JSON输入: 这是我的 JOLT 规格: 我得到的结果是: 在上面的输出中,我们看到帐户1001和1002重复交易的次数。我不希望这种情况发生,我希望交易被归类在各自的账户下。 所以,我想要的输出应该是: 类

  • 我正在为我的类开发一个简单的jsp/servlet/tomcat webapp。教授要求我们使用与默认动态web项目结构略有不同的文件夹结构。他不想使用webcontent文件夹,而是想要src/main/java和src/main/webapp下的所有源代码。 当我运行应用程序时,我的欢迎文件显示良好,但当我试图访问我的servlets时,我得到: ClassNotFoundException.