使用基于外部doc中指定的基于XSLT的顺序对XML元素顺序进行排序

时间:2010-12-16 11:00:53

标签: function xslt sorting document xslt-2.0

平台: Saxon 9 - XSLT 2.0

我有3000个xml文档,需要定期编辑,更新和保存。

部分过程涉及在编辑之前从存储库中签出文档,并在编辑完成后定期发布文档。

每个文档都包含一系列单独命名的部分,例如

   <part>
        <meta>
            <place_id>12345</place_id>
            <place_name>London</place_name>
            <country_id>GB</country_id>
            <country_name>United Kingdom</country_name>
        </meta>
        <text>
            <docs>some blurb</docs>
            <airport>some blurb LGW LHR</airport>
            <trains>some blurb</trains>
            <hotels>some blurb</hotels>
            <health>some blurb</health>
            <attractions>some blurb</attractions>
        </text>
   </part>

在文本元素中有近100个部分,与所有编辑团队一样,他们偶尔也会定期改变他们对首选订单的看法。也许每年两次。

目前,我们将XML文档部分提供给当前优先顺序的编辑进行编辑和发布。此顺序在动态生成的名为“stdhdg.xml”的外部文档中指定,并显示如下:

<hdgs>
    <hdg name="docs" newsort="10"/>
    <hdg name="airport" newsort="30"/>
    <hdg name="trains" newsort="20"/>
    <hdg name="hotels" newsort="40"/>
    <hdg name="health" newsort="60"/>
    <hdg name="attractions" newsort="50"/>
</hdgs>

首选排序顺序由hdg / @newport指定。

所以我使用这样的模板以正确的顺序处理

<xsl:template match="text">
    <xsl:variable name="thetext" select="."/>
<xsl:variable name="stdhead" select="document('stdhdg.xml')"/>
    <text>
        <xsl:for-each select="$stdhead//hdg">
            <xsl:sort data-type="number" order="ascending" select="@newsort"/>
            <xsl:variable name="tagname" select="@name"/>
            <xsl:variable name="thisnode" select="$thetext/*[local-name() = $tagname]"/>
            <xsl:apply-templates select="$thisnode"/>
        </xsl:for-each>
    </text>
</xsl:template>

但它看起来非常缓慢而且很麻烦,我觉得我应该用钥匙来加速它。

是否有更简单/更简洁的方法来执行此排序操作。

(请不要让我改变编辑的编辑方式。这不仅仅是我一生的价值)

TIA

Feargal

2 个答案:

答案 0 :(得分:0)

是的,密钥应该加速这样的查找。这是一个大纲:

<xsl:stylesheet ...>

  <xsl:key name="k1" match="text/*" use="local-name()"/>

  <xsl:variable name="stdhead" select="document('stdhdg.xml')"/>

  ...

<xsl:template match="text">
    <xsl:variable name="thetext" select="."/>
    <text>
        <xsl:for-each select="$stdhead//hdg">
            <xsl:sort data-type="number" order="ascending" select="@newsort"/>
            <xsl:apply-templates select="key('k1', @name, $thetext)"/>
        </xsl:for-each>
    </text>
</xsl:template>

</xsl:stylesheet>

所有内容都直接在浏览器中输入,因此请将其作为如何处理它的大纲,而不是在经过测试的代码中。

[edit]作为第二个想法,我认为每次处理text元素时排序都是浪费,所以你可以改为

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

  <xsl:key name="k1" match="text/*" use="local-name()"/>

  <xsl:variable name="stdhead" select="document('stdhdg.xml')"/>

  <xsl:variable name="sorted-headers" as="element(hdg)*">
    <xsl:perform-sort select="$stdhead//hdg">
      <xsl:sort select="@newsort" data-type="number"/>
    </xsl:perform-sort>
  </xsl:variable>

<xsl:template match="text">
    <xsl:variable name="thetext" select="."/>
    <text>
        <xsl:for-each select="$sorted-headers">
            <xsl:apply-templates select="key('k1', @name, $thetext)"/>
        </xsl:for-each>
    </text>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:0)

  

在文本元素中有   近100个部分,与所有部分一样   编辑团队,他们改变他们的   关注的首选订单   偶尔,但经常,基础。也许   每年两次。

     

。 。 。 。 。 。

     

但似乎非常   缓慢而笨重,我觉得我   应该用钥匙来加快速度

每次提交文档进行编辑时对文档进行排序是错误的方法

最佳解决方案是在更改“stdhdg.xml”文档时对其进行排序并保存每年仅排序2次

如果'stdhdg.xml'中的更改无法在组织上很好地同步,那么您可以使用重复(比如说每日)作业运行以下转换:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="vHeaderLoc" select="'file:///C:/temp/deleteMe/stdhdg.xml'"/>

 <xsl:variable name="vHeaderDoc" select=
 "document($vHeaderLoc)"/>

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

 <xsl:template match=
   "part/@hash
          [not(.
              = 
               string(document('file:///C:/temp/deleteMe/stdhdg.xml'))
              )
          ]">
  <xsl:attribute name="hash">
   <xsl:value-of select="string($vHeaderDoc)"/>
  </xsl:attribute>
 </xsl:template>

 <xsl:template match=
   "/*/text[not(/*/@hash
                = string(document('file:///C:/temp/deleteMe/stdhdg.xml'))
                )
            ]">
  <text>
   <xsl:apply-templates select="*">
    <xsl:sort data-type="number"
     select="$vHeaderDoc/*/hdg[@name=name(current())]"/>
   </xsl:apply-templates>
  </text>
 </xsl:template>
</xsl:stylesheet>

当主要内容XML文档是(注意顶部元素现在具有hash属性)时:

<part hash="010203040506">
    <meta>
        <place_id>12345</place_id>
        <place_name>London</place_name>
        <country_id>GB</country_id>
        <country_name>United Kingdom</country_name>
    </meta>
    <text>
        <docs>some blurb</docs>
        <airport>some blurb LGW LHR</airport>
        <trains>some blurb</trains>
        <hotels>some blurb</hotels>
        <health>some blurb</health>
        <attractions>some blurb</attractions>
    </text>
</part>

,stdhdg.xml文件为

<hdgs>
    <hdg name="docs">10</hdg>
    <hdg name="airport">30</hdg>
    <hdg name="trains">20</hdg>
    <hdg name="hotels">40</hdg>
    <hdg name="health">60</hdg>
    <hdg name="attractions">50</hdg>
</hdgs>

然后上面的转换会生成一个新排序的主内容,其中包含最新的哈希

<part hash="103020406050">
   <meta>
      <place_id>12345</place_id>
      <place_name>London</place_name>
      <country_id>GB</country_id>
      <country_name>United Kingdom</country_name>
   </meta>
   <text>
      <docs>some blurb</docs>
      <trains>some blurb</trains>
      <airport>some blurb LGW LHR</airport>
      <hotels>some blurb</hotels>
      <attractions>some blurb</attractions>
      <health>some blurb</health>
   </text>
</part>

请注意

  1. 主内容文档的顶部元素现在具有hash属性,其值是驻留在stdhdg.xml文档中的排序键的串联。

  2. stdhdg.xml文件的格式也略有改变,因此可以轻松地将键的串联生成为文档的字符串值。

  3. 如果主要内容中保存的哈希值与stdhdg.xml中的sort-keys-concatenation相同,则每日运行转换是身份转换。

  4. 如果旧散列与stdhdg.xml中的排序键不匹配,则会更新为新散列并重新排序这些节。