2

My XML file looks like :

<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>

I have Two Dates Say :

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

I want to write a Xpath Query which will return all the record which have <StartDate> and <EndDate> In between the above two dates(Both Inclusive).

0

3 Answers 3

3

You can parse data and add to an array, as a stdClass or whatever you like most:

<?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);

Output

array(1) {
  [0]=>
  object(stdClass)#10 (3) {
    ["name"]=>
    string(4) "Mike"
    ["startDate"]=>
    string(10) "05-06-2016"
    ["endDate"]=>
    string(10) "25-08-2016"
  }
}
Sign up to request clarification or add additional context in comments.

Comments

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();    
    }

Check the above output in PHP Sandbox

6 Comments

I do not think this will work for me. I want to check for DATE not strings values.
if you check the output xml it has only the record that meets the date condition that you want @Ajay. Additionally you check for date this way...
Yes it did happened in this case but these are not the only Dates and record i have to check for. I have thousands of record
Try it live in your script it will get your results, by the way i had to add a root tag in your xml <records></records> i think your xml is probably like that, holding a lot more records than this sample. The output of the above will be an xml with only the filtered results.
Check the edited answer i had a small typo error in here that was not in Sandbox example @Ajay.
|
1

I have Two Dates Say :

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

I want to write a Xpath Query which will return all the record which have <StartDate> and <EndDate> In between the above two dates(Both Inclusive).

Here is a single, pure XPath 2.0 expression that selects all such <record> elements:

/*/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')]

XSLT-based verification:

<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>

When this transformation is applied on the following XML document (the provided fragment with a top element parent):

<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>

the Xpath expression is evaluated and the selected nodes (in this case just one) are copied to the output:

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

II. XPath 1.0 solution

The equivalent XPath 1.0 expression is:

/*/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)]

XSLT 1.0 - based verification:

<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>

When this transformation is applied on the same XML document as above, the same wanted, correct result is produced:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.