按ID排序,然后按同一节点内的时间戳排序

bos*_*sam 6 sorting xslt timestamp xslt-1.0

关于使用XSL 1.0进行排序,我有一个非常特殊的问题(只有1.0 - 我正在使用.Net Parser).

这是我的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>
Run Code Online (Sandbox Code Playgroud)

我想首先通过ID对我的PatientsNP进行排序,然后获取每个ID的更高的TimeStamp.我的输出:

<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>
Run Code Online (Sandbox Code Playgroud)

首先,我尝试按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>
Run Code Online (Sandbox Code Playgroud)

我希望我清楚自己.提前感谢您带来的所有帮助!

编辑:

我真的很抱歉我应该提到我想让它尽可能通用.在我的例子中,我在谈论PatientsPN,但我真正想做的是匹配以PN结尾的每个PARENT节点(因此结束了 - 与XSL 1.0的copycat版本).

无论如何你真的很棒,我不能期待更多来自你.谢谢 !

解决方案 在重新构建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>
Run Code Online (Sandbox Code Playgroud)

它完美地完成了这项工作,它允许我有多个将被处理和排序的父节点.

Dim*_*hev 2

可以这么简单

一、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>
Run Code Online (Sandbox Code Playgroud)

当应用于提供的 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>
Run Code Online (Sandbox Code Playgroud)

产生了想要的正确结果

<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>
Run Code Online (Sandbox Code Playgroud)

解释

  1. 使用和"PN"的组合来匹配名称以 -- 结尾的任何元素。substring()string-length()

  2. 超越身份规则

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

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

  5. “简单”最大值(不排序)。

  6. 正确的模式匹配可以排除任何不需要的记录。


二. 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>
Run Code Online (Sandbox Code Playgroud)