XSLT 1.0:需要对具有父 ID 的元素进行分组

XSLT 1.0 : Need to group the elements which is having the Parent ID

我需要将 Parent/sscc 与其 Logistic/sscc 相关联。我们需要在输入文件中检查相同的 Parent/sscc 并且需要将其与其 logistic/sscc 相关联。作为结果输出必须是 logistic/sscc 及其 parent/sscc。我提供了输入和输出的详细信息。 新代码不复制 Parent/sscc 及其后勤 /sscc

输入:

<?xml version="1.0" encoding="UTF-8"?>
<wONM>
   <Standard>
      <Sender>1</Sender>                
   </Standard>
   <warehousingNotification>      
      <warehousingIdentification>
         <entityIdentification>000031115</entityIdentification>
      </warehousingIdentification>
      <warehousingOperationsTransaction>
        <SNumber>1</SNumber>
         <warehousingOperationsLocation>
            <inventoryLocation>        
            </inventoryLocation>
            <logistic>
               <sscc>101</sscc>
               <parent>
                  <sscc>203</sscc>
               </parent>               
            </logistic>
            <logistic>
               <sscc>190</sscc>
               <parent>
                  <sscc>204</sscc>
               </parent>              
            </logistic>
            <logistic>
               <sscc>102</sscc>
               <parent>
                  <sscc>203</sscc>
               </parent>               
            </logistic>
            <logistic>
               <sscc>191</sscc>
               <parent>
                  <sscc>204</sscc>
               </parent>               
            </logistic>
         </warehousingOperationsLocation>
      </warehousingOperationsTransaction>
      <warehousingOperationsTransaction>
        <SNumber>2</SNumber>
         <warehousingOperationsLocation>
            <inventoryLocation>
            </inventoryLocation>
            <logistic>
               <sscc>192</sscc>
               <parent>
                  <sscc>204</sscc>
               </parent>               
            </logistic>
            <logistic>
               <sscc>107</sscc>
               <parent>
                  <sscc>203</sscc>
               </parent>               
            </logistic>
            <logistic>
               <sscc>197</sscc>
               <parent>
                  <sscc>204</sscc>
               </parent>               
            </logistic>
            <logistic>
               <sscc>108</sscc>
               <parent>
                  <sscc>203</sscc>
               </parent>               
            </logistic>
         </warehousingOperationsLocation>
      </warehousingOperationsTransaction>
      <warehousingOperationsTransaction>
         <SNumber>3</SNumber>
         <warehousingOperationsLocation>
            <inventoryLocation>
            </inventoryLocation>
            <logistic>
               <sscc>101</sscc>
               <parent>
                  <sscc>205</sscc>
               </parent>              
            </logistic>
            <logistic>
               <sscc>102</sscc>
               <parent>
                  <sscc>206</sscc>
               </parent>               
            </logistic>
            <logistic>
               <sscc>109</sscc>
               <parent>
                  <sscc>203</sscc>
               </parent>              
            </logistic>
            <logistic>
               <sscc>110</sscc>
               <parent>
                  <sscc>203</sscc>
               </parent>               
            </logistic>
         </warehousingOperationsLocation>
      </warehousingOperationsTransaction>
   </warehousingNotification>
</wONM>

输出:

<?xml version="1.0" encoding="UTF-8"?>
<wONM>
   <Standard>
      <Sender>1</Sender>                
   </Standard>
   <warehousingNotification>      
       <warehousingIdentification>
         <entityIdentification>000031115</entityIdentification>
      </warehousingIdentification>
      <warehousingOperationsTransaction>
        <SNumber>1</SNumber>
         <warehousingOperationsLocation>
            <inventoryLocation>        
            </inventoryLocation>
            <logistic>
               <sscc>101</sscc>
               <sscc>102</sscc>
               <sscc>107</sscc>
               <sscc>108</sscc>
               <sscc>109</sscc>
               <sscc>110</sscc>
               <parent>
                  <sscc>203</sscc>
               </parent>               
            </logistic>
            <logistic>
               <sscc>190</sscc>
               <sscc>191</sscc>
               <sscc>192</sscc>
               <sscc>197</sscc>
               <parent>
                  <sscc>204</sscc>
               </parent>              
            </logistic>
            <logistic>
               <sscc>101</sscc>
               <parent>
                  <sscc>205</sscc>
               </parent>               
            </logistic>
            <logistic>
               <sscc>102</sscc>
               <parent>
                  <sscc>206</sscc>
               </parent>               
            </logistic>
         </warehousingOperationsLocation>
      </warehousingOperationsTransaction>   

   </warehousingNotification>
</wONM>

这看起来像是一个标准的分组问题。在 XSLT 2.0+ 中,它是 <xsl:for-each-group select=".//logistic" group-by="parent/sscc">。在 XSLT 1.0 中分组要困难得多,但如果您真的不能继续使用 XSLT 2.0 处理器,那么请阅读 Muenchian 分组技术,每本 XSLT 1.0 教科书和许多在线教程都介绍了这些技术。

这可以使用 XSLT 2.0 分组来实现,下面是实现所需输出的完整 XSLT 代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">

    <xsl:output indent="yes"></xsl:output>

    <xsl:template match="node()|@*">
        <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
    </xsl:template>

    <xsl:template match="*:wONM">
        <wONM>
            <xsl:apply-templates select="*:Standard"></xsl:apply-templates>
            <warehousingNotification>
                <xsl:apply-templates select="*:warehousingNotification/*:warehousingIdentification"/>
                <warehousingOperationsTransaction>
                    <xsl:apply-templates select="*:warehousingNotification/*:warehousingOperationsTransaction/*:SNumber"/>
                    <warehousingOperationsLocation>
                        <xsl:apply-templates select="*:warehousingNotification/*:warehousingOperationsTransaction/*:warehousingOperationsLocation/*:inventoryLocation"></xsl:apply-templates>
                        <xsl:for-each-group select="*:warehousingNotification//*:logistic" group-by="*:parent/*:sscc">
                            <logisticaaa>
                                <xsl:for-each select="current-group()">
                                    <sscc>
                                        <xsl:value-of select="./*:sscc"/>
                                    </sscc>
                                </xsl:for-each> 
                                <parent>
                                    <sscc><xsl:value-of select="current-grouping-key()"/></sscc>
                                </parent>
                            </logisticaaa>
                        </xsl:for-each-group>
                    </warehousingOperationsLocation>
                </warehousingOperationsTransaction>
            </warehousingNotification>            
        </wONM>
    </xsl:template>

</xsl:stylesheet>

如评论中所述,您没有尝试自己解决此问题。

我不会给您样式表(我已经写好了),而是分解我遵循的步骤,希望您可以自己尝试。如果您尝试通过显示努力来更新问题,我会将我的完整样式表添加到我的答案中。

第 1 步

从身份转换开始。这基本上会输出任何输入不变的内容。

我不会详细说明输入和输出之间可能存在的差异。

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

  <!--step 1-->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

第 2 步

我在您的输出中看到的第一个更改(按文档顺序)是您将所有 warehousingOperationsTransaction 元素合并为一个元素。

为此,我们可以添加一个与父项 (warehousingNotification) 匹配的模板。

(同样,我从 xsl:copy/xsl:apply-templates 开始,以获得不变的相同输出;就像恒等变换一样。)

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

  <!--step 1-->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!--step 2-->    
  <xsl:template match="warehousingNotification">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

第 3 步

现在我们可以将 xsl:apply-templates 更改为不处理现有的 warehousingOperationsTransaction 元素。

我们还可以创建新的 warehousingOperationsTransaction 以及其他静态元素(SNumberwarehousingOperationsLocationinventoryLocation)。

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

  <xsl:key name="parent" match="parent/sscc" use="."/>
  <xsl:key name="sscc_by_parent" match="sscc" use="../parent/sscc"/>

  <!--step 1-->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>


  <!--step 2-->
  <xsl:template match="warehousingNotification">
    <xsl:copy>
      <!--step 3 (exclude warehousingOperationsTransaction and add a new one)-->
      <xsl:apply-templates select="@*|node()[not(self::warehousingOperationsTransaction)]"/>
      <warehousingOperationsTransaction>
        <SNumber>1</SNumber>
        <warehousingOperationsLocation>
          <inventoryLocation/>          
        </warehousingOperationsLocation>
      </warehousingOperationsTransaction>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

- 超时 -

对于接下来的步骤,我只在 XSLT 中提供注释,我将在其中放置每个步骤的代码。如果您需要帮助,您可以在这里接管并提出具体问题。 (xsl:key 除外;我会给你那些。)

您需要了解 Muenchian 分组才能进行后续步骤。可以在这里找到一个很好的参考:Jeni's XSLT Pages: Grouping Using the Muenchian Method

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

  <!--step 4-->

  <!--step 1-->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>


  <!--step 2-->
  <xsl:template match="warehousingNotification">
    <xsl:copy>
      <!--step 3 (exclude warehousingOperationsTransaction and add a new one)-->
      <xsl:apply-templates select="@*|node()[not(self::warehousingOperationsTransaction)]"/>
      <warehousingOperationsTransaction>
        <SNumber>1</SNumber>
        <warehousingOperationsLocation>
          <inventoryLocation/>
          <!--step 5 (create logistic for each parent)-->
              <!--step 6 (output the sscc's based on parent (current context))-->
        </warehousingOperationsLocation>
      </warehousingOperationsTransaction>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

第 4 步

您需要创建 xsl:key 个元素进行分组。

由于您要为每个唯一 parent/sscc 创建一个新的 logic 元素,请为父元素创建一个键。

您还需要基于同级 parent/sscc 的所有 logistic/sscc 元素的键。

为了帮助您入门,这些是我使用的键:

<xsl:key name="parent" match="parent/sscc" use="."/>
<xsl:key name="sscc_by_parent" match="logistic/sscc" use="../parent/sscc"/>

第 5 步

为每个(xsl:for-每个)唯一父元素创建一个 logistic/parent 元素。

这个(没有双关语意)的关键是看 Jenny 的例子,其中提到“有几种通用方法可以测试两个节点是否相同”。

提示:您将使用 key() function in a predicate。您需要使用 "parent" 键。

第 6 步

现在我们需要在 logistic 元素中插入所有 sscc 元素。

为此,您可以在新的 parent 元素之前使用 xsl:copy-of。您将再次使用 key() 函数来访问 sscc_by_parent 键。

其中唯一棘手的部分是 key() 的第二个参数使用什么。由于您可能在步骤 5 中使用了 xsl:for-each 到 select .//parent/sscc,因此我们需要的值已经是 current 上下文。 (link 是提示。)

祝你好运!

这至少应该足以让您入门。如果没有,请考虑接受正规培训或聘请顾问。

编辑:转向 XSLT 2.0 是正确的决定。这是 1.0 样式表;也许将来会对其他人有所帮助。

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

  <!--step 4-->
  <xsl:key name="parent" match="parent/sscc" use="."/>
  <xsl:key name="sscc_by_parent" match="logistic/sscc" use="../parent/sscc"/>

  <!--step 1-->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>


  <!--step 2-->
  <xsl:template match="warehousingNotification">
    <xsl:copy>
      <!--step 3 (exclude warehousingOperationsTransaction and add a new one)-->
      <xsl:apply-templates select="@*|node()[not(self::warehousingOperationsTransaction)]"/>
      <warehousingOperationsTransaction>
        <SNumber>1</SNumber>
        <warehousingOperationsLocation>
          <inventoryLocation/>
          <!--step 5 (create logistic for each parent)-->
          <xsl:for-each select=".//parent/sscc[count(.|key('parent', .)[1]) = 1]">
            <logistic>
              <!--step 6 (output the sscc's based on parent (current context))-->
              <xsl:copy-of select="key('sscc_by_parent', current())"/>
              <parent>
                <xsl:copy-of select="."/>
              </parent>
            </logistic>
          </xsl:for-each>
        </warehousingOperationsLocation>
      </warehousingOperationsTransaction>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Fiddle: http://xsltfiddle.liberty-development.net/3MvmXiZ