如何订购自引用 xml



我有一个订单行列表,上面有每个产品。中的产品可以形成一个自引用层次结构。我需要以这样一种方式订购行,即所有没有父级或其父级在订单中缺少父级的产品都位于顶部,其次是其子项。在最终结果中,任何子项都不得高于其父项。

那么我该如何订购以下 xml:

<order>
  <line><product code="3" parent="1"/></line>
  <line><product code="2" parent="1"/></line>
  <line><product code="6" parent="X"/></line>
  <line><product code="1" /></line>
  <line><product code="4" parent="2"/></line>
</order>

进入这个:

<order>
  <line><product code="6" parent="X"/></line>
  <line><product code="1" /></line>
  <line><product code="2" parent="1"/></line>
  <line><product code="3" parent="1"/></line>
  <line><product code="4" parent="2"/></line>
</order>

请注意,特定级别内的顺序并不重要,只要子节点在其父节点之后的某个时间点遵循即可。

我有一个适用于不超过预定义深度的层次结构的解决方案:

<order>
<xsl:variable name="level-0" 
  select="/order/line[ not(product/@parent=../line/product/@code) ]"/>
<xsl:for-each select="$level-0">
   <xsl:copy-of select="."/>
</xsl:for-each>
<xsl:variable name="level-1"
  select="/order/line[ product/@parent=$level-0/product/@code ]"/>
<xsl:for-each select="$level-1">
   <xsl:copy-of select="."/>
</xsl:for-each>
<xsl:variable name="level-2"
  select="/order/line[ product/@parent=$level-1/product/@code ]"/>
<xsl:for-each select="$level-2">
   <xsl:copy-of select="."/>
</xsl:for-each>
</order>

上面的示例 xslt 适用于最大深度为 3 个级别的层次结构,并且很容易扩展到更多级别,但是我如何概括这一点并让 xslt 正确排序任意深度级别?

首先,您可以定义几个键来帮助您通过代码属性查找元素

<xsl:key name="products-by-parent" match="line" use="product/@parent" />
<xsl:key name="products-by-code" match="line" use="product/@code" />

首先,您将选择没有父元素的元素,使用键执行此检查:

<xsl:apply-templates select="line[not(key('products-by-code', product/@parent))]"/>
然后,

在与元素匹配的模板中,您只需复制该元素,然后使用另一个键选择其"子项"

,如下所示
<xsl:apply-templates select="key('products-by-parent', product/@code)"/>

这将是一个递归调用,因此它将递归查找其子项,直到找不到更多子项。

试试这个 XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="products-by-parent" match="line" use="product/@parent"/>
   <xsl:key name="products-by-code" match="line" use="product/@code"/>
   <xsl:template match="order">
      <xsl:copy>
         <xsl:apply-templates select="line[not(key('products-by-code', product/@parent))]"/>
      </xsl:copy>
   </xsl:template>
   <xsl:template match="line">
      <xsl:call-template name="identity"/>
      <xsl:apply-templates select="key('products-by-parent', product/@code)"/>
   </xsl:template>
   <xsl:template match="@*|node()" name="identity">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

请注意使用 XSLT 标识转换来复制 XML 中的现有节点。

非常有趣的问题。我将分两次执行此操作:首先,根据元素的层次结构嵌套元素。然后输出元素,按其祖先的计数排序。

XSLT 1.0 (+ EXSLT node-set() 函数):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="product-by-code" match="product" use="@code" />
<!-- first pass -->
<xsl:variable name="nested">
    <xsl:apply-templates select="/order/line/product[not(key('product-by-code', @parent))]" mode="nest"/>
</xsl:variable>
<xsl:template match="product" mode="nest">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates select="../../line/product[@parent=current()/@code]" mode="nest"/>
    </xsl:copy>
</xsl:template>
<!-- output -->
<xsl:template match="/order">
    <xsl:copy>
        <xsl:for-each select="exsl:node-set($nested)//product">
        <xsl:sort select="count(ancestor::*)" data-type="number" order="ascending"/>
            <line><product><xsl:copy-of select="@*"/></product></line>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet> 

应用于输入时,结果为:

<?xml version="1.0" encoding="UTF-8"?>
<order>
   <line>
      <product code="6" parent="X"/>
   </line>
   <line>
      <product code="1"/>
   </line>
   <line>
      <product code="3" parent="1"/>
   </line>
   <line>
      <product code="2" parent="1"/>
   </line>
   <line>
      <product code="4" parent="2"/>
   </line>
</order>

这仍然留下了现有/缺失的父 X 的问题 - 我稍后会尝试解决这个问题。

最新更新