Xpath-запрос и время - PullRequest
       21

Xpath-запрос и время

2 голосов
/ 24 января 2009

Привет, это содержимое моего файла XML:

<?xml version="1.0" encoding="ISO-8859-1"?>
<mainNode>
    <sub time="08:00">
        <status id="2">On</status>
        <status id="3">Off</status>
    </sub>
    <sub time="13:00">
        <status id="4">On</status>
        <status id="7">On</status>
    </sub>
    <sub time="16:00">
        <status id="5">On</status>
        <status id="6">On</status>
        <status id="7">Off</status>
        <status id="8">On</status>
    </sub>
    <sub time="20:00">
        <status id="4">Off</status>
        <status id="7">On</status>
    </sub>
    <sub time="23:59">
        <status id="4">On</status>
        <status id="7">On</status>
    </sub>
</mainNode>

Моя программа получает текущее время: если я получу 15.59, я должен получить все идентификаторы статуса следующего дополнительного времени (16.00):

<sub time="16:00">
        <status id="5">On</status>
        <status id="6">On</status>
        <status id="7">Off</status>
        <status id="8">On</status>
    </sub>

Со вторым запросом XPath я должен получить все идентификаторы состояния предыдущего дополнительного времени (13.00). Как это сделать? Я знаю SQL, но я довольно новичок в XPath. Я принимаю ссылки на серьезные ресурсы XPath, если таковые имеются. Спасибо!

Ответы [ 4 ]

2 голосов
/ 24 января 2009

Вот два решения:

I. XPath 1.0

Это одна пара XPath 1.0 выражений , которые выбирают необходимые узлы:

/*/*
    [translate(@time, ':','') 
    > 
     translate('15:59',':','')
    ][1]

выбирает первый sub узел со временем позже 15:59.

/*/*
    [translate(@time, ':','') 
    < 
     translate('15:59',':','')
    ][last()]

выбирает, выбирает первый sub узел с предыдущим, чем 15:59 sub время .

Мы можем включить их в XSLT-преобразование и убедиться, что получен действительно нужный результат:

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

    <xsl:template match="/">
      First time after 15:59: 
      <xsl:copy-of select=
       "/*/*
          [translate(@time, ':','') 
         > 
           translate('15:59',':','')
          ][1]
      "/>

      First time before 15:59: 
      <xsl:copy-of select=
       "/*/*
          [translate(@time, ':','') 
         &lt; 
           translate('15:59',':','')
          ][last()]
      "/>
  </xsl:template>
</xsl:stylesheet>

Когда вышеуказанное преобразование применяется к первоначально предоставленному документу XML:

<mainNode>
    <sub time="08:00">
        <status id="2">On</status>
        <status id="3">Off</status>
    </sub>
    <sub time="13:00">
        <status id="4">On</status>
        <status id="7">On</status>
    </sub>
    <sub time="16:00">
        <status id="5">On</status>
        <status id="6">On</status>
        <status id="7">Off</status>
        <status id="8">On</status>
    </sub>
    <sub time="20:00">
        <status id="4">Off</status>
        <status id="7">On</status>
    </sub>
    <sub time="23:59">
        <status id="4">On</status>
        <status id="7">On</status>
    </sub>
</mainNode>

желаемый результат получен :

  First time after 15:59: 


<sub time="16:00">
        <status id="5">On</status>
        <status id="6">On</status>
        <status id="7">Off</status>
        <status id="8">On</status>
</sub>

  First time before 15:59: 

 <sub time="13:00">
        <status id="4">On</status>
        <status id="7">On</status>
 </sub>

Примечание следующее:

  1. Использование функции XPath translate() для избавления от двоеточий

  2. Использование функции last() во втором выражении

  3. Нет необходимости преобразовывать время в секунды перед сравнением

  4. При использовании в качестве части документа XML (такого как таблица стилей XSLT, оператор < должен быть экранирован .

II. XPath 2.0

В XPath 2.0 мы можем использовать следующие два выражения для выбора нужных узлов:

/*/*[xs:time(concat(@time,':00')) 
    gt 
     xs:time('15:59:00')
    ][1]

выбирает первый sub узел со временем позже 15:59.

/*/*[xs:time(concat(@time,':00')) 
   lt 
     xs:time('15:59:00')
    ][last()]

выбирает выбирает первый sub узел с предыдущим, чем 15:59 sub время .

Мы можем включить их в преобразование XSLT 2.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"/>

    <xsl:template match="/">
      First time after 15:59: 
      <xsl:copy-of select=
       "/*/*[xs:time(concat(@time,':00')) 
           gt 
             xs:time('15:59:00')
             ][1]
      "/>

      First time before 15:59: 
      <xsl:copy-of select=
       "/*/*[xs:time(concat(@time,':00')) 
           lt 
             xs:time('15:59:00')
          ][last()]
      "/>
    </xsl:template>
</xsl:stylesheet>

Когда указанное преобразование применяется к первоначально предоставленному XML-документу (так же, как в первом решении), получается тот же самый желаемый результат.

Примечание следующее:

  1. В XPath 2.0 xs:time - это собственный тип данных . Однако для того, чтобы построить xs:time() из значений в документе xml, мы должны констатировать им недостающую часть секунд.
  2. В XPath 2.0 xs:time значения можно сравнивать с " операторами сопоставления атомарных значений " , такими как lt или gt.
2 голосов
/ 24 января 2009

Вот уродливое решение Xpath 1.0: -

sub[number((substring-before(@time, ':')) * 60 + number(substring-after(@time, ':'))) &gt; 959][1]

Примечание 959 = 15 * 60 + 59, что, я уверен, вы можете сделать в своем телефонном коде.

Предоставить этому узлу доступ к предыдущему узлу как: -

preceding-sibling::sub[1]

Однако прагматичным решением на основе здравого смысла было бы загрузить данные XML в набор структур данных и использовать язык, более подходящий для этой задачи, для поиска элементов.

1 голос
/ 24 января 2009

Ну, пока время HH: MM должно работать что-то вроде следующего: (Я должен извинить мой синтаксис, так как я просто балуюсь без запуска, рассмотрите этот псевдо-xpath):

xmlns:fn="http://www.w3.org/2005/02/xpath-functions"

//sub[fn:compare(@time,'12:59') > 0][1]/status

Это должно выбрать все элементы, где время больше 12:59, а затем выбрать первый из этих элементов.

Вы также можете передать значение '12: 59' в качестве x * шестнадцатеричного параметра в оценку xpath.

0 голосов
/ 24 января 2009

Если вы сгенерируете xml самостоятельно, вы можете изменить способ хранения атрибута времени, используя целочисленное значение (например, отметки), тогда вы можете выполнить простое числовое сравнение, используя что-то вроде

//sub[@time > 1389893892]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...