How can I create this XML properly with PHP - php

I'm trying to create XML from an array in the following format
<root>
<kyero>
<feed_version>3</feed_version>
</kyero>
<property>
<id>12367</id>
<date>2013-09-27 12:00:10</date>
<ref>V3TEST</ref>
<price>250000</price>
<currency>EUR</currency>
<price_freq>sale</price_freq>
<leasehold>0</leasehold>
<type>villa</type>
<town>almunecar</town>
<province>granada</province>
<country>spain</country>
<location>
<latitude>36.728807</latitude>
<longitude>-3.693466</longitude>
</location>
<features>
<feature>good rental potential</feature>
<feature>near beach</feature>
<feature>water possible</feature>
</features>
<notes>Private property notes</notes>
<images>
<image id="1">
<url>http://images.kyero.com/12811577_large.jpg</url>
</image>
<image id="2">
<url>http://images.kyero.com/12811578_large.jpg</url>
</image>
<image id="3">
<url>http://images.kyero.com/12811579_large.jpg</url>
</image>
<image id="4">
<url>http://images.kyero.com/12811581_large.jpg</url>
</image>
<image id="50">
<url>http://images.kyero.com/12811582_large.jpg</url>
</image>
</images>
</property>
</root>
The important take away is the nested arrays such as <features> and <images>.
An example of my array that I've built so far is:
Array
(
[kyero] => Array
(
[feed_version] => 3
)
[0] => Array
(
[id] => 2024
[ref] => jtl-2024-4202
[date] => 2019-11-19 15:34:39
[title] => A wonderful property
[price] => 14828
[price_freq] => month
[country] => United Kingdom
[location] => Array
(
[latitude] => 55.311512
[longitude] => -2.9378154999999
)
[features] => Array
(
[0] => Prominent Building
[1] => Great Location
[2] => Grade A Office Space
[3] => Air Conditioning
[4] => Generous Car Parking
)
[images] => Array
(
[0] => Array
(
[url] => https://example.com/image1.jpg
)
[1] => Array
(
[url] => https://example.com/image2.jpg
)
)
)
)
And the code to convert the array to XML
$xml_data = new SimpleXMLElement('<?xml version="1.0"?><root></root>');
//$data = array as shown above
array_to_xml($data, $xml_data);
The specific issue lies within this method
function array_to_xml($data, &$xml_data, $isImg = FALSE, $isFeature = FALSE) {
$i = 1;
foreach ($data as $key => $value) {
if (is_numeric($key)) {
$key = 'property'; //dealing with <0/>..<n/> issues
if($isImg){
$key = 'image';
}
if($isFeature){
$key = 'feature';
}
}
if (is_array($value)) {
if($key == 'images'){
$isImg = true;
} elseif($key == 'features'){
$isFeature = true;
}
$subnode = $xml_data->addChild($key);
if($key == 'image'){
$subnode->addAttribute('id', $i);
}
array_to_xml($value, $subnode, $isImg, $isFeature);
} else {
$xml_data->addChild("$key", htmlspecialchars("$value"));
}
$i++;
}
}
The output of the XML comes out as desired except for a few parts, specifically the nested parts like features and images.Note how each <url> is wrapped in a <feature> node. This should be a <image> node instead like shown in the originally XML shown at the start of this question, in addition to the id attributes on the image nodes as well.
(the <location> doesn't suffer from the same problem)
<features>
<feature>Prominent Building</feature>
<feature>Great Location</feature>
<feature>Grade A Office Space</feature>
<feature>Air Conditioning</feature>
<feature>Generous Car Parking</feature>
</features>
<images>
<feature>
<url>
https://example.com/image1.jpg
</url>
</feature>
<feature>
<url>
https://example.com/image2.jpg
</url>
</feature>
</images>
If I comment out
if($isFeature){
$key = 'feature';
}
and
elseif($key == 'features'){
$isFeature = true;
}
The images then are formatted correctly
<images>
<image id="1">
<url>
https://example.com/image1.jpg
</url>
</image>
<image id="2">
<url>
https://example.com/image2.jpg
</url>
</image>
</images>
but the features are incorrect, instead they become
<features>
<property>Prominent Building</property>
<property>Great Location</property>
<property>Grade A Office Space</property>
<property>Air Conditioning</property>
<property>Generous Car Parking</property>
</features>
The <property> nodes should be <feature> nodes instead.
Could you help me with the array_to_xml() method to handle these and other nested arrays correctly?
Original conversion code: https://stackoverflow.com/a/5965940/10884442

I agree with ThW about creating the data directly, but I am also a fan of doing things simply and SimpleXML is a good fit with what you are trying to do.
As I don't know how you obtained the data you have so far I will code this using that array, but instead of trying to do a generic flexible version, this shows how easy it is to create the entire structure using SimpleXML.
All it does is use the SimpleXML ability to reference an element by using object notation, the code is quite repetitive, but all it is doing is setting elements form the data in your array...
$xml_data = new SimpleXMLElement('<?xml version="1.0"?><root></root>');
$xml_data->kyero->feed_version = $data['kyero']['feed_version'];
$newProperty = $xml_data->addChild("property");
$newProperty->id = $data[0]['id'];
$newProperty->ref = $data[0]['ref'];
$newProperty->date = $data[0]['date'];
$newProperty->title = $data[0]['title'];
$newProperty->price = $data[0]['price'];
$newProperty->price_freq = $data[0]['price_freq'];
$newProperty->country = $data[0]['country'];
$newProperty->location->latitude = $data[0]['location']['latitude'];
$newProperty->location->longitude = $data[0]['location']['longitude'];
foreach ( $data[0]['features'] as $key => $feature ) {
$newProperty->features->feature[$key] = $feature;
}
$images = $newProperty->addChild("images");
foreach ( $data[0]['images'] as $key => $image ) {
$newImage = $images->addChild("image");
$newImage['id']= $key+1;
$newImage->url = $image['url'];
}
using your sample data, this gives...
<?xml version="1.0"?>
<root>
<kyero>
<feed_version>3</feed_version>
</kyero>
<property>
<id>2024</id>
<ref>jtl-2024-4202</ref>
<date>2019-11-19 15:34:39</date>
<title>A wonderful property</title>
<price>14828</price>
<price_freq>month</price_freq>
<country>United Kingdom</country>
<location>
<latitude>55.311512</latitude>
<longitude>-2.9378154999999</longitude>
</location>
<features>
<feature>Prominent Building</feature>
<feature>Great Location</feature>
<feature>Grade A Office Space</feature>
<feature>Air Conditioning</feature>
<feature>Generous Car Parking</feature>
</features>
<images>
<image id="1">
<url>https://example.com/image1.jpg</url>
</image>
<image id="2">
<url>https://example.com/image2.jpg</url>
</image>
</images>
</property>
</root>

Don't CONVERT, but CREATE. In other words do not try to build and array and implement a generic mapper. It is a lot more difficult. SimpleXML does not allow much control. In DOM here are 3 steps:
Create the node using DOMDocument::create...() methods
Append/Insert the node to its parent node using DOMNode::appendChild()/DOMNode::insertBefore()
Add attributes and child nodes to the node
Here is a small example:
$document = new DOMDocument('1.0', 'UTF-8');
$root = $document->appendChild($document->createElement('root'));
$kyero = $root->appendChild($document->createElement('kyero'));
$kyero->appendChild(
$document->createElement('feed_version')
)->textContent = '3';
$document->formatOutput = TRUE;
echo $document->saveXML();
Output:
<root>
<kyero>
<feed_version>3</feed_version>
</kyero>
</root>
DOMNode::appendChild() returns the appended node, so you can nest the create call.
Changing DOMNode::$textContent replaces all current child nodes with a single text node. Unlike the secondary argument for SimpleXMLElement::addChild()/DOMDocument::createElement() it will properly escape special characters.
I typically implement a method XMLAppendable::appendTo() for XML serializable objects. It allows you to keep the XML create snippets in a maintainable size and add some verification.
interface XMLAppendable {
public function appendTo(DOMElement $parentNode): void;
}
class ImageList implements XMLAppendable {
private $_images = [];
public function __construct(array $images) {
$this->_images = $images;
}
public function appendTo(DOMElement $parentNode): void {
$document = $parentNode->ownerDocument;
$images = $parentNode->appendChild(
$document->createElement('images')
);
foreach ($this->_images as $id => $url) {
$image = $images->appendChild($document->createElement('images'));
$image->setAttribute('id', $id);
$image
->appendChild($document->createElement('url'))
->textContent = $url;
}
}
}
$document = new DOMDocument('1.0', 'UTF-8');
$root = $document->appendChild(
$document->createElement('root')
);
$property = $root->appendChild(
$document->createElement('property')
);
$images = new ImageList(
[
'2' => 'http://images.kyero.com/12811578_large.jpg',
'50' => 'http://images.kyero.com/12811582_large.jpg'
]
);
$images->appendTo($property);
$document->formatOutput = TRUE;
echo $document->saveXML();

Related

XML change value from parent to child

I have this XML:
<destinos>
<destino>
<location>Spain</location>
<programas>
<item></item>
<item></item>
</programas>
</destino>
<destino>
<location>France</location>
<programas>
<item></item>
<item></item>
</programas>
</destino>
</destinos>
I need to include or copy the value of "Location" within each "item" and I am not able to do so.
<destinos>
<destino>
<location>Spain</location>
<programas>
<item>
<location>Spain</location>
</item>
<item>
<location>Spain</location>
</item>
</programas>
</destino>
<destino>
<location>France</location>
<programas>
<item>
<location>France</location>
</item>
<item>
<location>France</location>
</item>
</programas>
</destino>
</destinos>
I have no knowledge of PHP and I have been reading but I can't find the solution.
If someone could help me and explain I would be very grateful.
My code:
$url = file_get_contents("archive.xml");
$xml = simplexml_load_string($url);
$changes = $xml->xpath("//*[starts-with(local-name(), 'item')]");
foreach ($changes as $change)
$change[0] = $xml->destinos->destino->location;
header('Content-Type: application/xml');
echo $xml->asXML();
One option could be to use xpath with addChild with with the value of the location:
$url = file_get_contents("archive.xml");
$xml = simplexml_load_string($url);
$changes = $xml->xpath("/destinos/destino");
foreach ($changes as $change) {
$text = (string)$change->location;
foreach ($change->xpath("programas/item") as $i) {
$i->addChild("location", $text);
}
}
header('Content-Type: application/xml');
echo $xml->asXML();
Output
<destinos>
<destino>
<location>Spain</location>
<programas>
<item><location>Spain</location></item>
<item><location>Spain</location></item>
</programas>
</destino>
<destino>
<location>France</location>
<programas>
<item><location>France</location></item>
<item><location>France</location></item>
</programas>
</destino>
</destinos>
Php demo
Using SimpleXML, you can just use object notation to access the various elements of the document, this stops the need for XPath and can also make the code more readable...
$url = file_get_contents("archive.xml");
$xml = simplexml_load_string($url);
foreach ($xml->destino as $destino) {
// Process each item
foreach ( $destino->programas->item as $item ) {
// Set the location from the destino location value
$item->location = (string)$destino->location;
}
}
header('Content-Type: application/xml');
echo $xml->asXML();
One thing to note is that when using SimpleXML, the root node (<destinos> in this case) is the $xml object. This is why $xml->destino is accessing the <destino> elements.
With DOM you can append clones of the location nodes to the respective item elements.
$document = new DOMDocument();
$document->load($url);
$xpath = new DOMXpath($document);
// iterate the location child of the destino elements
foreach($xpath->evaluate('//destino/location') as $location) {
// iterate the item nodes inside the same parent node
foreach ($xpath->evaluate('parent::*/programas/item', $location) as $item) {
// append a copy of the location to the item
$item->appendChild($location->cloneNode(TRUE));
}
}
echo $document->saveXML();

php SimpleXml and xpath: How to acces children inside node that have other namespace?

I am trying to loop through a set of records in an XML document, but cannot access subnodes that are in other namespaces.
this is my php code:
$xml = new SimpleXMLElement($xml);
$ns = $xml->getNamespaces(true);
$xml->registerXPathNamespace('ns', $ns['']);
$xmlRecords = $xml->xpath('//ns:recordData');
foreach ($xmlRecords as $record)
{
$enrichedData = $record->gzd->enrichedData; //Yes! Works!
$originalData = $record->gzd->originalData; //empty
}
The problem is that $orginalData remains empty, probably because al the subnodes inside node originalData belong to other namespaces. My question is: how do I get these subnodes available in my records loop? I need all the nodes that are under the dcterms namespace.
Here is the source xml:
<?xml version="1.0" encoding="UTF-8"?>
<searchRetrieveResponse xmlns="http://www.loc.gov/zing/srw/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/zing/srw/ srw-types.xsd">
<version>1.2</version>
<numberOfRecords>23</numberOfRecords>
<records>
<record>
<recordSchema>http://standaarden.overheid.nl/sru/</recordSchema>
<recordPacking>xml</recordPacking>
<recordData>
<gzd xmlns="http://standaarden.overheid.nl/sru" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:overheid="http://standaarden.overheid.nl/owms/terms/" xmlns:overheidrg="http://standaarden.overheid.nl/cvdr/terms/" xsi:schemaLocation="http://standaarden.overheid.nl/sru gzd.xsd">
<originalData>
<overheidrg:meta>
<owmskern>
<dcterms:identifier>123431_2</dcterms:identifier>
<dcterms:title>Ligplaatsenverordening 2009</dcterms:title>
<dcterms:language>nl</dcterms:language>
<dcterms:type scheme="overheid:Informatietype">regeling</dcterms:type>
<dcterms:creator scheme="overheid:Gemeente">Muiden</dcterms:creator>
<dcterms:modified>2015-08-27</dcterms:modified>
</owmskern>
<owmsmantel>
<dcterms:isFormatOf resourceIdentifier="">Onbekend</dcterms:isFormatOf>
<dcterms:alternative>Ligplaatsenverordening 2009</dcterms:alternative>
<dcterms:source resourceIdentifier="">artikel 149 van de Gemeentewet</dcterms:source>
<dcterms:isRatifiedBy scheme="overheid:BestuursorgaanGemeente">gemeenteraad</dcterms:isRatifiedBy>
<dcterms:subject>ruimtelijke ordening, verkeer en vervoer</dcterms:subject>
<dcterms:issued>2012-12-20</dcterms:issued>
</owmsmantel>
<cvdripm>
<overheidrg:inwerkingtredingDatum>2013-01-01</overheidrg:inwerkingtredingDatum>
<overheidrg:uitwerkingtredingDatum />
<overheidrg:betreft>Onbekend</overheidrg:betreft>
<overheidrg:kenmerk>Onbekend</overheidrg:kenmerk>
<overheidrg:onderwerp />
<overheidrg:gedelegeerdeRegelgeving><al >Geen</al></overheidrg:gedelegeerdeRegelgeving>
<overheidrg:redactioneleToevoeging><al >Geen</al></overheidrg:redactioneleToevoeging>
</cvdripm>
</overheidrg:meta>
</originalData>
<enrichedData>
<organisatietype>Gemeente</organisatietype>
<publicatieurl_xhtml>http://decentrale.regelgeving.overheid.nl/cvdr/xhtmloutput/Historie/Muiden/123431/123431_2.html</publicatieurl_xhtml>
<publicatieurl_xml>http://decentrale.regelgeving.overheid.nl/cvdr/xmloutput/Historie/Muiden/123431/123431_2.xml</publicatieurl_xml>
</enrichedData>
</gzd>
</recordData>
<recordPosition>1</recordPosition>
</record>
</records>
</searchRetrieveResponse>
You can use the SimpleXMLElement children method with the corresponding namespaces like children($ns['overheidrg']) and children($ns['dcterms']).
For example:
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<searchRetrieveResponse xmlns="http://www.loc.gov/zing/srw/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/zing/srw/ srw-types.xsd">
<version>1.2</version>
<numberOfRecords>23</numberOfRecords>
<records>
<record>
<recordSchema>http://standaarden.overheid.nl/sru/</recordSchema>
<recordPacking>xml</recordPacking>
<recordData>
<gzd xmlns="http://standaarden.overheid.nl/sru" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:overheid="http://standaarden.overheid.nl/owms/terms/" xmlns:overheidrg="http://standaarden.overheid.nl/cvdr/terms/" xsi:schemaLocation="http://standaarden.overheid.nl/sru gzd.xsd">
<originalData>
<overheidrg:meta>
<owmskern>
<dcterms:identifier>123431_2</dcterms:identifier>
<dcterms:title>Ligplaatsenverordening 2009</dcterms:title>
<dcterms:language>nl</dcterms:language>
<dcterms:type scheme="overheid:Informatietype">regeling</dcterms:type>
<dcterms:creator scheme="overheid:Gemeente">Muiden</dcterms:creator>
<dcterms:modified>2015-08-27</dcterms:modified>
</owmskern>
<owmsmantel>
<dcterms:isFormatOf resourceIdentifier="">Onbekend</dcterms:isFormatOf>
<dcterms:alternative>Ligplaatsenverordening 2009</dcterms:alternative>
<dcterms:source resourceIdentifier="">artikel 149 van de Gemeentewet</dcterms:source>
<dcterms:isRatifiedBy scheme="overheid:BestuursorgaanGemeente">gemeenteraad</dcterms:isRatifiedBy>
<dcterms:subject>ruimtelijke ordening, verkeer en vervoer</dcterms:subject>
<dcterms:issued>2012-12-20</dcterms:issued>
</owmsmantel>
<cvdripm>
<overheidrg:inwerkingtredingDatum>2013-01-01</overheidrg:inwerkingtredingDatum>
<overheidrg:uitwerkingtredingDatum />
<overheidrg:betreft>Onbekend</overheidrg:betreft>
<overheidrg:kenmerk>Onbekend</overheidrg:kenmerk>
<overheidrg:onderwerp />
<overheidrg:gedelegeerdeRegelgeving><al >Geen</al></overheidrg:gedelegeerdeRegelgeving>
<overheidrg:redactioneleToevoeging><al >Geen</al></overheidrg:redactioneleToevoeging>
</cvdripm>
</overheidrg:meta>
</originalData>
<enrichedData>
<organisatietype>Gemeente</organisatietype>
<publicatieurl_xhtml>http://decentrale.regelgeving.overheid.nl/cvdr/xhtmloutput/Historie/Muiden/123431/123431_2.html</publicatieurl_xhtml>
<publicatieurl_xml>http://decentrale.regelgeving.overheid.nl/cvdr/xmloutput/Historie/Muiden/123431/123431_2.xml</publicatieurl_xml>
</enrichedData>
</gzd>
</recordData>
<recordPosition>1</recordPosition>
</record>
</records>
</searchRetrieveResponse>
XML;
$xml = new SimpleXMLElement($xml);
$ns = $xml->getNamespaces(true);
$xml->registerXPathNamespace('ns', $ns['']);
$xmlRecords = $xml->xpath('//ns:recordData');
foreach ($xmlRecords as $record)
{
$enrichedData = $record->gzd->enrichedData; //Yes! Works!
$originalData = $record
->gzd
->originalData
->children($ns['overheidrg'])
->meta
->children();
foreach ($originalData as $originalDataItem) {
// Get the children using the namespace 'dcterms'
$children = $originalDataItem->children($ns['dcterms']);
// Check for the name of the xml element
if ($originalDataItem->getName() === "owmskern") {
$identifier = $children->identifier->__toString();
// etc...
}
if ($originalDataItem->getName() === "owmsmantel") {
$isFormatOf = $children->isFormatOf->__toString();
// etc...
}
}
}

both appendChild and insertBefore when called on DOMDocument append node after root

I have a function to add a valueless element and a bunch of child elements with values to an xml file, so I wrote it to take two arguments $tag(String the parent node) and $hash(Array $elm=>$elm_value)
function addUser($tag, $hash)
{
$dom = new DOMDocuemnt();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('file');
$parent = $dom->createElement($tag);
foreach($hash as $elm => $value){
$n = $dom->createElement($elm, $value);
$parent->appendChild($n);
}
$dom->appendChild($parent);
$dom->save('file');
return $dom->saveXML();
}
Only problem is $dom->appendChild($parent) appends everything after the root element's closing tag, mucking up my xml file. So I tried $dom->insertBefore($parent) with the same result. So instead I tried $xpath = new DOMXPath($dom); $root = $xpath->query('/')->item(0); $root->appendChild($parent);. Same result. Then I tried selecting the root element with $dom->getElementsByTagName(name of root)->item(0); And was surprised when that actually worked! But what if I don't know the tag name? Is there another way to select the root element so that calling appendChild or inserBefore will add the element before the root closing tag instead of after it?
This seems to work:
Initial XML file -
<!-- test.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node Id="1">
<Clicks>click1</Clicks>
</node>
</root>
PHP -
<?php
function addUser($tag, $hash) {
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('test.xml');
$parent = $dom->createElement($tag);
$dom->documentElement->appendChild($parent);
foreach($hash as $elm => $value){
$n = $dom->createElement($elm);
$n->appendChild( $dom->createTextNode( $value ) );
$parent->appendChild($n);
}
$dom->save('test.xml');
}
$arr = array( 'name' => 'pushpesh', 'age' => 30, 'profession' => 'SO bugger' );
addUser('user', $arr);
XML file is now -
<!-- test.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node Id="1">
<Clicks>click1</Clicks>
</node>
<user>
<name>pushpesh</name>
<age>30</age>
<profession>SO bugger</profession>
</user>
</root>
Hope this helps.

Parse KML file with PHP

Is there a way to parse google maps *.kml file with simple_xml_load_file("*.kml") ?
I need to save in my database name and coordinates of each polygons registered in my KML file.
On my PHP script, simple_xml_load_file("*.kml") return false, so I can't read it.
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<Schema>
...
</Schema>
<Style id="FEATURES">
...
</Style>
<Folder>
<Placemark>
<name>
name
</name>
<Polygon>
<LinearRing>
<coordinates>
coordinates
</coordinates>
</LinearRing>
</Polygon>
</Placemark>
<Placemark>
...
</Placemark>
</Folder>
</Document>
</kml>
I need "name" and "coordinates" values for each "Placemark".
The xml structure is exactly that xml you sent:
For example:
<Document>
<Placemark>
<name>356HH</name>
<description>
</description>
<Polygon><outerBoundaryIs><LinearRing><coordinates>some cordinates here</coordinates></LinearRing></innerBoundaryIs></Polygon>
<Style><LineStyle><color>ff0000ff</color></LineStyle> <PolyStyle><fill>0</fill></PolyStyle></Style>
</Placemark>
<Placemark>
</document>
And it's my php code:
<?php
$completeurl = "2.xml";
$xml = simplexml_load_file($completeurl);
$placemarks = $xml->Document->Placemark;
$con=mysqli_connect("localhost","root","","j3");
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$query = '';
$run='';
for ($i = 0; $i < sizeof($placemarks); $i++) {
$coordinates = $placemarks[$i]->name;
$cor_d = explode(' ', $placemarks[$i]->Polygon->outerBoundaryIs->LinearRing->coordinates);
$qtmp=array();
foreach($cor_d as $value){
$tmp = explode(',',$value);
$ttmp=$tmp[1];
$tmp[1]=$tmp[0];
$tmp[0]=$ttmp;
$qtmp[]= '(' . $tmp[0] . ',' .$tmp[1].')';
}
$cor_d = json_encode($qtmp);
$query .='\''.$coordinates.'\', \''.$cor_d.'\'';
$run .="INSERT INTO jos_rpl_addon_zipinfo (name, boundary) VALUES (".$query." );";
//echo $run;
//break;
}
mysqli_query($con,$run);
//echo $run;
mysqli_close($con);
?>
The first line:
<?xml version="1.0" encoding="UTF-8"?>
Tells PHP that this is a document encoded in UTF-8, but your error says it is not encoded as UTF-8. Is this a doc you created with a text editor? If so, you can usually use your editor to save it out in UTF-8. Or you can probably use PHP to detect the encoding and change that first line.
If the XML strucutre is as what you have posted, you can try :-
$xml = simplexml_load_file(...);
$childs = $xml->Document->Folder->children();
foreach ($childs as $child)
{
// child object is same as -> Placemark
}
Example :-
SimpleXMLElement Object
(
[name] =>
name
[Polygon] => SimpleXMLElement Object
(
[LinearRing] => SimpleXMLElement Object
(
[coordinates] =>
coordinates
)
)
)

PHP - SimpleXML - AddChild with another SimpleXMLElement

I'm trying to build a rather complex XML document.
I have a bunch of sections of the XML document that repeats. I thought I'd use multiple string templates as base document for the sections and create instances of XML elements using simplexml_load_string.
So I have one instance of SimpleXMLElement as the base document
$root =
simplexml_load_string($template_root);
then I loop through some items in my database, create new SimpleXMLElement, something like this:
for (bla bla bla):
$item = simplexml_load_string($template_item);
// do stuff with item
// try to add item to the root document..
// Stuck here.. can't do $root->items->addChild($item)
endfor;
I can't call addChild because it just expects a tag name and value.. you can't addChild another SimpleXMLElement.
Am I missing something here? seems really dumb that addChild can't take a SimpleXMLELement as a parameter.
Is there any other way to do this? (apart from using a different xml lib)
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.
You could use this function that is based in creating the children with attributes from the source:
function xml_adopt($root, $new) {
$node = $root->addChild($new->getName(), (string) $new);
foreach($new->attributes() as $attr => $value) {
$node->addAttribute($attr, $value);
}
foreach($new->children() as $ch) {
xml_adopt($node, $ch);
}
}
$xml = new SimpleXMLElement("<root/>");
$child = new SimpleXMLElement("<content><p a=\"aaaaaaa\">a paragraph</p><p>another <br/>p</p></content>");
xml_adopt($xml, $child);
echo $xml->asXML()."\n";
This will produce:
<?xml version="1.0"?>
<root><content><p a="aaaaaaa">a paragraph</p><p>another p<br/></p></content></root>
The xml_adopt() example doesn't preserve namespace nodes.
My edit was rejected because it changed to much? was spam?.
Here is a version of xml_adopt() that preserves namespaces.
function xml_adopt($root, $new, $namespace = null) {
// first add the new node
// NOTE: addChild does NOT escape "&" ampersands in (string)$new !!!
// replace them or use htmlspecialchars(). see addchild docs comments.
$node = $root->addChild($new->getName(), (string) $new, $namespace);
// add any attributes for the new node
foreach($new->attributes() as $attr => $value) {
$node->addAttribute($attr, $value);
}
// get all namespaces, include a blank one
$namespaces = array_merge(array(null), $new->getNameSpaces(true));
// add any child nodes, including optional namespace
foreach($namespaces as $space) {
foreach ($new->children($space) as $child) {
xml_adopt($node, $child, $space);
}
}
}
(edit: example added)
$xml = new SimpleXMLElement(
'<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
<channel></channel></rss>');
$item = new SimpleXMLElement(
'<item xmlns:media="http://search.yahoo.com/mrss/">
<title>Slide Title</title>
<description>Some description</description>
<link>http://example.com/img/image.jpg</link>
<guid isPermaLink="false">A1234</guid>
<media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
</media:content>
</item>');
$channel = $xml->channel;
xml_adopt($channel, $item);
// output:
// Note that the namespace is (correctly) only preserved on the root element
'<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<item>
<title>Slide Title</title>
<description>Some description</description>
<link>http://example.com/img/image.jpg</link>
<guid isPermaLink="false">A1234</guid>
<media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
</media:content>
</item>
</channel>
</rss>'

Categories