如何在 VB.NET (VS2008) 中使用 XSLT 指定自定义属性排序顺序

How to specify a custom attribute sort order using XSLT in VB.NET (VS2008)

我有这样的架构:

XML:

<Shipment> <Destination City='New York'> <Delivery> <Product ProductCode='B' ProductType='THI'></Product> <Product ProductCode='U' ProductType='SIS'></Product> <Product ProductCode='R' ProductType='JUS'></Product> <Product ProductCode='G' ProductType='TMA'></Product> <Product ProductCode='E' ProductType='DEU'></Product> <Product ProductCode='R' ProductType='POK'></Product> </Delivery> </Destination> <Destination City='London'> <Delivery> <Product ProductCode='C' ProductType='MAK'></Product> <Product ProductCode='H' ProductType='ESN'></Product> <Product ProductCode='E' ProductType='OSE'></Product> <Product ProductCode='R' ProductType='NSE'></Product> <Product ProductCode='R' ProductType='ATA'></Product> <Product ProductCode='Y' ProductType='LLL'></Product> </Delivery> </Destination> <Destination City='Paris'> <Delivery> <Product ProductCode='B' ProductType='WHO'></Product> <Product ProductCode='A' ProductType='WAT'></Product> <Product ProductCode='G' ProductType='CHE'></Product> <Product ProductCode='E' ProductType='STH'></Product> <Product ProductCode='L' ProductType='WAT'></Product> <Product ProductCode=' ' ProductType='CHM'></Product> </Delivery> </Destination> <Destination City='Munich'> <Delivery> <Product ProductCode='Q' ProductType='ENN'></Product> <Product ProductCode='U' ProductType='THE'></Product> <Product ProductCode='I' ProductType='SHA'></Product> <Product ProductCode='C' ProductType='DOW'></Product> <Product ProductCode='H' ProductType='KNO'></Product> <Product ProductCode='E' ProductType='WSS'></Product> </Delivery> </Destination> </Shipment>  

而且我需要它对每次交付中的产品代码进行排序,因此输出是:

XML:

<Shipment> <Destination City='New York'> <Delivery> <Product ProductCode='R' ProductType='JUS'></Product> <Product ProductCode='R' ProductType='POK'></Product> <Product ProductCode='B' ProductType='THI'></Product> <Product ProductCode='U' ProductType='SIS'></Product> <Product ProductCode='E' ProductType='DEU'></Product> <Product ProductCode='G' ProductType='TMA'></Product> </Delivery> </Destination> <Destination City='London'> <Delivery> <Product ProductCode='R' ProductType='NSE'></Product> <Product ProductCode='R' ProductType='ATA'></Product> <Product ProductCode='C' ProductType='MAK'></Product> <Product ProductCode='H' ProductType='ESN'></Product> <Product ProductCode='E' ProductType='OSE'></Product> <Product ProductCode='Y' ProductType='LLL'></Product> </Delivery> </Destination> <Destination City='Paris'> <Delivery> <Product ProductCode='B' ProductType='WHO'></Product> <Product ProductCode='A' ProductType='WAT'></Product> <Product ProductCode='E' ProductType='STH'></Product> <Product ProductCode='L' ProductType='WAT'></Product> <Product ProductCode=' ' ProductType='CHM'></Product> <Product ProductCode='G' ProductType='CHE'></Product> </Delivery> </Destination> <Destination City='Munich'> <Delivery> <Product ProductCode='Q' ProductType='ENN'></Product> <Product ProductCode='U' ProductType='THE'></Product> <Product ProductCode='I' ProductType='SHA'></Product> <Product ProductCode='C' ProductType='DOW'></Product> <Product ProductCode='H' ProductType='KNO'></Product> <Product ProductCode='E' ProductType='WSS'></Product> </Delivery> </Destination> </Shipment>  

产品代码是字母或空白。以下规则适用:

  1. 产品代码 R 应该首先出现
  2. 产品代码 G 应该出现在最后
  3. 两者之间还有什么

我以前从来没有在愤怒中使用过 XSLT,而且我已经有很长时间没有使用过任何 VB.NET 所以为了让某些东西正常工作,我想出了以下方法。

Module Module1

    Sub Main()

        ' The document
        Dim document As String = "<Shipment> <Destination City='New York'> <Delivery> <Product ProductCode='B' ProductType='THI'></Product> <Product ProductCode='U' ProductType='SIS'></Product> <Product ProductCode='R' ProductType='JUS'></Product> <Product ProductCode='G' ProductType='TMA'></Product> <Product ProductCode='E' ProductType='DEU'></Product> <Product ProductCode='R' ProductType='POK'></Product> </Delivery> </Destination> <Destination City='London'> <Delivery> <Product ProductCode='C' ProductType='MAK'></Product> <Product ProductCode='H' ProductType='ESN'></Product> <Product ProductCode='E' ProductType='OSE'></Product> <Product ProductCode='R' ProductType='NSE'></Product> <Product ProductCode='R' ProductType='ATA'></Product> <Product ProductCode='Y' ProductType='LLL'></Product> </Delivery> </Destination> <Destination City='Paris'> <Delivery> <Product ProductCode='B' ProductType='WHO'></Product> <Product ProductCode='A' ProductType='WAT'></Product> <Product ProductCode='G' ProductType='CHE'></Product> <Product ProductCode='E' ProductType='STH'></Product> <Product ProductCode='L' ProductType='WAT'></Product> <Product ProductCode='S' ProductType='CHM'></Product> </Delivery> </Destination> <Destination City='Munich'> <Delivery> <Product ProductCode='Q' ProductType='ENN'></Product> <Product ProductCode='U' ProductType='THE'></Product> <Product ProductCode='I' ProductType='SHA'></Product> <Product ProductCode='C' ProductType='DOW'></Product> <Product ProductCode='H' ProductType='KNO'></Product> <Product ProductCode='E' ProductType='WSS'></Product> </Delivery> </Destination> </Shipment>  "

        ' Load it
        Dim xDoc As XmlDocument = New XmlDocument()
        xDoc.LoadXml(DirtyHack(document))

        ' Required for string output
        Dim sb As StringBuilder = New StringBuilder()
        Dim writer As XmlWriter = XmlWriter.Create(sb)

        ' Do the transformation
        Dim tranny As XslCompiledTransform = New XslCompiledTransform()
        tranny.Load("c:\sandbox\MassageXML\Autobots.xslt")
        tranny.Transform(xDoc, writer)

        ' See what mess we've made
        Debug.WriteLine(sb.ToString)

    End Sub

    Function DirtyHack(ByVal inputString As String) As String

        Dim dict = New Dictionary(Of String, String)

        dict.Add("ProductCode='", "SortOrder='2' ProductCode='")
        dict.Add("SortOrder='2' ProductCode='R'", "SortOrder='1' ProductCode='R'")
        dict.Add("SortOrder='2' ProductCode='G'", "SortOrder='3' ProductCode='G'")

        For Each kvp As KeyValuePair(Of String, String) In dict
            inputString = inputString.Replace(kvp.Key, kvp.Value)
        Next

        Return inputString

    End Function

End Module

本质上,我即时添加了另一个属性,按此排序然后将其删除,但我确信必须有更简洁的方法。有什么想法吗?

这是目前为止的 XSLT 脚本:

<?xml version="1.0" encoding="utf-8"?>
<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:template match="@SortOrder" />

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

  <xsl:template match="Delivery">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="*">
        <xsl:sort select="@SortOrder" data-type="number" order="ascending"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

这是我的建议,它将产品代码到数字排序值的映射定义为参数,并在 xsl:sort 中选择它,唯一的缺点是在 XSLT 1.0 中你需要 exsl:node-setmsxsl:node-set将参数所在的结果树片段转换成节点集即可使用如图:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:exsl="http://exslt.org/common"
    exclude-result-prefixes="msxsl exsl"
>

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

  <xsl:param name="sort-map-rtf">
      <map pc="R" sort="1"/>
      <map pc="G" sort="3"/>
      <map pc="*" sort="2"/>
  </xsl:param>

  <xsl:variable name="sort-map" select="exsl:node-set($sort-map-rtf)/map"/>


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

  <xsl:template match="Delivery">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="*">
        <xsl:sort select="($sort-map[@pc = current()/@ProductCode] | $sort-map[@pc = '*'])[1]/@sort" data-type="number" order="ascending"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

在线示例 http://xsltransform.net/3NJ38ZM