How to move childnode to parentnode level in xml ? - php

Recently I have been working with the xml coding,and the problem happened.
Given the xml like this:
<Colonel id="1">
<Lieutenant_Colonel id="2">
<Secretary id="6"/>
</Lieutenant_Colonel>
<Lieutenant_Colonel id="3">
<Secretary id="7"/>
</Lieutenant_Colonel>
<Secretary id="5"/>
</Colonel>
now the Colonel(id=1) has gone with the Secretary(id=5)
the xml we want is like
<!-- <Colonel id="1"> -->
<Lieutenant_Colonel id="2">
<Secretary id="6"/>
</Lieutenant_Colonel>
<Lieutenant_Colonel id="3">
<Secretary id="7"/>
</Lieutenant_Colonel>
<!--
<Secretary id="5"/>
</Colonel> -->
or
<Lieutenant_Colonel id="2">
<Secretary id="6"/>
</Lieutenant_Colonel>
<Lieutenant_Colonel id="3">
<Secretary id="7"/>
</Lieutenant_Colonel>
How to do this work?
please help me

This is a pretty long them, so I´ll have to refer other questions. First, consider this function:
function RemoveColonel($id, $secretaryId)
{
$colXpath = '//colonel[#id="' . $id . '"]';
$secXpath = $colXpath . '/Secretary[#id="' . $secretaryId . '"]';
RemoveNode($secXpath);
$colChildren = $rootXml->xpath($colXpath)->children();
$children = array();
foreach ($colChildren as $child ){
$children[] = $child;
}
RemoveNode($colXpath);
return $children;
}
Given the Id of a colonel and a secretary for that colonel, it deletes the secretary, then it saves the colonel's children to remove the colonel and return the children. After that, you can insert your elements in the root node again if you please. If you can't have the secretary Id at the moment of deleting the colonel, you can workaround the xpath to remove the secretary as child of the colonel by removing the id specification.
The RemoveNode code is a bit long, so I'll have to give you this question on how to remove nodes using SimpleXML in PHP.
I hope I can be of some help!

Related

Delete an item by id in XML with PHP

I want to delete a node by id in XML my code is this:
$id=$_GET["id"];
$usuarios= simplexml_load_file('cart.xml');
if($_GET["action"] == "delete"){
foreach($usuarios->carro as $elemento){
if($elemento['id'] == $id) {
echo '<script>alert("delete")</script>';
unset($elemento['id']);
}
}
}
but doesn't work.
My XML cart.xml:
<?xml version="1.0"?>
<info>
<carro id="0">
<usuario>alex</usuario>
<producto>instict</producto>
<Size>CH</Size>
<cantidad>1</cantidad>
<precio>100</precio>
</carro>
<carro id="1">
<usuario>alex</usuario>
<producto>instict</producto>
<Size>G</Size>
<cantidad>1</cantidad>
<precio>100</precio>
</carro>
<carro id="2">
<usuario>alex</usuario>
<producto>instict</producto>
<Size>G</Size>
<cantidad>1</cantidad>
<precio>100</precio>
</carro>
<carro id="3">
<usuario>Gera</usuario>
<producto>instict</producto>
<Size>M</Size>
<cantidad>1</cantidad>
<precio>100</precio>
</carro>
</info>
I want to delete for example id="2" so the output would be:
<?xml version="1.0"?>
<info>
<carro id="0">
<usuario>alex</usuario>
<producto>instict</producto>
<Size>CH</Size>
<cantidad>1</cantidad>
<precio>100</precio>
</carro>
<carro id="1">
<usuario>alex</usuario>
<producto>instict</producto>
<Size>G</Size>
<cantidad>1</cantidad>
<precio>100</precio>
</carro>
<carro id="3">
<usuario>Gera</usuario>
<producto>instict</producto>
<Size>M</Size>
<cantidad>1</cantidad>
<precio>100</precio>
</carro>
</info>
Change
$usuarios->carro as $key => $elemento
and then
unset($usuarios->carro[$key]);
and then write result in new file as string
file_put_contents('newfile.xml', $usuarios->asXML());

XML PHP Get children value

XML:
<Result xmlns="" xmlns:xsi="" totalResultsAvailable="0" totalResultsReturned="0" schk="true" totalLooseOffers="0" xsi:schemaLocation="">
<details>
<ID></ID>
<applicationVersion>1.0</applicationVersion>
<applicationPath/>
<date>2016-05-23T12:17:16.369-03:00</date>
<elapsedTime>17</elapsedTime>
<status>success</status>
<message>success</message>
</details>
<category id="1">
<thumbnail url="http://image.google.com/test.jpg"/>
<links>
<link url="www.google.com" type="category"/>
<link url="www.google2.com" type="xml"/>
</links>
<name>Category</name>
<filters>
<filter id="1" name="Filter1">
<value id="1" value="Test1"/>
<value id="2" value="Test2"/>
<value id="3" value="Test3"/>
</filter>
<filter id="2" name="Filter2">
<value id="1" value="Test4"/>
<value id="2" value="Test5"/>
<value id="3" value="Test6"/>
</filter>
</filters>
</category>
</Result>
PHP:
$xml = simplexml_load_file("http://xml.com");
foreach($xml->category->filters as $filters){
foreach($filters->children() as $child){
echo $child['value'];
}
}
I'm trying to get the filters value, but nothing shows with the code i have. I saw something about xpath but don't know if it's applicable in this situation. Do you have any clue?
--
When the XML looks like this:
<Result xmlns="" xmlns:xsi="" totalResultsAvailable="0" totalResultsReturned="0" schk="true" totalLooseOffers="0" xsi:schemaLocation="">
<details>
<ID></ID>
<applicationVersion>1.0</applicationVersion>
<applicationPath/>
<date>2016-05-23T12:17:16.369-03:00</date>
<elapsedTime>17</elapsedTime>
<status>success</status>
<message>success</message>
</details>
<subCategory id="1">
<thumbnail url="http://image.google.com/test.jpg"/>
<name>Subcategory</name>
</subCategory>
<subCategory id="2">
<thumbnail url="http://image.google.com/test2.jpg"/>
<name>Subcategory2</name>
</subCategory>
</Result>
Then am able to do this:
foreach($xml->subCategory as $subCategory){
$categoryId = $subCategory['id'];
$categoryName = $subCategory->name;
}
The elements you reference as $child in the inner loop actually point to the <filter> nodes, not the children <value> nodes you are attempting to target attributes for. So this really is just a matter of extending the outer foreach loop to iterate over $xml->category->filters->filter rather than its parent $xml->category->filters.
// Iterate the correct <filter> node, not its parent <filters>
foreach ($xml->category->filters->filter as $filter) {
foreach($filter->children() as $child){
echo $child['value'] . "\n";
}
}
Here it is in demonstration: https://3v4l.org/Rqc4Y
Using xpath, you can target the inner nodes directly.
$values = $xml->xpath('//category/filters/filter/value');
foreach ($values as $value) {
echo $value['value'];
}
https://3v4l.org/vPhKE
Both of these examples output
Test1
Test2
Test3
Test4
Test5
Test6

XML to MySQL when xml file has multiple matching fields

I've been doing some work on an XML to Mysql using load XML. I have been successful with itin the past. The difference with the latest effort is that we have multiple occurrences of a field-name in the MySQL. A sample of this is below:
<row>
<pictures>
<picture name="Photo 1">
<filename>image1.jpg</filename>
</picture>
<picture name="Photo 2">
<filename>image2.jpg</filename>
</picture>
<picture name="Photo 4">
<filename>image3.jpg</filename>
</picture>
<picture name="Photo 3">
<filename>image4.jpg</filename>
</picture>
<picture name="Photo 7">
<filename>image5.jpg</filename>
</picture>
<picture name="Photo 6">
<filename>image6.jpg</filename>
</picture>
<picture name="Photo 5">
<filename>image7.jpg</filename>
</picture>
<picture name="Photo 8">
<filename>image8.jpg</filename>
</picture>
<picture name="Photo 9">
<filename>image9.jpg</filename>
</picture>
</pictures>
</row>
I need to import this into a MySQL table with the fields:
picture1
picture2
picture3
picture4
picture5
picture6
picture7
picture8
picture9
As you can see, the 'name' attribute doesn't necessarily occur in the correct order, so I need them to simply be inserted in order. So the first <filename> to go to picture1, the second <filename> to picture2 etc..
What is currently being achieved is that I always end up with the last <picture> entry in the list being in the table. This is I assume because the filed is being overwritten each time.
Any ideas how to achieve this? I have found similar queries to this but no answers as yet and have been looking for a good while. The rest of the file is loading fine as they have unique field-names and can easily be mapped to a MySQL column, but I am struggling with this one.
As the XML does not match the format you aim for you need to transform it first. Traditionally this is done with XSLT but you can also do this with XMLReader and XMLWriter in PHP which has the benefit that it does not require to keep the whole XML document(s) in memory.
The XMLReaderIterator package has support for such operations, an example is already given with the library.
Creating a modification of that example code by taking your specific case and an exemplary input file named pictures.xml and keeping the output to the standard-output for demonstration purposes allows me to quote the following excerpt:
[... starts like examples/read-write.php]
/** #var $iterator XMLWritingIteration|XMLReaderNode[] */
$iterator = new XMLWritingIteration($writer, $reader);
$writer->startDocument();
$rename = ['row' => 'resultset', 'pictures' => 'row'];
$trimLevel = null;
$pictureCount = null;
foreach ($iterator as $node) {
$name = $node->name;
$isElement = $node->nodeType === XMLReader::ELEMENT;
$isEndElement = $node->nodeType === XMLReader::END_ELEMENT;
$isWhitespace = $node->nodeType === XMLReader::SIGNIFICANT_WHITESPACE;
if (($isElement || $isEndElement) && $name === 'filename') {
// drop <filename> opening and closing tags
} elseif ($isElement && $name === 'picture') {
$writer->startElement('field');
$writer->writeAttribute('name', sprintf('picture%d', ++$pictureCount));
$trimLevel = $node->depth;
} elseif ($trimLevel && $isWhitespace && $node->depth > $trimLevel) {
// drop (trim) SIGNIFICANT_WHITESPACE
} elseif ($isElement && isset($rename[$name])) {
$writer->startElement($rename[$name]);
if ($rename[$name] === 'row') {
$pictureCount = 0;
}
} else {
$iterator->write();
}
}
This is one XMLWritingIteration that is composed of an XMLReader and XMLWriter object. That iteration allows you to take over everything from the input document (via $iterator->write()) and do the needed changes only on occasions:
drop the <filename> and </filename> tags
create <field> elements with the correct name attributes to have the pictures in document order (Mysql XML nomenclature)
drop significant whitespace as <filename> tags are dropped as well
rename the document element from <row> to <resultset> (Mysql XML nomenclature)
rename the <pictures> element to <row> (again Mysql XML nomenclature)
the counter for the picture fields is reset per each (output) row
everything else is kept as-is
Such a transformation results in the following example output with the XML presented in your question:
<?xml version="1.0"?>
<resultset>
<row>
<field name="picture1">image1.jpg</field>
<field name="picture2">image2.jpg</field>
<field name="picture3">image3.jpg</field>
<field name="picture4">image4.jpg</field>
<field name="picture5">image5.jpg</field>
<field name="picture6">image6.jpg</field>
<field name="picture7">image7.jpg</field>
<field name="picture8">image8.jpg</field>
<field name="picture9">image9.jpg</field>
</row>
</resultset>
For more information about the XML format used by Mysql, please see the Mysql documentation for the --xml commandline switch which describes the standard XML output format which can be read in by LOAD XML.
For this little example you could as well use XSLT as there would be no problem to do the whole transformation in memory. But if you need to look for memory (which can happen if you deal with XML database dumps), the XMLWritingIteration allows iteration based XML transformation with an XML Pull parser (XMLReader) and forward-only XML output via XMLWriter.
And here is the XSLT solution. As information, XSLT is a declarative special-purpose language to transform, re-style, and restructure XML documents in various formats for end use purposes. PHP maintains an XSLT processor. Be sure to uncomment out extension=php_xsl.dll
XLST (accommodates image numbers greater than two digits)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8"/>
<xsl:template name="picturesort" match="pictures" >
<row>
<pictures>
<xsl:for-each select="picture">
<xsl:variable name="numkey"
select="substring-after(substring-before(filename, '.'), 'e')"/>
<picture name="{../picture[substring-after(#name, ' ') = $numkey]/#name}">
<xsl:copy-of select="filename"/>
</picture>
</xsl:for-each>
</pictures>
</row>
</xsl:template>
</xsl:stylesheet>
XML OUTPUT
<?xml version="1.0" encoding="UTF-8"?>
<row>
<pictures>
<picture name="Photo 1">
<filename>image1.jpg</filename>
</picture>
<picture name="Photo 2">
<filename>image2.jpg</filename>
</picture>
<picture name="Photo 3">
<filename>image3.jpg</filename>
</picture>
<picture name="Photo 4">
<filename>image4.jpg</filename>
</picture>
<picture name="Photo 5">
<filename>image5.jpg</filename>
</picture>
<picture name="Photo 6">
<filename>image6.jpg</filename>
</picture>
<picture name="Photo 7">
<filename>image7.jpg</filename>
</picture>
<picture name="Photo 8">
<filename>image8.jpg</filename>
</picture>
<picture name="Photo 9">
<filename>image9.jpg</filename>
</picture>
</pictures>
</row>
PHP
<?php
// Load the XML source
$xml = new DOMDocument;
$xml->load('C:/Path/To/XMLfile.xml');
$xsl = new DOMDocument;
$xsl->load('C:/Path/To/XSLfile.xsl');
// Configure the transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// Transform XML source
$newXml = $proc->transformToXML($xml);
echo $newXml;
// Save output to file
file_put_contents("C:/Path/To/NewXMLfile.xml", $newXml);
?>
Possible way:
iterate over all <picture> in a <row>
build an associative array with key = name and value = filename
sort array by keys
feed the array to your DB

PHP - Parse XML contained within XML element and Output into XML doc

I'm looking to parse out only the content of the element below into its own XML document, but am unsure of the proper PHP method to use. XML data in boxb.php is unable to be modified.
EX:
Parsing code:
<?php
include 'boxb.php';
$boxb = new SimpleXMLElement($xmlstr);
$boxb->ad[0]->content;
echo $boxb->ad[0]->content;
?>
boxb.php contains the following:
<?php
$xmlstr = <<<XML
<boxb>
<ad type="agnostic_template">
<url><![CDATA[http://ads.cookie.com/8/redir/1db04901-225e-11e4-86f3-bc305bf4914b/0/632361]]></url>
<track />
<content>
<VAST version="2.0">
<Ad id="228">
<InLine>
<AdSystem version="4.11.0-10">LiveRail</AdSystem>
<AdTitle><![CDATA[TV Overlay PNG]]></AdTitle>
<Description />
<Error><![CDATA[http://t4.liverail.com/?metric=error&erc=[ERRORCODE]&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=&y=&xy=&redirect=]]></Error>
<Impression id="LR"><![CDATA[http://t4.liverail.com/?metric=impression&cofl=0&flid=0&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=29&y=29&xy=9dae&z2=0.00000]]></Impression>
<Impression id="QC"><![CDATA[http://pixel.quantserve.com/pixel/p-d05JkuPGiy-jY.gif?r=6662]]></Impression>
<Impression id="CS"><![CDATA[http://b.scorecardresearch.com/p?c1=1&c2=9864668&c3=1331&c4=&c5=09]]></Impression>
<Impression><![CDATA[http://load.exelator.com/load/?p=104&g=440&j=0]]></Impression>
<Impression><![CDATA[http://navdmp.com/usr?vast=http%3A%2F%2Ft4.liverail.com%2F%3Fmetric%3Dmsync%26p%3D78]]></Impression>
<Impression><![CDATA[http://pixel.tapad.com/idsync/ex/receive?partner_id=LIVERAIL&partner_device_id=97838239447]]></Impression>
<Impression><![CDATA[http://t4.liverail.com/?metric=rsync&p=3016&redirect=http%3A%2F%2Fliverail2waycm-atl.netmng.com%2Fcm%2F%3Fredirect%3Dhttp%253A%252F%252Ft4.liverail.com%252F%253Fmetric%253Dcsync%2526p%253D3016%2526s%253D(NM-UserID)]]></Impression>
<Impression><![CDATA[http://t4.liverail.com/?metric=rsync&p=3017&redirect=http%3A%2F%2Fm.xp1.ru4.com%2Factivity%3F_o%3D62795%26_t%3Dcm_rail]]></Impression>
<Impression><![CDATA[http://n.us1.dyntrk.com/adx/lr/sync_lr.php?lrid=97838239447]]></Impression>
<Creatives>
<Creative sequence="1" id="8455">
<NonLinearAds>
<NonLinear width="300" height="60">
<StaticResource creativeType="image/png"><![CDATA[http://cdn.liverail.com/adasset/228/8455/overlay.png]]></StaticResource>
<NonLinearClickThrough><![CDATA[http://t4.liverail.com/?metric=clickthru&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=&y=&xy=&redirect=http%3A%2F%2Fwww.liverail.com]]></NonLinearClickThrough>
</NonLinear>
<TrackingEvents>
<Tracking event="acceptInvitation"><![CDATA[http://t4.liverail.com/?metric=accept&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=&y=&xy=]]></Tracking>
<Tracking event="collapse"><![CDATA[http://t4.liverail.com/?metric=minimize&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=&y=&xy=]]></Tracking>
</TrackingEvents>
</NonLinearAds>
</Creative>
<Creative sequence="1" id="8455">
<CompanionAds>
<Companion width="300" height="60">
<StaticResource creativeType="image/jpeg"><![CDATA[http://cdn.liverail.com/adasset/228/8455/300x60.jpg]]></StaticResource>
<TrackingEvents>
<Tracking event="creativeView"><![CDATA[http://t4.liverail.com/?metric=companion&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=&y=&xy=]]></Tracking>
</TrackingEvents>
<CompanionClickThrough><![CDATA[http://t4.liverail.com/?metric=cclickthru&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=&y=&xy=&redirect=http%3A%2F%2Fwww.liverail.com]]></CompanionClickThrough>
</Companion>
<Companion width="300" height="250">
<StaticResource creativeType="image/jpeg"><![CDATA[http://cdn.liverail.com/adasset/228/8455/300x250.jpg]]></StaticResource>
<TrackingEvents>
<Tracking event="creativeView"><![CDATA[http://t4.liverail.com/?metric=companion&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=&y=&xy=]]></Tracking>
</TrackingEvents>
<CompanionClickThrough><![CDATA[http://t4.liverail.com/?metric=cclickthru&pos=1&coid=135&pid=1331&nid=1331&oid=228&olid=2281331&cid=8455&tpcid=&vid=&amid=&cc=default&pp=&vi=0&vv=&sg=&tsg=&pmu=0&pau=0&psz=0&ctx=&tctx=&coty=7&adt=0&did=&buid=&scen=&mca=&mma=&mct=0&url=http%3A%2F%2Fwww.iab.net%2Fguidelines%2F508676%2Fdigitalvideo%2Fvast%2Fvast_xml_samples&trid=53ea9957dc20a5.06241648&bidf=0.00000&bids=0.00000&bidt=1&bidh=0&bidlaf=0&cb=6662.66.104.228.162.0&ver=1&w=&wy=&x=&y=&xy=&redirect=http%3A%2F%2Fwww.liverail.com]]></CompanionClickThrough>
</Companion>
</CompanionAds>
</Creative>
</Creatives>
<Extensions />
</InLine>
</Ad>
</VAST>
<!-- 321 US_NEWJERSEY_NEWYORK_METUCHEN 08840 -->
</content>
</ad>
</boxb>
XML;
?>
If you want to get the XML-data, use asXML(),
echo $boxb->ad[0]->content->asXML();
And if you want to create your own XML document, you could use for example,
$myXML = new SimpleXMLElement($boxb->ad[0]->content->asXML());
echo $myXML->asXML();
Which would echo,
<!--?xml version="1.0"?-->
<content>
...
</content>
<?php
include 'boxb.php';
// Load string and parse as XML
$boxb = simplexml_load_string($xmlstr);
// Extract "content" element from loaded XML
$content = $boxb->ad->content;
// Convert extracted info into XML
$new_xml = $content->asXML();
// Send a header tag to the browser, stating that this info is XML
header('Content-type: application/XML');
// Print the actual XML
echo $new_xml;
?>

Why is my recursive loop creating too many children?

I'm using a PHP recursive loop to parse through an XML document to create a nested list, however for some reason the loop is broken and creating duplicates of elements within the list, as well as blank elements.
The XML (a list of family tree data) is structured as follows:
<?xml version="1.0" encoding="UTF-8"?>
<family>
<indi>
<id>id1</id>
<fn>Thomas</fn>
<bday></bday>
<dday></dday>
<spouse></spouse>
<family>
<indi>
<id>id1</id>
<fn>Alexander</fn>
<bday></bday>
<dday></dday>
<spouse></spouse>
<family>
</family>
</indi>
<indi>
<id>id1</id>
<fn>John</fn>
<bday></bday>
<dday></dday>
<spouse></spouse>
<family>
<indi>
<id>id1</id>
<fn>George</fn>
<bday></bday>
<dday></dday>
<spouse></spouse>
<family>
</family>
</indi>
</family>
</indi>
</family>
</indi>
</family>
And here's my PHP loop, which loads the XML file then loops through it to create a nested ul:
<?php
function outputIndi($indi) {
echo '<li>';
$id = $indi->getElementsByTagName('id')->item(0)->nodeValue;
echo '<span class="vcard person" id="' . $id . '">';
$fn = $indi->getElementsByTagName('fn')->item(0)->nodeValue;
$bday = $indi->getElementsByTagName('bday')->item(0)->nodeValue;
echo '<span class="edit fn">' . $fn . '</span>';
echo '<span class="edit bday">' . $bday . '</span>';
// ...
echo '</span>';
echo '<ul>';
$family = $indi->getElementsByTagName('family');
foreach ($family as $subIndi) {
outputIndi($subIndi);
}
echo '</ul></li>';
}
$doc = new DOMDocument();
$doc->load('armstrong.xml');
outputIndi($doc);
?>
EDIT here's the desired outcome (nested lists, with ul's signifying families and li's signifying individuals)
<ul>
<li>
<span class="vcard">
<span class="fn">Thomas</span>
<span class="bday"></span>
<span class="dday"></span>
<ul>
... repeat for all ancestors ...
</ul>
<li>
<ul>
You can see the output at http://chris-armstrong.com/gortin . Any ideas where I'm going wrong? I think it's something to do with the $subIndi value, but anytime I try and change it I get an error. Would really appreciate any help!
Sounds perfect! Could you give me an
example? Does this mean I can save the
data as XML, then load it in as nested
ul's?
Yes, you can do exactly that. Here's an XSL which renders nested UL's:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Family tree</h2>
<ul>
<li><xsl:value-of select="indi/fn" /></li>
<!-- apply-templates will select all the indi/family nodes -->
<xsl:apply-templates select="indi/family" />
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="family">
<ul>
<li>
<div>
<xsl:value-of select="id" />: <xsl:value-of select="fn" />
(<xsl:variable name="bday" select="bday" />
to
<xsl:variable name="dday" select="dday" />)
</div>
</li>
<!-- This node matches the 'family' nodes, and we're going to apply-templates on the inner 'family' node,
so this is the same thing as recursion. -->
<xsl:apply-templates select="family" />
</ul>
</xsl:template>
</xsl:stylesheet>
I don't know php, but this article will show you how to transform XML using the style sheet above.
You can also link your style sheet by adding a stylesheet directive at the top of your XML file (see for an example).
getElementsByTagName will give you all nodes, not just immediate children:
$family = $indi->getElementsByTagName('family');
foreach ($family as $subIndi) {
outputIndi($subIndi);
}
You will call outputIndi() for grand children, etc repeatedly.
Here is an example (from another stackoverflow question):
for ($n = $indi->firstChild; $n !== null; $n = $n->nextSibling) {
if ($n instanceof DOMElement && $n->tagName == "family") {
outputIndi($n);
}
}
Replace this
$family = $indi->getElementsByTagName('family');
foreach ($family as $subIndi) {
outputIndi($subIndi);
}
by this
if(!empty($indi))
foreach($indi as $subIndi){
outputIndi($subIndi);
}
I realize
if($indi->hasChildNodes())
is better than
if(!empty($indi))

Categories