Home > OS >  How to get the value of a very specific XML element...?
How to get the value of a very specific XML element...?

Time:01-30

Suppose I have this in a file called data.xml:

<stats>
    <data1>lorem</data1>
    <data2>ipsum</data2>
    <source mount="/mno">
        <thing>1.0</thing>
        <somethingelse>10</somethingelse>
        <important>12345</important>
        <more>word</more>
    </source>
    <source mount="/pqr">
        <thing>1.0</thing>
        <somethingelse>10</somethingelse>
        <important>23456</important>
        <more>word</more>
    </source>
    <source mount="/stu">
        <thing>1.0</thing>
        <somethingelse>10</somethingelse>
        <important>34567</important>
        <more>word</more>
    </source>
    <source mount="/vwx">
        <thing>1.0</thing>
        <somethingelse>10</somethingelse>
        <important>45678</important>
        <more>word</more>
    </source>
</stats>

I now need to extract the contents of specifically the element <important> that's under <source mount="/stu">, and display this on a web page. I figured out I can do that like this:

<?php
$xml=simplexml_load_file("data.xml") or die("Error: Cannot create object");
echo $xml->source[3]->important;
?>

This actually works. What I do here is go to the third <source> which happens to be the one I need. However... The contents of the XML may (and will) change, and when that happens, <source mount="/stu"> may not be the third one anymore, which means the output would no longer be that of the specific element I need here.

How do I make it so that it will always display the contents of <important> from specifically <source mount="/stu">, even if the number of items in the XML changes?

I've been told XPATH could be the way to go, but coding is not my profession, and I find it hard to get my head around... I've also studied some other XML related questions here on StackOverflow, but my novice brain does not understand how to apply those to my situation. So any advise would be welcome here. Is XPATH indeed a good idea? If so, how do I implement/use that here. Or are there other/better ways of doing this?

Many thanks.

CodePudding user response:

One way is that you could loop over all $xml->source and exit from the loop when the value is important like so using a foreach loop:

<?php
    $xml=simplexml_load_file("data.xml") or die("Error: Cannot create object");
    foreach($xml->source as $source){
        if($source['mount'] == '/stu'){
            echo $source->important;
            break;
        }
    }
?>

CodePudding user response:

Using DOMDocument & DOMXPath make the task of retrieving particular nodes from within a simple XML file like this quite straightforward.

The XML posted contained errors with the data tags as mentioned - these were caught by libxml as it has it's own error handling routines.

$xml='<stats>
        <data1>lorem</data1>
        <data2>ipsum</data2>
        <source mount="/mno">
            <thing>1.0</thing>
            <somethingelse>10</somethingelse>
            <important>12345</important>
            <more>word</more>
        </source>
        <source mount="/pqr">
            <thing>1.0</thing>
            <somethingelse>10</somethingelse>
            <important>23456</important>
            <more>word</more>
        </source>
        <source mount="/stu">
            <thing>1.0</thing>
            <somethingelse>10</somethingelse>
            <important>34567</important>
            <more>word</more>
        </source>
        <source mount="/vwx">
            <thing>1.0</thing>
            <somethingelse>10</somethingelse>
            <important>45678</important>
            <more>word</more>
        </source>
    </stats>';

    
    libxml_use_internal_errors( true ) ;
    $dom=new DOMDocument();
    $dom->validateOnParse=false;
    $dom->recover=true;
    $dom->strictErrorChecking=false;
    $dom->loadXML( $xml );
    $errors=libxml_get_errors();
    
    $xp=new DOMXPath( $dom );
    if( $xp ){
        $expr='//source[@mount="/stu"]/important';
        $col=$xp->query( $expr );
        
        if( $col && $col->length > 0 )echo $col->item(0)->nodeValue;
    }

The XPath expression $expr='//source[@mount="/stu"]/important'; says to find any source node that has an attribute mount that exactly equals the desired string /stu - considerably more complex matching of nodes and attributes can be done than this.

This outputs:

34567

CodePudding user response:

With XPath, you describe what you want to select. Starting form the top of the document at the "root node" with / and then "walk" down the levels of the tree with each "step".

If you are looking for all of the important elements that are children of source elements, which are children of stats:

/stats/source/important

Or you could look for important elements anywhere, at any level within the document with:

//important

And if you only want the important element that is a child of source with the attribute mount="/stu":

/stats/source[@mount="/stu"]/important

Applied:

$result = $xml->xpath('/stats/source[@mount="/stu"]/important'); 
foreach ($result as $important){
    echo '/stats/source[@mount="/stu"]/important: ', $important, "\n";
}
  •  Tags:  
  • Related