PHP / SimpleXML - Arrays created differently for one child and several children

I use SimpleXML to parse the XML feed of property lists from different realtors. The corresponding 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 then converted to an array as follows:

 $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 problem I am facing is that when the array is created, the data for one listing is in a different position in the array for multiple lists - I'm not sure how to explain this, but if I var_dump () the array for several elements looks like this way:

 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 have a var_dump () array for a single list, it looks like this:

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

But I need it to look like this:

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

Each of these arrays represents property listings from a single realtor. I'm not sure if this is just the way SimpleXML or json works, but what I need is to use the same format (an array containing a list of properties, which is the value of the [0] key).

Thanks in advance!

+7
xml php simplexml
source share
6 answers

SimpleXML is just as dodgy. I have used it lately, trying to make configuration files β€œeasier” to write and find out in the process that SimpleXML does not always work in concert. In this case, I think it will be useful for you to simply determine if the <property> only one in the set, and if so, first wrap it in an array, and then send it to your loop.

NOTE: ['root'] is there because I needed to wrap the '<root></root>' element around your XML so that my testing 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']; } } 

This will help restore your properties. It feels a bit hacked. But basically you find the lack of a multidimensional array here:

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

If you do not find a multidimensional array, you will obviously make one of the single <property> . Then, to verify that everything was restored 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 leads to the following conclusion:

 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 the rebuild dump will create:

 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 this helps you get closer to solving your problem.

+4
source share

The big massive β€œthink outside the box” question to ask yourself: why do you first convert a SimpleXML object to an array?

SimpleXML is not just a library for parsing XML, and then using something else to manipulate it, it is designed exactly for what you are going to do with this array.

In fact, this problem, sometimes having separate elements, and sometimes several, is one of the big advantages that it has in the representation of a simple array: for nodes that, as you know, will be single, you can leave [0] ; but for nodes, which, as you know, can be several, you can use the [0] or foreach , and this will work too.

Here are some examples of why SimpleXML matches its name with your XML:

 $sxml = simplexml_load_string($xml); // Looping over multiple nodes with the same name // We could also use $sxml->children() to loop regardless of name // or even the shorthand foreach ( $sxml as $children ) foreach ( $sxml->branch as $branch ) { // Access an attribute using array index notation // the (string) is optional here, but good habit to avoid // passing around SimpleXML objects by mistake echo 'The branch name is: ' . (string)$branch['name'] . "\n"; // We know there is only one <properties> node, so we can take a shortcut: // $branch->properties means the same as $branch->properties[0] // We don't know if there are 1 or many <property> nodes, but it // doesn't matter: we're asking to loop over them, so SimpleXML // knows what we mean foreach ( $branch->properties->property as $property ) { echo 'The property reference is ' . (string)$property->reference . "\n"; } } 

Basically, whenever I see this ugly trick json_decode(json_encode( , I compress a bit, because 99 times out of 100 the following code is much uglier than just using SimpleXML.

+4
source share

One option is to read XML using DOM + XPath. XML cannot simply be converted to JSON, but creating a specific JSON for a specific XML file is easy:

 $dom = new DOMDocument(); $dom->loadXml($xml); $xpath = new DOMXPath($dom); $result = []; foreach ($xpath->evaluate('//branch') as $branchNode) { $properties = []; foreach ($xpath->evaluate('properties/property', $branchNode) as $propertyNode) { $properties[] = [ 'reference' => $xpath->evaluate('string(reference)', $propertyNode), 'price' => (int)$xpath->evaluate('string(price)', $propertyNode), 'bedrooms' => (int)$xpath->evaluate('string(bedrooms)', $propertyNode) ]; } $result[] = [ 'name' => $xpath->evaluate('string(@name)', $branchNode), 'properties' => $properties ]; } echo json_encode($result, JSON_PRETTY_PRINT); 

Exit: https://eval.in/154352

 [ { "name": "Trustee Realtors", "properties": [ { "reference": "1", "price": 275000, "bedrooms": 3 }, { "reference": "2", "price": 350000, "bedrooms": 4 }, { "reference": "3", "price": 128500, "bedrooms": 4 } ] }, { "name": "Quick-E-Realty Inc", "properties": [ { "reference": "4", "price": 180995, "bedrooms": 3 } ] } 
+2
source share

You used this:

 $xml_array['branch']['properties']['property'] 

how is the loop source? try using this:

 $xml_array['branch']['properties'] 

don't use ['property'] at the end of the line, don't use 3 segments, just use 2 segments

 <?php $xml = file_get_contents('simple.xml'); $xml = simplexml_load_string($xml); $xml_array = json_decode(json_encode((array) $xml), 1); $xml_array = array($xml->getName() => $xml_array); print_r($xml_array); foreach($xml_array['branch']['properties'] as $a){ print_r($a); } ?> 
0
source share

To solve this problem, you should choose using xpath (like other references), but in my opinion this is not a very familiar tool for most web developers. I created a very small package with composer support that solves this problem. Subscribe to the symfony CssSelector package ( https://symfony.com/doc/current/components/css_selector.html ), which rewrites CSS selectors in xpath selectors. My package is just a thin shell that actually deals with what you mostly do with XML using PHP. You can find it here: https://github.com/diversen/simple-query-selector

 use diversen\querySelector; // Load simple XML document $xml = simplexml_load_file('test2.xml'); // Get all branches as DOM elements $elems = querySelector::getElementsAsDOM($xml, 'branch'); foreach($elems as $elem) { // Get attribute name echo $elem->attributes()->name . "\n"; // Get properties as array $props = querySelector::getElementsAsAry($elem, 'property'); print_r($props); // You will get the array structure you expect } 

You can also (if you don't care about the branch name):

 $elems = querySelector::getElementsAsAry($xml, 'property'); 
0
source share

Testing if the XML parsing has multiple tags or one tag converted to an array, instead of rebuilding the array, you can simply check the following case:

 <?php if (is_array($info[0])) { foreach ($info as $fields) { // Do something... } } else { // Do something else... } 
0
source share

All Articles