Reputation: 25
I have a mind boggling task. I have a feed which is straight forward. Properties for rent to be precise and it looks something like this:
<ad>
<name>Property 1</name>
<latitude>29.723085</latitude>
<longitude>-95.66024</longitude>
<area>2000</area>
</ad>
<ad>
<name>Property 1</name>
<latitude>29.723085</latitude>
<longitude>-95.66024</longitude>
<area>2500</area>
</ad>
In the above example there is a single property with two different units. The only thing which is common for both is the name and latitude, longitude fields.
Now, I need to recreate this XML into the new one using PHP, and to place all of the different units to the one property. So the new feed will look like following:
<ad>
<property_name>Property 1</property_name>
<latitude>29.723085</latitude>
<longitude>-95.66024</longitude>
<units>
<unit>
<area>2000</area>
</unit>
<unit>
<area>2500</area>
</unit>
</units>
</ad>
Can somebody help me out with this? Many thanks
Upvotes: 0
Views: 36
Reputation: 11689
There are more options to obtain your desired result. With DOMDocument
you can use importNode
, or you can directly modify original XML.
I will show you a method to create a completely new XML.
First of all, you have to load existing XML in a DOMDocument
object:
$src = new DOMDocument();
libxml_use_internal_errors(1);
$src->loadXML( $xml );
Then create the destination DOMDocument
object. For this, I use a generic <root>
tag; you can replace it with your complete XML wrapping <ad>
tags:
$dom = new DOMDocument();
libxml_use_internal_errors(1);
$dom->loadXML( '<root></root>' );
$dom->formatOutput = True;
$root = $dom->getElementsByTagName( 'root' )->item(0);
Now, init a DOMXpath
object for destination XML. DOMXPath
permits to execute complex XML queries:
$xpath = new DOMXPath( $dom );
At this point, perform a foreach
through all <ad>
nodes of source XML and — for each node — retrieve <name>
and <area>
values:
foreach( $src->getElementsByTagName( 'ad' ) as $node )
{
$name = $node->getElementsByTagName( 'name' )->item(0)->nodeValue;
$area = $node->getElementsByTagName( 'area' )->item(0)->nodeValue;
Now use DOMXPath
to find if in destination XML there is already a <ad>
node with <name>
value = retrieved name:
$found = $xpath->query( '//ad[name[.="'.$name.'"]]' );
If the destination node is found, set its <units>
as $child
:
if($found->length)
{
$child = $found->item(0)->getElementsByTagName('units')->item(0);
}
Otherwise, create a new node adding base data from source, then set it as $child
:
else
{
$lat = $node->getElementsByTagName( 'latitude' )->item(0)->nodeValue;
$long = $node->getElementsByTagName( 'longitude' )->item(0)->nodeValue;
$child = $dom->createElement( 'ad' );
$child->appendChild( $dom->createElement( 'name', $name ) );
$child->appendChild( $dom->createElement( 'latitude', $lat ) );
$child->appendChild( $dom->createElement( 'longitude', $long ) );
$child->appendChild( $dom->createElement( 'units' ) );
$root->appendChild( $child );
$child = $child->getElementsByTagName('units')->item(0);
}
At this point, you have the correct $child
node to add the <unit>
:
$unit = $dom->createElement( 'unit' );
$unit->appendChild( $dom->createElement( 'area', $area ) );
$child->appendChild( $unit );
}
At the end of foreach()
loop, you can print the obtained XML:
echo $dom->saveXML();
output:
<?xml version="1.0"?>
<root>
<ad>
<name>Property 1</name>
<latitude>29.723085</latitude>
<longitude>-95.66024</longitude>
<units/>
<unit>
<area>2000</area>
</unit>
<unit>
<area>2500</area>
</unit>
</ad>
</root>
Additional notes:
In script above, I assume that each property name has unique long/lat. If can exists properties with same name but different long/lat, you have to find node not only by name, but also by latitude/longitude.
I assume also that your structure sample is respected in each node. Otherwise, syntax like $node->getElementsByTagName( 'latitude' )->item(0)->nodeValue
can fails, and you have to replace it by:
if( $node->getElementsByTagName( 'latitude' )->length )
{
$lat = $node->getElementsByTagName( 'latitude' )->item(0)->nodeValue;
}
and so for each code line with ->item(0)
syntax.
Upvotes: 1