如何使用XSL对XML文件的一部分进行排序

时间:2016-09-23 14:41:27

标签: xml sorting xslt

我正在尝试使用XSL对XML文件进行排序。我对XSL很陌生,并编写了一个.Net应用程序,将XSL应用于XML并将其保存到新文件中。这很好用。 XML的简化版本看起来像这样(精明的读者会注意到它是一个Management Studio项目文件):

<?xml version="1.0"?>
<SqlWorkbenchSqlProject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Name="PMISQL">
  <Items>
    <LogicalFolder Name="Connections" Type="2" Sorted="true">
      <Items>
        <ConnectionNode Name="cerberus:XPOOLE\barney.russ">
          <Created>2013-06-28T15:50:27.919788+01:00</Created>
          <Type>SQL</Type>
          <Server>cerberus</Server>
          <UserName />
          <Authentication>Windows Authentication</Authentication>
          <InitialDB>master</InitialDB>
          <LoginTimeout>15</LoginTimeout>
          <ExecutionTimeout>0</ExecutionTimeout>
          <ConnectionProtocol>NotSpecified</ConnectionProtocol>
          <ApplicationName>Microsoft SQL Server Management Studio - Query</ApplicationName>
        </ConnectionNode>
      </Items>
    </LogicalFolder>
    <LogicalFolder Name="Queries" Type="0" Sorted="true">
      <Items>
        <FileNode Name="PMI193_Documents.sql">
          <AssociatedConnectionMoniker>8c91a03d-f9b4-46c0-a305-b5dcc79ff907:tyro:True</AssociatedConnectionMoniker>
          <AssociatedConnSrvName>tyro</AssociatedConnSrvName>
          <AssociatedConnUserName />
          <FullPath>PMI193_Documents.sql</FullPath>
        </FileNode>
        <FileNode Name="PMI002_EventTypes.sql">
          <AssociatedConnectionMoniker>8c91a03d-f9b4-46c0-a305-b5dcc79ff907:cerberus:True</AssociatedConnectionMoniker>
          <AssociatedConnSrvName>cerberus</AssociatedConnSrvName>
          <AssociatedConnUserName />
          <FullPath>PMI002_EventTypes.sql</FullPath>
        </FileNode>
        <FileNode Name="PMI079a_DementiaScreeningDetail.sql">
          <AssociatedConnectionMoniker>8c91a03d-f9b4-46c0-a305-b5dcc79ff907:tyro:True</AssociatedConnectionMoniker>
          <AssociatedConnSrvName>tyro</AssociatedConnSrvName>
          <AssociatedConnUserName />
          <FullPath>PMI079a_DementiaScreeningDetail.sql</FullPath>
        </FileNode>
      </Items>
    </LogicalFolder>
    <LogicalFolder Name="Miscellaneous" Type="3" Sorted="true">
      <Items />
    </LogicalFolder>
  </Items>
  <SccProjectName>$/DataWarehouse/Dev/Reports</SccProjectName>
  <SccAuxPath />
  <SccLocalPath>..</SccLocalPath>
  <SccProvider>MSSCCI:Team Foundation Server MSSCCI Provider</SccProvider>
</SqlWorkbenchSqlProject>

您会注意到有几个LogicalFolder部分。我只想对属性Name =&#34; Queries&#34;我希望它按子元素Items / FileNode / FullPath排序。

我已经使用了一些XSL,它是身份XSL并对其进行了扩展。它会重现原始文件,但不会对我想要的部分进行排序(实际上它似乎没有排序)。我创建的XSL看起来像这样:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt">

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

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

    <xsl:template match="/SqlWorkbenchSqlPro/Items/LogicalFolder/Items">
        <xsl:apply-templates select=".">
            <xsl:sort select="FullPath"/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

我不知道如何指定我只对排序部分/ SqlWorkbenchSqlPro / Items / LogicalFolder / Items感兴趣,其中Name =&#34; Queries&#34;,如何指定哪个级别要排序的元素,以及如何指定用于对项目进行排序的元素。真的,我在XSL上并不是很擅长,因为我几个小时前才开始看它。

任何帮助非常感谢。我很头疼。

2 个答案:

答案 0 :(得分:0)

您在身份模板上构建的事实是一个良好的开端,所以您不远处。您遇到的第一个问题是您的模板引用SqlWorkbenchSqlPro,但在您的XML中它是SqlWorkbenchSqlProject,因此不匹配任何内容。

但是还没有去纠正它,因为如果你单独做到这一点,你的模板就会卡在一个循环中。使用<xsl:apply-templates select=".">只会选择相同的匹配模板。你应该在这里<xsl:apply-templates select="FileNode" />,或只是<xsl:apply-templates />

但是要回答您的主要问题,您可以在方括号中添加条件以定位您想要的节点

 <xsl:template match="LogicalFolder[@Name='Queries']/Items">

请注意,此处无需指定节点的完整路径。

试试这个XSLT

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt">

    <xsl:strip-space elements="*" />
    <xsl:output method="xml" indent="yes"/>

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

    <xsl:template match="LogicalFolder[@Name='Queries']/Items">
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:sort select="FullPath"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

请注意在xsl:copy模板中使用Items,以便在选择和排序其子节点之前复制现有的Items元素。

答案 1 :(得分:0)

  

如何指定我只对排序LogicalFolder/Items Name="Queries"

部分感兴趣

这很容易。制作模板,使其仅匹配此特定类型的节点。

<xsl:template match="LogicalFolder[@Name = 'Queries']/Items">
    <xsl:copy>
        <xsl:apply-templates select="*">
            <xsl:sort select="FullPath" data-type="text" />
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

这会复制<Items>元素。之后,您不再需要将模板应用于.(当前节点,即<Items>),而是*(所有子元素,即<FileNode>

  

如何指定要排序的元素级别,

这也很容易。订购适用于所选节点。在上面的例子中,选择了所有子元素。这意味着排序仅限于LogicalFolder/Items下的元素的顶级。

假设可以嵌套<Items>并且您想要递归排序,您需要做的就是更改模板以应用于{{>所有节点<Items>LogicalFolder[@Name = 'Queries'] 1}}通过更改match表达式,如下所示:

<xsl:template match="Items[ancestor::LogicalFolder[@Name = 'Queries']]">

不需要进行其他更改,XSLT处理器会为您执行递归操作。 (<xsl:apply-templates>是递归步骤。)

  

以及如何指定用于对项目进行排序的元素

<xsl:sort>中的select表达式始终与正在排序的节点相关。在上面,

<xsl:apply-templates select="*">

选择一堆<FileNode>元素,这意味着

<xsl:sort select="FullPath" data-type="text" />

是对的。 (data-type添加了,因为这是一种很好的做法,不是因为它是绝对必要的。text是默认的。)