在xslt中过滤和分组xml数据

时间:2011-07-22 20:39:52

标签: c# xml xslt

我有一个xml内容,我正在应用xslt 1.0进行转换。我也传递过滤参数。但我无法对xslt 1.0中的过滤数据进行分组。

我会将“国家价值”(与“美国”一样)作为过滤参数。过滤后,分组将应用于“组”字段以过滤数据。如果只存在一个组,则不要对数据进行分组。仅当多于一个组成为可能时才应用分组。

请帮助我。

提前感谢。

以下是我的示例XML内容。

<?xml version="1.0" encoding="utf-8" ?>
<DataRows>
  -<DataRow>
    - <Country>
      <Conty>United States</Conty>
      <Conty>United Kingdom</Conty>
    </Country>
    <Group>Group 1</Group>
    <Order>1</Order>
    <Name>Name 1_1</Name>
    <Title>Title 1</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>United States</Conty>
      <Conty>United Kingdom</Conty>
    </Country>
    <Group>Group 1</Group>
    <Order>2</Order>
    <Name>Name 2_2</Name>
    <Title>Title 2</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>United States</Conty>
    </Country>
    <Group>Group 1</Group>
    <Order>1</Order>
    <Name>Name 3_1</Name>
    <Title>Title 3</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>United States</Conty>
      <Conty>Germany</Conty>
    </Country>
    <Group>Group 1</Group>
    <Order>2</Order>
    <Name>Name 4_2</Name>
    <Title>Title 4</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>United States</Conty>
    </Country>
    <Group>Group 2</Group>
    <Order>4</Order>
    <Name>Name 8_4</Name>
    <Title>Title 8</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>United Kingdom</Conty>
    </Country>
    <Group>Group 2</Group>
    <Order>1</Order>
    <Name>Name 9_1</Name>
    <Title>Title 9</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>United States</Conty>
      <Conty>Germany</Conty>
    </Country>
    <Group>Group 2</Group>
    <Order>3</Order>
    <Name>Name 5_3</Name>
    <Title>Title 5</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>United States</Conty>
      <Conty>Germany</Conty>
    </Country>
    <Group>Group 2</Group>
    <Order>4</Order>
    <Name>Name 6_4</Name>
    <Title>Title 6</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>United States</Conty>
    </Country>
    <Group>Group 2</Group>
    <Order>3</Order>
    <Name>Name 7_3</Name>
    <Title>Title 7</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
  -<DataRow>
    - <Country>
      <Conty>Germany</Conty>
    </Country>
    <Group>Group 1</Group>
    <Order>1</Order>
    <Name>Name 10_1</Name>
    <Title>Title 10</Title>
    <PhoneNo>732-989-9898</PhoneNo>
    <ImageUrl />
    <EmailId />
  </DataRow>
</DataRows>

2 个答案:

答案 0 :(得分:0)

除非您进行了两阶段转换,否则我认为您应该首先进行分组,然后进行过滤。

通过共同的Meunchain分组方法实现分组。您首先要定义一个键,以便根据

来查找 DataRow 元素
<xsl:key name="RowLookup" match="DataRow" use="Group"/>

然后,要获取唯一的组名,请匹配DataRow元素,这些元素恰好是您的特定组中键中的第一个出现元素

<xsl:apply-templates select="DataRow[generate-id() = generate-id(key('RowLookup', Group)[1])]"/>

因此,现在您已按元素进行分组,因此您需要检查当前组中与过滤器匹配的至少一个DataRow元素

<xsl:if test="../DataRow[Group=current()/Group]/Country[Conty=$Conty]">

然后要获取当前组的所有DataRow元素,您可以使用密钥

<xsl:apply-templates select="key('RowLookup', Group)" mode="ingroup"/>

这是完整的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:param name="Conty">United Kingdom</xsl:param>

   <xsl:key name="RowLookup" match="DataRow" use="Group"/>

   <xsl:template match="/DataRows">
      <xsl:copy>
         <!-- Select unique groups -->
         <xsl:apply-templates 
            select="DataRow[generate-id() = generate-id(key('RowLookup', Group)[1])]"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="DataRow">
      <!-- Check any DataRow elements for the current group match the filter -->
      <xsl:if test="../DataRow[Group=current()/Group]/Country[Conty=$Conty]">
         <Group>
            <xsl:attribute name="name">
               <xsl:value-of select="Group"/>
            </xsl:attribute>
            <!-- Get all the DataRow elements for the current group -->
            <xsl:apply-templates select="key('RowLookup', Group)" mode="ingroup"/>
         </Group>
      </xsl:if>
   </xsl:template>

   <xsl:template match="DataRow" mode="ingroup">
      <!-- Check this DataRow matches the filter -->
      <xsl:if test="Country[Conty=$Conty]">
         <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
         </xsl:copy>
      </xsl:if>
   </xsl:template>

   <!-- Ignore Group and Country elements -->
   <xsl:template match="Group|Country"/>

   <!-- Standard Identity Transform for all other nodes -->
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

将此XSLT应用于示例XML时,您将获得以下结果

<DataRows>
   <Group name="Group 1">
      <DataRow>
         <Order>1</Order>
         <Name>Name 1_1</Name>
         <Title>Title 1</Title>
         <PhoneNo>732-989-9898</PhoneNo>
         <ImageUrl/>
         <EmailId/>
      </DataRow>
      <DataRow>
         <Order>2</Order>
         <Name>Name 2_2</Name>
         <Title>Title 2</Title>
         <PhoneNo>732-989-9898</PhoneNo>
         <ImageUrl/>
         <EmailId/>
      </DataRow>
   </Group>
   <Group name="Group 2">
      <DataRow>
         <Order>1</Order>
         <Name>Name 9_1</Name>
         <Title>Title 9</Title>
         <PhoneNo>732-989-9898</PhoneNo>
         <ImageUrl/>
         <EmailId/>
      </DataRow>
   </Group>
</DataRows>

我不确定这是否是您想要的确切结构,但我希望它能为您提供一般性的想法。

答案 1 :(得分:0)

如果您真的想先进行过滤,然后进行分组,那么您正在寻找某种“双通”变换。这可以通过使用节点集扩展函数来实现,以创建包含过滤数据的结果树片段。

在下面的示例中,我使用的是Microsoft的扩展功能,但根据您的平台,您可能需要指定另一个。 (EXSLT是另一个常见的。使用名称空间http://exslt.org/common。)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
   <xsl:output method="xml" indent="yes"/>

   <xsl:param name="Conty">United Kingdom</xsl:param>

   <xsl:variable name="FilteredData">
      <xsl:apply-templates select="/DataRows/DataRow" mode="filter"/>
   </xsl:variable>

   <xsl:template match="DataRow" mode="filter">
      <!-- Check this DataRow matches the filter -->
      <xsl:if test="Country[Conty=$Conty]">
         <xsl:copy>
            <xsl:apply-templates select="@*|node()" mode="filter"/>
         </xsl:copy>
      </xsl:if>
   </xsl:template>

   <!-- Ignore Country node in the filter -->
   <xsl:template match="Country" mode="filter"/>

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

   <xsl:template match="/DataRows">
      <xsl:copy>
         <!-- Read the filtered data -->
         <xsl:choose>
            <!-- Check there is a Group which differs from the first group -->
            <xsl:when test="msxsl:node-set($FilteredData)/DataRow[position() > 1][Group != msxsl:node-set($FilteredData)/DataRow[1]/Group]">
               <xsl:apply-templates select="msxsl:node-set($FilteredData)"/>
            </xsl:when>
            <xsl:otherwise>
               <xsl:apply-templates select="msxsl:node-set($FilteredData)" mode="nogroup"/>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:copy>
   </xsl:template>

   <!-- Filtered data row -->
   <xsl:template match="DataRow">
      <!-- Is this DataRow the first in the group -->
      <xsl:if test="not(preceding-sibling::DataRow[Group=current()/Group])"><!-- If so, create the group node -->
         <Group>
            <xsl:attribute name="name">
               <xsl:value-of select="Group"/>
            </xsl:attribute><!-- Get all the DataRow elements from the filter for the current group -->
            <xsl:apply-templates select="../DataRow[Group=current()/Group]" mode="ingroup"/>
         </Group>
      </xsl:if>
   </xsl:template>

   <!-- Identity template for the group -->
   <xsl:template match="@*|node()" mode="ingroup">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()" mode="ingroup"/>
      </xsl:copy>
   </xsl:template>

   <!-- Ignore Group and Country node in the grouping -->
   <xsl:template match="Group|Country" mode="ingroup"/>

   <!-- Identity template for no grouping -->
   <xsl:template match="@*|node()" mode="nogroup">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()" mode="nogroup"/>
      </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

使用此选项时,输出应如下

<DataRows>
    <Group name="Group 1">
        <DataRow>
            <Order>1</Order>
            <Name>Name 1_1</Name>
            <Title>Title 1</Title>
            <PhoneNo>732-989-9898</PhoneNo>
            <ImageUrl/>
            <EmailId/>
        </DataRow>
        <DataRow>
            <Order>2</Order>
            <Name>Name 2_2</Name>
            <Title>Title 2</Title>
            <PhoneNo>732-989-9898</PhoneNo>
            <ImageUrl/>
            <EmailId/>
        </DataRow>
    </Group>
    <Group name="Group 2">
        <DataRow>
            <Order>1</Order>
            <Name>Name 9_1</Name>
            <Title>Title 9</Title>
            <PhoneNo>732-989-9898</PhoneNo>
            <ImageUrl/>
            <EmailId/>
        </DataRow>
    </Group>
</DataRows>

有关详细信息,请参阅Understanding the node-set function