XSLT 递归父/子组合 [英] XSLT Recursive Parent/Child Combination

查看:56
本文介绍了XSLT 递归父/子组合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

非常有趣的 Python 我知道可以用 XSLT 1.0 解决赏金问题.请注意,这不是一个重复的问题,因为之前的帖子以 Python 方法为中心,而这是针对同一问题尝试 XSLT 解决方案.以下是我的尝试,但仅限于预设数量的父/子组合,这里有四个级别,并且有条件地遍历每个级别.

Very interesting Python bounty question which I know can be solved with XSLT 1.0. Please note this is not a duplicate question as previous post centered on Python methods while this is attempting an XSLT solution to the same problem. Below is my attempt but is constrained to a pre-set number of parent/child combinations here being four levels deep and walks through each level conditionally.

有没有办法将我的解决方案推广到任何组合级别?我知道这可能需要使用 --> 分隔符标记值.预期输出是当前输出但需要动态解决方案.我包含 Python 脚本以显示最终结果.为了明确利益冲突,我不会在上面的帖子中使用任何答案,但请邀请您这样做!

Is there a way to generalize my solution for any combination level? I understand this may require tokenizing values with the --> separator. Expected output is current output but need a dynamic solution. I include the Python script to show final end result. To be clear in conflict of interest, I will not use any answer here in above post but do kindly invite you to do so!

XML 输入

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<nodes>
    <node name="Car" child="Engine"/>
    <node name="Car" child="Wheel"/>
    <node name="Engine" child="Piston"/>
    <node name="Engine" child="Carb"/>
    <node name="Carb" child="Bolt"/>
    <node name="Spare Wheel"/>
    <node name="Bolt" child="Thread"/>
    <node name="Carb" child="Foat"/>
    <node name="Truck" child="Engine"/>
    <node name="Engine" child="Bolt"/>
    <node name="Wheel" child="Hubcap"/>
</nodes>

XSLT

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

  <xsl:template match="nodes">
    <data>
      <xsl:apply-templates select="node[not(@name=ancestor::nodes/node/@child)]"/>
    </data>
  </xsl:template>

  <xsl:template match="node">
    <xsl:variable select="@name" name="currname"/>
    <xsl:variable select="@child" name="currchild"/>
    <xsl:variable select="/nodes/node" name="nodeset1"/>
    <xsl:variable select="/nodes/node[@name=$currchild]" name="nodeset2"/>
    <xsl:variable select="/nodes/node[@name=$nodeset2/@child]" name="nodeset3"/>
    <xsl:variable select="/nodes/node[@name=$nodeset3/@child]" name="nodeset4"/>

        <xsl:for-each select="$nodeset2">
          <xsl:variable select="@child" name="nodeset2child"/>

          <xsl:for-each select="$nodeset3">
              <xsl:variable select="@child" name="nodeset3child"/>              
              <xsl:if test="@name=$nodeset2child">

                <xsl:for-each select="$nodeset4">
                  <xsl:if test="@name=$nodeset3child">
                    <xsl:value-of select="$currname"/> --> <xsl:value-of select="$currchild"/> --> <xsl:value-of select="$nodeset2child"/> --> <xsl:value-of select="$nodeset3child"/> --> <xsl:value-of select="@child"/><xsl:text>&#xa;</xsl:text>
                  </xsl:if>                  
                </xsl:for-each>
                <xsl:if test="$nodeset2child!=$nodeset3/@child and $nodeset3child != $nodeset4/@name">                  
                    <xsl:value-of select="$currname"/> --> <xsl:value-of select="$currchild"/> --> <xsl:value-of select="$nodeset2child"/> --> <xsl:value-of select="$nodeset3child"/><xsl:text>&#xa;</xsl:text>
                </xsl:if>

              </xsl:if>              
          </xsl:for-each>

          <xsl:if test="not($nodeset2child=$nodeset3/@child or ancestor::nodes/node[@name=$nodeset2child]/@child)">
              <xsl:value-of select="$currname"/> --> <xsl:value-of select="$currchild"/> --> <xsl:value-of select="$nodeset2child"/><xsl:text>&#xa;</xsl:text>
          </xsl:if>

        </xsl:for-each>
        <xsl:value-of select="@name[not(ancestor::node/@child=$nodeset2/@name)]"/><xsl:text>&#xa;</xsl:text>

  </xsl:template>  
</xsl:transform>

XML 转换后的输出

<?xml version='1.0' encoding='UTF-8'?>
<data>Car --&gt; Engine --&gt; Piston
Car --&gt; Engine --&gt; Carb --&gt; Bolt --&gt; Thread
Car --&gt; Engine --&gt; Carb --&gt; Foat
Car --&gt; Engine --&gt; Bolt --&gt; Thread

Car --&gt; Wheel --&gt; Hubcap

Spare Wheel
Truck --&gt; Engine --&gt; Piston
Truck --&gt; Engine --&gt; Carb --&gt; Bolt --&gt; Thread
Truck --&gt; Engine --&gt; Carb --&gt; Foat
Truck --&gt; Engine --&gt; Bolt --&gt; Thread

</data>

Python 脚本 (在转换后的输出根节点上运行 xpath)

import lxml.etree as ET

# LOAD XML AND XSL DOCS
dom = ET.parse('Input.xml')
xslt = ET.parse('XSLTScript.xsl')

# TRANSFORM XML
transform = ET.XSLT(xslt)
newdom = transform(dom)

# XPATH NEW DOM ROOT NODE (<data>)        
print(newdom.xpath('/data')[0].text.replace("\n\n", "\n"))

# Car --> Engine --> Piston
# Car --> Engine --> Carb --> Bolt --> Thread
# Car --> Engine --> Carb --> Foat
# Car --> Engine --> Bolt --> Thread
# Car --> Wheel --> Hubcap
# Spare Wheel
# Truck --> Engine --> Piston
# Truck --> Engine --> Carb --> Bolt --> Thread
# Truck --> Engine --> Carb --> Foat
# Truck --> Engine --> Bolt --> Thread

推荐答案

这是一个更短(23 行)且高效的解决方案

这在计算上也是最简单的——比较嵌套级别 1 和嵌套级别 3 - 4 ...

This is also computationally the simplest one -- compare nesting level 1 to nesting level of 3 - 4 ...

这个解决方案是尾递归意味着任何好的 XSLT 处理器都会通过迭代对其进行优化,从而避免堆栈溢出的可能性,因为最大调用堆栈深度保持不变 (1):

This solution is tail-recursive meaning that any good XSLT processor optimizes it with iteration, thus avoiding the possibility of stack-overflow, as the maximum call-stack depth remains constant (1):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kNodeByChild" match="node" use="@child"/>
 <xsl:key name="kNodeByName" match="node" use="@name"/>

  <xsl:template match="/*">
    <xsl:apply-templates select="node[not(key('kNodeByChild', @name))]"/>
  </xsl:template>

  <xsl:template match="node[not(key('kNodeByName', @child))]">
    <xsl:param name="pParentPath"/>
    <xsl:value-of select="concat($pParentPath, @name, ' ---> ', @child, '&#xA;')"/>
  </xsl:template>

  <xsl:template match="node">
    <xsl:param name="pParentPath"/>

    <xsl:apply-templates select="key('kNodeByName', @child)">
      <xsl:with-param name="pParentPath" select="concat($pParentPath, @name, ' ---> ')"/>
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<nodes>
    <node name="Car" child="Engine"/>
    <node name="Car" child="Wheel"/>
    <node name="Engine" child="Piston"/>
    <node name="Engine" child="Carb"/>
    <node name="Carb" child="Bolt"/>
    <node name="Spare Wheel"/>
    <node name="Bolt" child="Thread"/>
    <node name="Carb" child="Foat"/>
    <node name="Truck" child="Engine"/>
    <node name="Engine" child="Bolt"/>
    <node name="Wheel" child="Hubcap"/>
</nodes>

产生了想要的、正确的结果:

Car ---> Engine ---> Piston
Car ---> Engine ---> Carb ---> Bolt ---> Thread
Car ---> Engine ---> Carb ---> Foat
Car ---> Engine ---> Bolt ---> Thread
Car ---> Wheel ---> Hubcap
Spare Wheel ---> 
Truck ---> Engine ---> Piston
Truck ---> Engine ---> Carb ---> Bolt ---> Thread
Truck ---> Engine ---> Carb ---> Foat
Truck ---> Engine ---> Bolt ---> Thread

这篇关于XSLT 递归父/子组合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆