XSLT按优先级在相同节点之间进行选择

时间:2018-06-13 12:45:19

标签: xml xslt xpath

我一直在努力根据优先级从相同节点的数组中选择正确的节点。

这是XML:

<?xml version="1.0" encoding="UTF-8"?>
  <LandXML>
    <CgPoints>
      <CgPoint name="name1" oID="id1"></CgPoint>
      <CgPoint name="name2" oID="id2"></CgPoint>
      <CgPoint name="name3" oID="id1"></CgPoint>
      <CgPoint name="name4" oID="id1"></CgPoint>
      <CgPoint name="name5" oID="id2"></CgPoint>
      <CgPoint name="name6" oID="id3"></CgPoint>
    </CgPoints>
    <Points>
      <Point uniqueID="name1" class="medium" text="text1"></Point>
      <Point uniqueID="name2" class="medium" text="text2"></Point>
      <Point uniqueID="name3" class="high" text="text3"></Point>
      <Point uniqueID="name4" class="low" text="text4"></Point>
      <Point uniqueID="name5" class="low" text="text5"></Point>
      <Point uniqueID="name6" class="medium" text="text6"></Point>
    </Points>
  </LandXML>

我想要做的是浏览CgPoints元素并从节点获取所有oID名称,当有相同的oID名称时,只选择一个class元素中的最高Points属性。之后,我想将text属性添加到提取的oID名称。

输出如下:

id1,text3, <!-- this is the node with `name` attribute value `name3` -->
id2,text2,
id3,text6, <!-- this is the node with `name` attribute value `name2` -->

这是迄今为止我提出的xslt:

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

<xsl:output method="text" indent="no" encoding="utf-8"/> 
<xsl:variable name="fileExt" select="'txt'"/>

<!-- Comma separator -->
<xsl:variable name="separator" select="','"/>
<xsl:key name="keyHPoint" match="/LandXML/Points/Point" use="@uniqueID"/>

<xsl:template match="/">
  <xsl:apply-templates select="/LandXML/CgPoints/CgPoint">
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="/LandXML/CgPoints/CgPoint">
  <xsl:variable name="name" select="./@name"/>
  <xsl:variable name="id" select="./@oID"/>

  <xsl:variable name="allpoints" select="/LandXML/CgPoints/CgPoint[@oID = $id]"/>
  <xsl:variable name="Point1" select="key('keyHPoint', $allpoints/@oID)"/>

  <xsl:choose>
        <xsl:when test="count($allpoints) = 1">
            <xsl:value-of select="concat($id, $separator)"/>
            <xsl:apply-templates select="/LandXML/Points/Point[@uniqueID = $name]"/>
            <xsl:call-template name="newline">
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:choose>
                <xsl:when test="$Point1[@class = 'high']">
                    <xsl:value-of select="concat($id, $separator)"/>
                    <xsl:apply-templates select="/LandXML/Points/Point[@uniqueID = $name]"/>
                    <xsl:call-template name="newline">
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:choose>
                        <xsl:when test="$Point1[@class = 'medium']">
                            <xsl:value-of select="concat($id, $separator)"/>
                            <xsl:apply-templates select="/LandXML/Points/Point[@uniqueID = $numurs]"/>
                            <xsl:call-template name="newline">
                            </xsl:call-template>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:choose>
                                <xsl:when test="$Point1[@class = 'low']">
                                    <xsl:value-of select="concat($id, $separator)"/>
                                    <xsl:apply-templates select="/LandXML/Points/h:Point[@uniqueID = $name]"/>
                                    <xsl:call-template name="newline">
                                    </xsl:call-template>
                                </xsl:when>
                                <xsl:otherwise>

                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<!-- Text template -->
<xsl:template match="/LandXML/Points/Point">
  <xsl:value-of select="concat(./@text, $separator)"/>
</xsl:template>

<!-- New line -->
<xsl:template name="newline">
  <xsl:text>&#xa;</xsl:text>
</xsl:template>

</xsl:stylesheet>

使用此xslt,我获得所有节点的输出,不应用优先级选择。 在此先感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

使用XSLT 2或3,您可以按oID属性进行分组,按引用的类对每个组进行排序(例如,通过在一系列可能的值中取index-of)然后输出首先按排序顺序(https://xsltfiddle.liberty-development.net/nc4NzQj/1):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="3.0">

  <xsl:param name="class-order" as="xs:string*" select="'high', 'medium', 'low'"/>

  <xsl:output method="text"/>

  <xsl:key name="pref" match="Point" use="@uniqueID"/>

  <xsl:template match="/">
    <xsl:for-each-group select="LandXML/CgPoints/CgPoint" group-by="@oID">
        <xsl:for-each select="current-group()">
            <xsl:sort select="index-of($class-order, key('pref', @name)/@class)"/>
            <xsl:if test="position() eq 1">
                <xsl:value-of select="current-grouping-key(), key('pref', @name)/@text" separator=","/>
                <xsl:text>&#10;</xsl:text>
            </xsl:if>
        </xsl:for-each>
    </xsl:for-each-group>
  </xsl:template>

</xsl:stylesheet>

使用XSLT 1,您可以以某种方式使用相同的算法,只有分组必须使用密钥和Muenchian分组以及排序,例如通过获取剩余类值的串联的字符串长度({{3} }):

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

  <xsl:param name="class-order" select="'high|medium|low'"/>

  <xsl:output method="text"/>

  <xsl:key name="point-group" match="CgPoint" use="@oID"/>

  <xsl:key name="pref" match="Point" use="@uniqueID"/>

  <xsl:template match="/">
    <xsl:for-each select="LandXML/CgPoints/CgPoint[generate-id() = generate-id(key('point-group', @oID)[1])]">
        <xsl:for-each select="key('point-group', @oID)">
            <xsl:sort select="string-length(substring-after($class-order, key('pref', @name)/@class))" order="descending"/>
            <xsl:if test="position() = 1">
                <xsl:value-of select="concat(@oID, ',', key('pref', @name)/@text, '&#10;')"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>