Search code examples
phpxmlsimplexml

PHP/SimpleXML - Arrays generated differently for single child and multiple children


I'm using SimpleXML to parse an XML feed of property listings from different realtors. The relevant section of the XML feed looks something like this:

<branch name="Trustee Realtors">
    <properties>
        <property>
            <reference>1</reference>
            <price>275000</price>
            <bedrooms>3</bedrooms>
        </property>
        <property>
            <reference>2</reference>
            <price>350000</price>
            <bedrooms>4</bedrooms>
        </property>
        <property>
            <reference>3</reference>
            <price>128500</price>
            <bedrooms>4</bedrooms>
        </property>
    </properties>
</branch>
<branch name="Quick-E-Realty Inc">
    <properties>
        <property>
            <reference>4</reference>
            <price>180995</price>
            <bedrooms>3</bedrooms>
        </property>
    </properties>
</branch>

and is then converted to an array like this:

$xml = file_get_contents($filename);
$xml = simplexml_load_string($xml);
$xml_array = json_decode(json_encode((array) $xml), 1);
$xml_array = array($xml->getName() => $xml_array);

The issue I'm having is that when the array is created the data for the single listing is in a different position in the array to the multiple listings - I'm not sure exactly how to explain this, but if I var_dump() the array for the multiple items it looks like this:

array(3) {
    [0]=>
    array(3) {
        ["reference"]=>
        string(4) "0001"
        ["price"]=>
        string(6) "275000"
        ["bedrooms"]=>
        int(3)
    }
    [1]=>
    array(3) {
        ["reference"]=>
        string(4) "0002"
        ["price"]=>
        string(6) "350000"
        ["bedrooms"]=>
        int(4)
    }
    [2]=>
    array(3) {
    ["reference"]=>
        string(4) "0003"
        ["price"]=>
        string(6) "128500"
        ["bedrooms"]=>
        int(2)
    }
}

If I var_dump() the array for the single listing it looks like this:

array(3) {
    ["reference"]=>
    string(4) "0004"
    ["price"]=>
    string(6) "180995"
    ["bedrooms"]=>
    int(3)
}

But what I need it to look like is this:

array(1) {
    [0]=>
    array(3) {
        ["reference"]=>
        string(4) "0004"
        ["price"]=>
        string(6) "180995"
        ["bedrooms"]=>
        int(3)
    }
}

Each of these arrays represents the property listings from a single realtor. I'm not sure whether this is just the way that SimpleXML or the json functions work but what I need is for the same format to be used (the array containing the property listing to be the value of the [0] key).

Thanks in advance!


Solution

  • SimpleXML is quirky like this. I used it recently trying to make configuration files "easier" to write up and found out in the process that SimpleXML doesn't always act consistent. In this case I think you will benefit from simply detecting if a <property> is the only one in a set, and if so, then wrap it in an array by itself and then send it to your loop.

    NOTE: ['root'] is there because I needed to wrap a '<root></root>' element around your XML to make my test work.

    //Rebuild the properties listings
    $rebuild = array();
    foreach($xml_array['root']['branch'] as $key => $branch) {
        $branchName = $branch['@attributes']['name'];
        //Check to see if 'properties' is only one, if it
        //is then wrap it in an array of its own.
        if(is_array($branch['properties']['property']) && !isset($branch['properties']['property'][0])) {
            //Only one propery found, wrap it in an array
            $rebuild[$branchName] = array($branch['properties']['property']);
        } else {
            //Multiple properties found
            $rebuild[$branchName] = $branch['properties']['property'];
        }
    }
    

    That takes care of rebuilding your properties. It feels a little hackish. But basically you are detecting for the lack of a multi-dimensional array here:

    if(is_array($branch['properties']['property']) && !isset($branch['properties']['property'][0]))
    

    If you don't find a multi-dimensional array then you explicitly make one of the single <property>. Then to test that everything was rebuilt correctly you can use this code:

    //Now do your operation...whatever it is.
    foreach($rebuild as $branch => $properties) {
        print("Listings for $branch:\n");
        foreach($properties as $property) {
            print("Reference of " . $property['reference'] . " sells at $" . $property['price'] . " for " . $property['bedrooms'] . " bedrooms.\n");
        }
        print("\n");
    }
    

    This produces the following output:

    Listings for Trustee Realtors:
    Reference of 1 sells at $275000 for 3 bedrooms.
    Reference of 2 sells at $350000 for 4 bedrooms.
    Reference of 3 sells at $128500 for 4 bedrooms.
    
    Listings for Quick-E-Realty Inc:
    Reference of 4 sells at $180995 for 3 bedrooms.
    

    And a dump of the rebuild will produce:

    Array
    (
        [Trustee Realtors] => Array
            (
                [0] => Array
                    (
                        [reference] => 1
                        [price] => 275000
                        [bedrooms] => 3
                    )
    
                [1] => Array
                    (
                        [reference] => 2
                        [price] => 350000
                        [bedrooms] => 4
                    )
    
                [2] => Array
                    (
                        [reference] => 3
                        [price] => 128500
                        [bedrooms] => 4
                    )
    
            )
    
        [Quick-E-Realty Inc] => Array
            (
                [0] => Array
                    (
                        [reference] => 4
                        [price] => 180995
                        [bedrooms] => 3
                    )
    
            )
    
    )
    

    I hope that helps you out getting closer to a solution to your problem.