2011-12-13 102 views
5

对于使用XSL 1.0进行排序(只有1.0 - 我使用.Net解析器),我有一个非常特殊的问题。按ID排序,然后按同一节点中的时间戳排序

这里是我的xml:

<Root> 
.... 
<PatientsPN> 
     <Patient> 
      <ID>1</ID> 
      <TimeStamp>20111208165819</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Fanny</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>4</ID> 
      <TimeStamp>20111208165910</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Fanny4</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>4</ID> 
      <TimeStamp>20111208165902</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>FannyMOI</PrenomPatient> 
      <Sexe>M</Sexe> 
     </Patient> 
     <Patient> 
      <ID>2</ID> 
      <TimeStamp>20111208170000</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>FannyMOI</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>2</ID> 
      <TimeStamp>20111208165819</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Fanny</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>2</ID> 
      <TimeStamp>20111208170050</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Cmoi2</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>3</ID> 
      <TimeStamp>20111208165829</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Jesuis3</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
    </PatientsPN> 
... 
</Root> 

我想通过ID排序首先我PatientsNP然后取每个ID的更高的时间戳。 我的输出:

<Root> 
<PatientsPN> 
<Patient> 
      <ID>1</ID> 
      <TimeStamp>20111208165819</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Fanny</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
<Patient> 
      <ID>2</ID> 
      <TimeStamp>20111208170050</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Cmoi2</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
<Patient> 
      <ID>3</ID> 
      <TimeStamp>20111208165829</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Jesuis3</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
<Patient> 
      <ID>4</ID> 
      <TimeStamp>20111208165910</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Fanny4</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
</PatientsPN> 
</Root> 

首先,我试图排序ID名单,然后通过每个节点的解析和使用XPath提取更高的时间戳,但没有奏效。它不断重复其他节点。

也尝试Muench排序方法,但我不能让它与更通用的东西正常工作。

我的XSL是:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:param name="mark">PN</xsl:param> 
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/> 

    <xsl:template match="/"> 
     <Root> 
      <xsl:apply-templates/> 
     </Root> 
    </xsl:template> 

    <xsl:template match="/Root/*"> 
     <xsl:for-each select="."> 
      <xsl:choose> 
       <xsl:when test="substring(name(), (string-length(name()) - string-length($mark)) + 1) = $mark"> 
        <!-- Search for an ID tag --> 
        <xsl:copy> 
         <xsl:if test="node()/ID"> 
<xsl:for-each select="node()"> 
           <xsl:sort select="ID" order="ascending" /> 
<!-- So far everything I've done here failed --> 
<xsl:for-each select=".[ID = '1']"> 
           <xsl:copy> 
            <xsl:copy-of select="node()[not(number(TimeStamp) &lt; (preceding-sibling::node()/TimeStamp | following-sibling::node()/TimeStamp))]"/> 
            </xsl:copy> 
           </xsl:for-each> 
<!-- This is just an example, I don't want to have ID = 1 and ID = 2 --> 
</xsl:for-each> 
         </xsl:if> 

         <xsl:if test="not(node()/ID)"> 
          <xsl:copy-of select="node()[not(number(TimeStamp) &lt; (preceding-sibling::node()/TimeStamp | following-sibling::node()/TimeStamp))]"/> 
         </xsl:if> 
        </xsl:copy> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:copy-of select="."/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

我希望我自己清楚。 在此先感谢您为我带来的所有帮助!

编辑:

真的对不起乡亲我应该mentionned,我想让它尽可能地通用。在我的例子中,我讨论的是PatientsPN,但我试图做的事情是匹配每个以PN结尾的PARENT节点(因此结尾 - 与XSL 1.0的复制版本)。

无论如何,你真的很棒,我不能指望你会有更多。谢谢 !

SOLUTION 重塑通过Dimitre给出的解决方案后,我想出了这个XSL:

<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:key name="kPatById" match="*['PN' = substring(name(), string-length(name()) -1)]/*" 
use="concat(generate-id(..), '|', ID)"/> 

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

<xsl:template match="*['PN' = substring(name(), string-length(name()) -1)]"> 
<xsl:copy> 
<xsl:apply-templates select="node()"> 
<xsl:sort select="ID" data-type="number"/> 
</xsl:apply-templates> 
</xsl:copy> 
</xsl:template> 

<xsl:template match="*['PN' = substring(name(), string-length(name()) -1)]/node()[TimeStamp &lt; key('kPatById', concat(generate-id(..), '|', ID))/TimeStamp]"/> 
</xsl:stylesheet> 

它的工作奇妙的,它让我有将要处理多个父节点并分类。

回答

1

可以是为简单:

一XSLT 1。0溶液

<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:key name="kPatById" match= 
"*['PN' = substring(name(), string-length(name()) -1)]/Patient" 
    use="concat(generate-id(..), '|', ID)"/> 

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

<xsl:template match= 
"*['PN' = substring(name(), string-length(name()) -1)]"> 
    <xsl:apply-templates select="Patient"> 
    <xsl:sort select="ID" data-type="number"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match= 
"*['PN' = substring(name(), string-length(name()) -1)] 
    /Patient 
     [TimeStamp &lt; key('kPatById', concat(generate-id(..), '|', ID))/TimeStamp] 
    "/> 
</xsl:stylesheet> 

当所提供的XML文档施加:

<Root> 
.... 
    <PatientsPN> 
     <Patient> 
      <ID>1</ID> 
      <TimeStamp>20111208165819</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Fanny</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>4</ID> 
      <TimeStamp>20111208165910</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Fanny4</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>4</ID> 
      <TimeStamp>20111208165902</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>FannyMOI</PrenomPatient> 
      <Sexe>M</Sexe> 
     </Patient> 
     <Patient> 
      <ID>2</ID> 
      <TimeStamp>20111208170000</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>FannyMOI</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>2</ID> 
      <TimeStamp>20111208165819</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Fanny</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>2</ID> 
      <TimeStamp>20111208170050</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Cmoi2</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
     <Patient> 
      <ID>3</ID> 
      <TimeStamp>20111208165829</TimeStamp> 
      <NomPatient>Dudule</NomPatient> 
      <PrenomPatient>Jesuis3</PrenomPatient> 
      <Sexe>F</Sexe> 
     </Patient> 
    </PatientsPN> 
... 
</Root> 

有用,正确的结果产生

<Root> 
.... 
    <Patient> 
     <ID>1</ID> 
     <TimeStamp>20111208165819</TimeStamp> 
     <NomPatient>Dudule</NomPatient> 
     <PrenomPatient>Fanny</PrenomPatient> 
     <Sexe>F</Sexe> 
    </Patient> 
    <Patient> 
     <ID>2</ID> 
     <TimeStamp>20111208170050</TimeStamp> 
     <NomPatient>Dudule</NomPatient> 
     <PrenomPatient>Cmoi2</PrenomPatient> 
     <Sexe>F</Sexe> 
    </Patient> 
    <Patient> 
     <ID>3</ID> 
     <TimeStamp>20111208165829</TimeStamp> 
     <NomPatient>Dudule</NomPatient> 
     <PrenomPatient>Jesuis3</PrenomPatient> 
     <Sexe>F</Sexe> 
    </Patient> 
    <Patient> 
     <ID>4</ID> 
     <TimeStamp>20111208165910</TimeStamp> 
     <NomPatient>Dudule</NomPatient> 
     <PrenomPatient>Fanny4</PrenomPatient> 
     <Sexe>F</Sexe> 
    </Patient> 
... 
</Root> 

说明

  1. 匹配与"PN"结束与名称的任何元件 - 使用的substring()string-length()的组合。

  2. 覆盖identity rule

  3. 排序,但不使用任何Muenchian分组

  4. 使用一个密钥来获取同一个患者的所有记录在同一个xxxPN父母下。

  5. “简单”最大(无需排序)。

  6. 适当的模式匹配以排除任何不需要的记录。


II。 XSLT 2.0解决方案

的XSLT 2.0解决方案,我觉得最好的是几乎一样的XSLT 1.0以上的解决方案,但可能更有效:

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

<xsl:key name="kPatById" match="*[ends-with(name(),'PN')]/Patient" 
      use="concat(generate-id(..), '|', ID)"/> 

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

<xsl:template match="*[ends-with(name(),'PN')]"> 
    <xsl:apply-templates select="Patient"> 
    <xsl:sort select="ID" data-type="number"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match= 
"*[ends-with(name(),'PN')] 
    /Patient 
     [number(TimeStamp) 
     lt 
      max((key('kPatById', concat(generate-id(..), '|', ID)) 
              /TimeStamp/xs:double(.))) 
     ]"/> 
</xsl:stylesheet> 
+0

这真的令人印象深刻,谢谢!但我很抱歉,我没有解释我的问题很好,我不能真的有一个xsl键匹配一个预定义的节点,因为我无法知道我的名字节点 – bosam

+0

@bosam:我更新了两个解决方案来实现你的新需求。 (请点击答案旁边的复选标记)这个好问题的最佳答案,然后问一个新问题,我想你的问题,+1。 –

+0

我更新了你的代码(见主要帖子),它的工作非常好,非常感谢,感谢大家的帮助,你为我节省了大量的时间!邮件标记为解决方案 – bosam

1

第一个答案(没有检查)似乎是正确的,但我无法拒绝发布XSLT 1.0版本,该版本不使用'evil'for-each关键字。

排序是通过在排序之前对ID和时间戳进行协调来完成的。

<?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="PatientsPN"> 
    <xsl:copy> 
     <xsl:apply-templates select="//Patient"> 
     <xsl:sort select="concat(ID,TimeStamp)"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="Patient"> 
    <xsl:if test="not(ID=following-sibling::Patient/ID)"> 
     <xsl:copy> 
     <xsl:apply-templates select="@* | node()"/> 
     </xsl:copy> 
    </xsl:if> 
    </xsl:template> 

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

希望这有助于

+1

@_Marvin斯密特:但是你的解决方案*不使用* “邪恶”'和waaay tooo looong :) –

+0

你的解决方案很好,但我不知道你是否看过我的例子,我试图使它通用。在我的例子中,我讨论的是PatientsPN,但我试图做的是匹配以PN结尾的每个节点(因此结尾 - 与XSL 1.0的复制版本)。我应该在问题中指定它,对不起我现在添加它。 – bosam

+0

:D爱上这样的爱!没有讨厌的批评是改善的方式! 虽然我可以将“IF测试”移到选择应用模板中。 :)我只是认为“ID + TimeStamp”排序+“获取所有唯一ID的最新条目是一个有趣的方式来解决它:) –