【问题标题】:Simple way to group heavy data in xslt 1.0 using non Muenchian grouping使用非 Muenchian 分组在 xslt 1.0 中对大量数据进行分组的简单方法
【发布时间】:2015-01-14 19:55:36
【问题描述】:

我是 xslt 的新手,因此无法理解这里为我想要实现的目标提供的复杂解决方案。以非 Muenchian 方式对重型数据结构进行分组(意味着有很多属性和元素,因此不能使用任何级联解决方案)。因为我觉得大数据的串联会影响性能。这是我尝试过的示例输入数据、所需输出和 xsl:

#输入xml#

    <out:OuterSegment>
        <out:Segment >
             <out:from Code="CHN"/>
             <out:tothis Code="HYD"/>
             <out:group>0</out:group>
        </out:Segment>
        <out:Segment >
             <out:from Code="HYD"/>
             <out:tothis Code="BLR"/>
             <out:group>1</out:group>
        </out:Segment>
        <out:Segment >
             <out:from Code="BLR"/>
             <out:tothis Code="TVN"/>
             <out:group>2</out:group>
        </out:Segment>
        <out:Segment >
             <out:from Code="TVN"/>
             <out:tothis Code="DEL"/>
             <out:group>2</out:group>
        </out:Segment>
    </out:OuterSegment>

输出xml

    <out2:OuterSegment>
        <out2:Segment >
             <out2:from Code="CHN"/>
             <out2:tothis Code="HYD"/>
             <out2:group>0</out2:group>
        </out2:Segment>
    </out2:OuterSegment>    
    <out2:OuterSegment>
        <out2:Segment >
             <out2:from Code="HYD"/>
             <out2:tothis Code="BLR"/>
             <out2:group>1</out2:group>
        </out2:Segment>
    </out2:OuterSegment>
    <out2:OuterSegment>
        <out2:Segment >
             <out2:from Code="BLR"/>
             <out2:tothis Code="TVN"/>
             <out2:group>2</out2:group>
        </out2:Segment>
    </out2:OuterSegment>
    <out2:OuterSegment>
        <out2:Segment >
             <out2:from Code="TVN"/>
             <out2:tothis Code="DEL"/>
             <out2:group>2</out2:group>
        </out2:Segment>
    </out2:OuterSegment>

XSL sn-p

    <xsl:choose>
    <xsl:when test="position() = 1">
         <xsl:message> its the first row </xsl:message>
         <out:OuterSegment>            
                <xsl:apply-templates select="in2:IncomingSegment"
                                                    mode="localIncomingSegmentRefToSegment" />
            </out:OuterSegment>
    </xsl:when>
    <xsl:otherwise>
         <xsl:variable name="prevgroup" select="//in2:IncomingSegment[position() - 1]/@Group"/>
         <xsl:message> is the subsequent row</xsl:message>
         <xsl:if test="$prevgroup = //in2:IncomingSegment[$prevgroup]/@Group">
                <xsl:apply-templates select="in2:IncomingSegment"
                                                    mode="localIncomingSegmentRefToSegment" />                      
         </xsl:if>
         <xsl:if test="$prevgroup != //in2:IncomingSegment[$prevgroup]/@Group">
                <out:OuterSegment>     
                        <xsl:apply-templates select="in2:IncomingSegment"
                                                    mode="SegmentRefToSegment" />
                </out:OuterSegment>
         </xsl:if>
    </xsl:otherwise>
    </xsl:choose>

我需要提一下,上面的输入 xml 是由另一个 xslt 生成的,其输入为:

实际输入xml

    <in:RefSegment key="1" group="0">
        <in:inSegment >
             <in:from Code="CHN"/>
             <in:tothis Code="HYD"/>
        </in:inSegment>
    </in:RefSegment>
    <in:RefSegment key="2" group="1">
        <in:inSegment >
             <in:from Code="HYD"/>
             <in:tothis Code="BLR"/>
        </in:inSegment>
    </in:RefSegment>
    <in:RefSegment key="3" group="2">
        <in:inSegment >
             <in:from Code="BLR"/>
             <in:tothis Code="TVN"/>
        </in:inSegment>
    </in:RefSegment>
    <in:RefSegment key="4" group="2">
        <in:inSegment >
             <in:from Code="TVN"/>
             <in:tothis Code="BLR"/>
        </in:inSegment>
    </in:RefSegment>
    <in:DataSegments>
        <in:Data >
                    <in:keyref> 0 </in:keyref>
        </in:Data>
        <in:Data >
            <in:keyref> 1 </in:keyref>
        </in:Data>
        <in:Data >
                <in:keyref> 2 </in:keyref>
        </in:Data>
            <in:Data >
                <in:keyref> 2 </in:keyref>
        </in:Data>
  <in:DataSegments>

上述实际的input.xml是否可以一次性得到最终的output.xml?

【问题讨论】:

  • 如果您能解释一下转换背后的逻辑,将会有所帮助。我在结果中看不到任何分组:您从 4 个段开始并以相同的 4 个段结束,没有添加任何数据(AFAICS)。 -- 另请注意,您的两个输入都不是格式正确的:没有根元素,并且前缀未绑定到命名空间。

标签: xml xpath xslt-1.0 grouping hierarchical-data


【解决方案1】:

我不知道您如何处理有关 inout2 前缀的输入和输出 xml 的名称空间以及名称空间是否值得关注,所以这种方法可能没有用,但我想要分享这个想法。
这是基于假设所有 &lt;in:Data&gt; 节点位于与 &lt;in:RefSegment&gt; 对应的位置,正如您的实际输入 XML 所建议的那样 - 如果不是这种情况,请将其添加到您的问题中,因为有必要按 group 的值进行分组&lt;in:RefSegment&gt; 的属性和对应的&lt;in:keyref&gt; 值。

作为输入 XML sn-p 的调整,我将 &lt;in:RefSegment&gt; 节点包装在具有示例命名空间的根节点中:&lt;root xmlns:in="http://www.example.com"&gt;,并将示例命名空间 xmlns:in="http://www.example.com"xmlns:out2="http://www.example2.com" 添加到样式表声明中。由于xmlns:out2命名空间,这个命名空间被添加到每个out2:OuterSegment节点。

遵循 XSLT

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:in="http://www.example.com" xmlns:out2="http://www.example2.com"
  exclude-result-prefixes="in" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
  <xsl:strip-space elements="*"/>
    <xsl:template match="in:RefSegment">
    <xsl:variable name="position" select="position()"/>
     <out2:OuterSegment>
       <out2:Segment>
         <out2:from>
           <xsl:attribute name="Code">
             <xsl:value-of select="in:inSegment/in:from/@Code" />
           </xsl:attribute>
         </out2:from>
         <out2:tothis Code="HYD">
            <xsl:attribute name="Code">
              <xsl:value-of select="in:inSegment/in:tothis/@Code" />
            </xsl:attribute>
         </out2:tothis>
         <out2:group>
           <xsl:value-of select="normalize-space(//in:DataSegments/in:Data[$position]/in:keyref)"/>
        </out2:group>
      </out2:Segment>
    </out2:OuterSegment>
  </xsl:template>
  <xsl:template match="in:DataSegments"/>
</xsl:transform>

当应用到您的实际输入 XML 并调整缺少的关闭 &lt;/in:DataSegments&gt; 时,会产生以下结果:

<out2:OuterSegment xmlns:out="http://www.example2.com">
  <out2:Segment>
    <out2:from Code="CHN"/>
    <out2:tothis Code="HYD"/>
    <out2:group>0</out2:group>
  </out2:Segment>
</out2:OuterSegment>
<out2:OuterSegment xmlns:out="http://www.example2.com">
  <out2:Segment>
    <out2:from Code="HYD"/>
    <out2:tothis Code="BLR"/>
    <out2:group>1</out2:group>
  </out2:Segment>
</out2:OuterSegment>
<out2:OuterSegment xmlns:out="http://www.example2.com">
  <out2:Segment>
    <out2:from Code="BLR"/>
    <out2:tothis Code="TVN"/>
    <out2:group>2</out2:group>
  </out2:Segment>
</out:OuterSegment>
<out:OuterSegment xmlns:out="http://www.example2.com">
   <out2:Segment>
     <out2:from Code="TVN"/>
     <out2:tothis Code="BLR"/>
     <out2:group>2</out2:group>
   </out2:Segment>
</out2:OuterSegment>

匹配in:RefSegment 节点&lt;xsl:template match="in:RefSegment"&gt; 的模板生成&lt;out2:OuterSegment&gt; 节点。 &lt;xsl:variable name="position" select="position()"/&gt; 是当前 in:RefSegment 的位置。
从当前处理的in:RefSegment中检索生成的out2:fromout2:tothis节点的属性:

<out2:from>
   <xsl:attribute name="Code">
        <xsl:value-of select="in:inSegment/in:from/@Code" />
    </xsl:attribute>
</out2:from>

以及从位于同一位置的in:Data 节点检索到的out2:group 的值:

<out2:group>
  <xsl:value-of select="normalize-space(//in:DataSegments
                        /in:Data[$position/in:keyref)"/>
</out2:group>

使用额外的 normalize-space() 删除输入中的空格。
匹配in:DataSegments &lt;xsl:template match="in:DataSegments"/&gt; 的模板为空,因此从输出中删除了in:DataSegments 节点。

【讨论】:

  • 我应该提到所有自定义的,因为我的实际 xmls 有更多的属性和元素。 是同级元素,并非上下文中的所有元素都不是同一个父节点的子节点。
  • 使用 for-each-group 可以完成这项工作。没有意识到 xslt 1.0 支持向前兼容性,因为 for-each-group 是 xslt 2.0 的功能。谢谢马塔亚斯。
猜你喜欢
  • 2015-07-30
  • 2018-03-28
  • 1970-01-01
  • 2021-11-20
  • 2022-11-11
  • 1970-01-01
  • 2012-09-28
  • 2014-08-07
  • 2017-11-13
相关资源
最近更新 更多