PHP + XPath在指定日期之间获取节点值

时间:2016-06-23 16:55:39

标签: php xml xpath xpath-2.0

我的XML文件如下:

<record>
    <name>John</name>
    <StartDate>01-05-2016</StartDate>
    <EndDate>30-10-2016</EndDate>
</record>
<record>
    <name>Jerry</name>
    <StartDate>29-04-2016</StartDate>
    <EndDate>30-06-2016</EndDate>
</record>
<record>
    <name>Mike</name>
    <StartDate>05-06-2016</StartDate>
    <EndDate>25-08-2016</EndDate>
</record>

我有两个日期说:

start date: 30-04-2016 and,
end date: 27-08-2016

我想编写一个Xpath查询,它将返回所有记录,其中<StartDate><EndDate>在上述两个日期之间(包含两者)。

3 个答案:

答案 0 :(得分:3)

您可以解析数据并添加到数组,作为stdClass或您最喜欢的任何内容:

<?php

$xml =
    '<root>
        <record>
            <name>John</name>
            <StartDate>01-05-2016</StartDate>
            <EndDate>30-10-2016</EndDate>
        </record>
        <record>
            <name>Jerry</name>
            <StartDate>29-04-2016</StartDate>
            <EndDate>30-06-2016</EndDate>
        </record>
        <record>
            <name>Mike</name>
            <StartDate>05-06-2016</StartDate>
            <EndDate>25-08-2016</EndDate>
        </record>
    </root>';

$doc= new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXpath($doc);
$elements = $xpath->query("//record");

$output = [];

$format = 'd-m-Y';
$startDate = DateTime::createFromFormat($format, '30-04-2016');
$endDate = DateTime::createFromFormat($format, '27-08-2016');

foreach($elements as $element) {
    $elementStartDate = DateTime::createFromFormat($format, $element->getElementsByTagName("StartDate")->item(0)->nodeValue);
    $elementEndDate = DateTime::createFromFormat($format, $element->getElementsByTagName("EndDate")->item(0)->nodeValue);

    if( ($startDate <= $elementStartDate) &&
        ($endDate >= $elementEndDate)) {
        $obj = new stdClass;
        $obj->name = $element->getElementsByTagName("name")->item(0)->nodeValue;
        $obj->startDate = $element->getElementsByTagName("StartDate")->item(0)->nodeValue;
        $obj->endDate = $element->getElementsByTagName("EndDate")->item(0)->nodeValue;
        $output[] = $obj;
    }
}

var_dump($output);

<强>输出

array(1) {
  [0]=>
  object(stdClass)#10 (3) {
    ["name"]=>
    string(4) "Mike"
    ["startDate"]=>
    string(10) "05-06-2016"
    ["endDate"]=>
    string(10) "25-08-2016"
  }
}

答案 1 :(得分:1)

    $startDate = date_format(date_create('30-04-2016'),'d-m-Y');
    $endDate = date_format(date_create('27-08-2016'),'d-m-Y');
    $doc = new DOMDocument();
    $doc->loadXML('<records><record><name>John</name><StartDate>01-05-2016</StartDate><EndDate>30-10-2016</EndDate></record><record><name>Jerry</name><StartDate>29-04-2016</StartDate><EndDate>30-06-2016</EndDate></record><record><name>Mike</name><StartDate>05-06-2016</StartDate><EndDate>25-08-2016</EndDate></record></records>');

    $xpath = new DOMXpath($doc);


    //Get all XML "RECORDS"
    $elements = $xpath->query("//record");
    // Loop through the result elements of the xpath query
    if (!is_null($elements)) {
      foreach ($elements as $element) {
        $nodes = $element->childNodes;
        foreach ($nodes as $node) {
        // Use two logical type variables for each element
        ($node->nodeName=='StartDate' && date_format(date_create($node->nodeValue),'d-m-Y')>=$startDate) ? $stD = 1 : $stD = 0;           
        ($node->nodeName=='EndDate' && date_format(date_create($node->nodeValue),'d-m-Y')<=$endDate) ?  $edD = 1 : $edD = 0;

        }
        // if both $stD and $edD variables do not have logical 1 as value then our $element does not meet the expected dates condition so we remove it from the xml string
        if($stD!=1 && $edD!=1) {
            $element->parentNode->removeChild($element);
        }
      }
      echo $doc->saveXML();    
    }

检查PHP Sandbox

中的上述输出

答案 2 :(得分:1)

  

我有两个日期说:

<StartDate>
     

我想编写一个Xpath Query,它将返回所有记录   在上述两个日期之间有<EndDate><record>   包括在内)。

这是一个纯粹的XPath 2.0表达式,用于选择所有此类/*/record [xs:date(string-join(reverse(tokenize(StartDate, '-')), '-')) ge xs:date('2016-04-30') and xs:date(string-join(reverse(tokenize(EndDate, '-')), '-')) le xs:date('2016-08-27')] 元素:

<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:template match="/">
    <xsl:copy-of select=
    "/*/record
        [xs:date(string-join(reverse(tokenize(StartDate, '-')), '-')) 
         ge xs:date('2016-04-30') 
        and xs:date(string-join(reverse(tokenize(EndDate, '-')), '-')) 
         le xs:date('2016-08-27')]"/>
  </xsl:template>
</xsl:stylesheet>

基于XSLT的验证

<t>
    <record>
      <name>John</name>
      <StartDate>01-05-2016</StartDate>
      <EndDate>30-10-2016</EndDate>
    </record>
    <record>
      <name>Jerry</name>
      <StartDate>29-04-2016</StartDate>
      <EndDate>30-06-2016</EndDate>
    </record>
    <record>
      <name>Mike</name>
      <StartDate>05-06-2016</StartDate>
      <EndDate>25-08-2016</EndDate>
   </record>
</t>

将此转换应用于以下XML文档(提供的带有顶部元素父级的片段):

<record>
  <name>Mike</name>
  <StartDate>05-06-2016</StartDate>
  <EndDate>25-08-2016</EndDate>
</record>

评估Xpath表达式,并将选定的节点(在本例中只是一个)复制到输出中:

/*/record
    [concat(substring(StartDate,7), substring(StartDate,4,2), substring(StartDate,1,2)) 
      >= 20160430
   and not(concat(substring(EndDate,7), substring(EndDate,4,2), substring(EndDate,1,2)) 
           > 20160827)]

<强> II。 XPath 1.0解决方案

等效的XPath 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:template match="/">
    <xsl:copy-of select=
    "/*/record
        [concat(substring(StartDate,7), substring(StartDate,4,2), substring(StartDate,1,2)) 
          >= 20160430
       and not(concat(substring(EndDate,7), substring(EndDate,4,2), substring(EndDate,1,2)) 
                > 20160827)]"/>
  </xsl:template>
</xsl:stylesheet>

基于XSLT 1.0的验证

<record>
   <name>Mike</name>
   <StartDate>05-06-2016</StartDate>
   <EndDate>25-08-2016</EndDate>
</record>

当此转换应用于与上述相同的XML文档时,会生成相同的想要的正确结果

{{1}}