2010-12-16 72 views
1

平台:撒克逊9 - XSLT 2.0排序XML元素顺序XSLT基于在外部文档指定的顺序

我有需要定期编辑,更新和保存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> 

,其中,通过热浸镀锌/ @ newsort规定的优选排序次序。

于是我就用一个模板像这样以正确的顺序

<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

+0

好问题,+1。请参阅我的答案,以获得完全消除每次编辑之前重新排序的解决方案。 :) – 2010-12-16 15:43:21

回答

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> 

所有直接在浏览器中输入,所以把它作为大纲如何接近它,而不是在测试代码。

[编辑]作为第二个想法,我觉得每次处理一个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> 
+0

+1对于'xsl:perform-sort'示例。我怀疑即使对于大型文档这个关键用法(选择上下文节点子节点),它也会加快进程的速度,这会使您忘记内存成本。 – 2010-12-16 12:58:22

+0

@Alejandro:你可能有兴趣阅读我的答案,伙计们。 :) – 2010-12-16 15:44:13

0

在文本元素有 近100节,并且与所有 编辑团队一样,他们偶尔会根据首选订单 更改 。也许 每年两次。

。 。 。 。 。 。

但它似乎很 缓慢和繁琐,我觉得我 应该使用键来加快它

当它被提出来编辑每次排序的文件是错误的做法

最好的解决方案是对它进行排序并保存,每年仅排序2次当'stdhdg.xml'文档发生更改时。

如果“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中的排序键不匹配,那么它将更新为新散列,并且节将被重新排序。