Asked  7 Months ago    Answers:  5   Viewed   27 times

I'm trying to compare two SimpleXML Objects.

One is fetched from DB and the other one from a XML API, but the result is always false, whether the XML are in fact identical or not.

What am I doing wrong?

$objDbXml   = simplexml_load_string($objReisen->xml); // XML from DB           
$objApiXml  = simplexml_load_string(getXMlFromApi()); // XML from Api
var_dump($objDbXml->Reise->Z_LEISTUNGEN == $objApiXml->Reise->Z_LEISTUNGEN);
// Result is always false

The output of var_dump($objDbXml->Reise->Z_LEISTUNGEN , $objApiXml->Reise->Z_LEISTUNGEN):

object(SimpleXMLElement) #69 (1) {
    ["TextLine"]= > array(11) {
        [0] = > string(43) "Erlebnisreise mit höchstens 13 Teilnehmern" 
        [1] = > string(39) "Durchführungsgarantie ab 4 Teilnehmern" 
        [2] = > string(127) "Linienflug mit South African Airways von Frankfurt a.M. nach Kapstadt und zurück von Port Elizabeth (von München auf Anfrage)" 
        [3] = > string(28) "Reiseminibus mit Klimaanlage" 
        [4] = > string(111) "Übernachtungen in Hotels und Lodges sowie 2 Übernachtungen in einer exklusiven Lodge im Kariega Game Reserve" 
        [5] = > string(67) "Täglich Frühstück, 2 x Mittagessen, 4 x Abendessen, 1 Weinprobe" 
        [6] = > string(123) "1 Safari im Addo-Elephant-NP; 2 Safaris im offenen Geländewagen, 1 Wandersafari und 1 Bootsfahrt im Kariega Game Reserve" 
        [7] = > string(41) "Nationalparkgebühren und Eintrittsgelder"
        [8] = > string(14) "Reiseliteratur" 
        [9] = > string(43) "Zertifikat über 100 m² Regenwald für Sie" 
        [10] = > string(42) "Deutsch sprechende Chamäleon-Reiseleitung"
    }
}

object(SimpleXMLElement) #67 (1) {
    ["TextLine"]= > array(11)
    {
        [0] = > string(43) "Erlebnisreise mit höchstens 12 Teilnehmern" 
        [1] = > string(39) "Durchführungsgarantie ab 4 Teilnehmern" 
        [2] = > string(127) "Linienflug mit South African Airways von Frankfurt a.M. nach Kapstadt und zurück von Port Elizabeth (von München auf Anfrage)" 
        [3] = > string(28) "Reiseminibus mit Klimaanlage" 
        [4] = > string(111) "Übernachtungen in Hotels und Lodges sowie 2 Übernachtungen in einer exklusiven Lodge im Kariega Game Reserve" 
        [5] = > string(67) "Täglich Frühstück, 2 x Mittagessen, 4 x Abendessen, 1 Weinprobe" 
        [6] = > string(123) "1 Safari im Addo-Elephant-NP; 2 Safaris im offenen Geländewagen, 1 Wandersafari und 1 Bootsfahrt im Kariega Game Reserve" 
        [7] = > string(41) "Nationalparkgebühren und Eintrittsgelder" 
        [8] = > string(14) "Reiseliteratur" 
        [9] = > string(43) "Zertifikat über 100 m² Regenwald für Sie" 
        [10] = > string(42) "Deutsch sprechende Chamäleon-Reiseleitung"
    }
}

 Answers

60

The problem here, as so often with SimpleXML, is in the fact that a SimpleXMLElement is not a "normal" PHP object. SimpleXML is not a parser which spits out fully-formed PHP objects with properties and methods, but a "live" API linked to an internal representation of an XML document.

The manual page on Comparing Objects states that "Two object instances are equal if they have the same attributes and values, and are instances of the same class." When you run print_r() or var_dump() over a SimpleXMLElement it appears to have properties representing the child nodes and attributes, which would be the same for two objects built from identical XML. However, the actual implementation contains only a pointer into a memory structure created when the XML was parsed, which will be different even if you parse the same string twice. Thus simply comparing two SimpleXMLElement objects with == will never return true.

The actual solution depends on what exactly you want to compare:

  • if you want to see if a particular fragment of the XML is 100% identical between the two documents, you could use ->asXML() to get an XML string for that part of the document; e.g. $objDbXml->Reise->Z_LEISTUNGEN->asXML() == $objApiXml->Reise->Z_LEISTUNGEN->asXML()
  • if there are a few specific properties which you want to compare, you may be better off selecting those out and comparing them individually, so that the test returns true even if they appear in a slightly different order, or with special characters encoded slightly differently
Wednesday, March 31, 2021
 
Pupil
answered 7 Months ago
77

It's not really clear to me what you're asking for in the end, however if you want to find out if a SimpleXMLElement hast a Node-Value or not, you can make use of the strlen() function in PHP.

It will take the string context of the SimpleXMLElement which is it's node-value (at least for those leaf-nodes you have in your question) and therefore will return 0 when the node is empty and larger than zero if it contains text.

Accessing a children by an attribute value is not supported by SimpleXMLElement out of the box. The Array-Access works as documented to access attributes, not children; See Example #5 Using attributes.

However you can extend SimpleXMLElement to add that functionality, for example to get a children by the Type attribute value:

class MyXMLElement extends SimpleXMLElement
{
    public function byType($value) {
        list($result) = ((array)$this->getByAttribute('Type', $value)) + array(NULL);
        return $result[0];
    }

    public function getByAttribute($attribute, $value) {
        return $this->xpath(sprintf('.//*[@%s = "%s"]', $attribute, $value));
    }
}

This new variant can be used instead of the old one so that you can easily access what you're looking for:

$team = simplexml_load_string($buffer, 'MyXMLElement');

echo "Team ",  $team['uID'], " Player(s):n";
foreach($team->Player as $i => $player)
{
    printf(" %d. %s %sn", $i + 1, $player->byType('first_name'), $player->byType('last_name'));
}

This for example with the reduced example you've got in your question making <Team> the root-element outputs:

Team t684 Player(s):
 1. Manuel Neuer

You find accessing a children by attribute value in SimpleXML outline as well in the following question:

  • SimpleXML: Selecting Elements Which Have A Certain Attribute Value (Dec 2009)
  • Access value through parent attribute (Jun 2010)
  • Help accessing xml attribute in php (Aug 2010)
  • Implementing condition in XPath (Aug 2010)

Last time I extended SimpleXMLElement on Stackoverflow was in an answer to Displaying 5 latest thumbnails from public flickr api using atom_1 and php.

No, actually turned out that last time I extended SimpleXMLElement on Stackoverflow was explaining exactly the same access by attribute value thing in the question PHP/XML - how to read multible sub's

Wednesday, March 31, 2021
 
twk
answered 7 Months ago
twk
70

You have a network/DNS problem, not a PHP problem. Seems your machine cannot resolve the IP of api.flickr.com.

"su" to your web server user and try to resolve the name there, i.e.

$ sudo bash
  # get root
$ su - apache
  # we're the apache user now
$ ping api.flickr.com
Saturday, May 29, 2021
 
barden
answered 5 Months ago
55

As far as I know, you can't do it with SimpleXML because addChild doesn't make a deep copy of the element (being necessary to specify the tag name can easily be overcome by calling SimpleXMLElement::getName()).

One solution would be to use DOM instead:

With this function:

function sxml_append(SimpleXMLElement $to, SimpleXMLElement $from) {
    $toDom = dom_import_simplexml($to);
    $fromDom = dom_import_simplexml($from);
    $toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true));
}

We have for

<?php
header("Content-type: text/plain");
$sxml = simplexml_load_string("<root></root>");

$n1 = simplexml_load_string("<child>one</child>");
$n2 = simplexml_load_string("<child><k>two</k></child>");

sxml_append($sxml, $n1);
sxml_append($sxml, $n2);

echo $sxml->asXML();

the output

<?xml version="1.0"?>
<root><child>one</child><child><k>two</k></child></root>

See also some user comments that use recursive functions and addChild, e.g. this one.

Friday, July 16, 2021
 
Null
answered 4 Months ago
60

function compare(base, compared, deepSearch) {
  var missing = [];

  var compareProp = function (baseValue, comparedValue, path, deepSearch) {
    //console.log('comparing', path.join('.'));

    if (comparedValue === undefined) {
      console.log('missing key', path.join('.'));

        if (!deepSearch) {
          return;
        }
    }

    if (typeof baseValue === 'object') {
      Object.keys(baseValue).forEach(function (key) {
        compareProp(baseValue [key], comparedValue && comparedValue [key], path.concat(key), deepSearch);
      }); 
    }
  };

  Object.keys(base).forEach(function (key) {
    compareProp(base [key], compared [key], [key], deepSearch);
  });
}

UC = {};
UC.start = {}
UC.start.enableHardEccDecline = '';
UC.start.template = {};
UC.start.template.ecc = '';
UC.start.template.decline = {};
UC.start.template.decline.title = '';
UC.start.template.decline.body = '';
UC.general = {};

compare (UC, {}, true);
Saturday, August 28, 2021
 
Travis
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :