I have an XML file:
<include>
<data>
<name>Chicken</name>
<price>1</price>
</data>
<data>
<name>Fish</name>
<price>5</price>
</data>
...Cut off about 100 lines...
<data>
<name>Pig</name>
<price>105</price>
</data>
<include>
Now, I want to add the ascending <id>, right above the <name> like this:
<include>
<data>
<id>1203001</id>
<name>Chicken</name>
<price>1</price>
</data>
<data>
<id>1203002</id>
<name>Fish</name>
<price>5</price>
</data>
...Cut off about 100 lines...
<data>
<id>1203105</id>
<name>Pig</name>
<price>105</price>
</data>
<include>
I used the for loop, here is my code:
$data = <<<XML
<include>
<data>
<name>Chicken</name>
<price>1</price>
</data>
<data>
<name>Fish</name>
<price>5</price>
</data>
...Cut off about 100 lines...
<data>
<name>Pig</name>
<price>105</price>
</data>
<include>
XML;
for($i = 1203001; $i <= 1203105; $i++) {
$xml = new SimpleXMLElement($data);
$xml->data->addChild("id", $i);
}
echo $xml->asXML();
However, the result I get is that <id> only appears once, with the largest value, next to </data> instead of the position I want::
<include>
<data>
<name>Chicken</name>
<price>1</price>
<id>1203105</id></data>
<data>
<name>Fish</name>
<price>5</price>
</data>
...
Please show me a method to solve this problem. I would appreciate your solution.
You're recreating $xml from $data every time through the loop, so you're discarding the changes you made on the previous iteration.
Initialize $xml before the loop. Then loop over the data child nodes.
$xml = new SimpleXMLElement($data);
$i = 1203001;
foreach ($xml->data as $node) {
$node->addChild("id", $i++);
}
If you want to insert <id> at the beginning of the <data> node instead of the end, see SimpleXML how to prepend a child in a node?
Related
An API returns the following, and I want to add another data record for testing purposes:
<?xml version="1.0" encoding="UTF-8" ?>
<API>
<code>0000</code>
<time>1549959757</time>
<data cnt="1">
<record>
<record>
<approval_idx>224833</approval_idx>
<reg_date>2019-02-12</reg_date>
<user_id>test</user_id>
<state1>10</state1>
<state2>03</state2>
<name>tester</name>
<e_id>3743</e_id>
<cname>test cname</cname>
<cId>3089</cId>
<loc>oh</loc>
</record>
</record>
</data>
</API>
I tried doing this
$utilApi = new libUtilApi($apiRequest);
$utilApi->request();
$resultAPI = $utilApi->response;
$resultAPI = simplexml_load_string($resultAPI);
$record = $resultAPI->addChild("record");
$record->addChild("approval_idx", "7546");
but the output look like this
<?xml version="1.0" encoding="UTF-8" ?>
<API>
<code>0000</code>
<time>1549959757</time>
<data cnt="1">
<record>
<record>
<approval_idx>224833</approval_idx>
<reg_date>2019-02-12</reg_date>
<user_id>test</user_id>
<state1>10</state1>
<state2>03</state2>
<name>tester</name>
<e_id>3743</e_id>
<cname>test cname</cname>
<cId>3089</cId>
<branch>oh</branch>
</record>
</record>
<record>
<approval_idx>7546</approval_idx>
<reg_date>2019-02-13</reg_date>
<user_id>test3</user_id>
<state1>75</state1>
<state2>09</state2>
<name>new test</name>
<e_id>7546</e_id>
<cname>Q1</cname>
<cId>325</cId>
<branch>oh</branch>
</record>
</data>
</API>
The added child is not going in right position, I tried
$record = $resultAPI->record[0]->addChild("record");
but it falls to an error like:
Call to a member function addChild() on null
-I just updated the code, yes the api is returning the proper format and data, its just that I can't make the addition of children on the right place
I am having nested XML, I want to remove only parent node < items> in xml document keeping all its child nodes.
<root>
<items>
<Product>
<name> </name>
<size> </size>
<images>
<img1></img1>
<img2></img2>
</images>
</Product>
<Product>
<name> </name>
<size> </size>
<images>
<img1></img1>
<img2></img2>
</images>
</Product>
</items>
</root>
Expected Output -
<root>
<Product>
<name> </name>
<size> </size>
<images>
<img1></img1>
<img2></img2>
</images>
</Product>
<Product>
<name> </name>
<size> </size>
<images>
<img1></img1>
<img2></img2>
</images>
</Product>
</root>
I have researched & tried a lot, on removing the < items> node all its child nodes are also getting deleted. Please help if there is any way using DOMDocument or any other way in php.
Well, Geza Boems answer is not exactly what I meant. Using Xpath you can fetch the items nodes for iteration. This is a stable result, so you can iterate it while modifying the DOM.
$document = new DOMDocument();
$document->loadXML($input);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('//items') as $itemsNode) {
// as long that here is any child inside it
while ($itemsNode->firstChild instanceof DOMNode) {
// move it before its parent
$itemsNode->parentNode->insertBefore($itemsNode->firstChild, $itemsNode);
}
// remove the empty items node
$itemsNode->parentNode->removeChild($itemsNode);
}
echo $document->saveXML();
As #ThW mentioned, you have to collect the child nodes in ITEMS, then insert them into ROOT, and finally delete ITEMS.
$input = "
<root>
<items>
<Product>
<name> </name>
<size> </size>
<images>
<img1></img1>
<img2></img2>
</images>
</Product>
<Product>
<name> </name>
<size> </size>
<images>
<img1></img1>
<img2></img2>
</images>
</Product>
</items>
</root>";
$doc = new DOMDocument();
$ret = $doc->loadXML($input);
$root = $doc->firstChild;
$nodes_to_insert = array();
$nodes_to_remove = array();
foreach($root->childNodes as $items) {
if($items->nodeName != "items") {
continue;
}
$nodes_to_remove[] = $items;
foreach($items->childNodes as $child) {
if($child->nodeType != XML_ELEMENT_NODE) {
continue;
}
$nodes_to_insert[] = $child;
}
}
foreach($nodes_to_insert as $node) {
$root->appendChild($node);
}
foreach($nodes_to_remove as $node) {
$root->removeChild($node);
}
var_dump($doc->saveXML());
This code will search for all "items" tag within root, not only one. Inside "items", it will search all normal node (ELEMENT type, but no TEXT node, etc.)
In the last line there is a dump, but normally you will not see anything in a browser, because of the XML header line. But if you take a look at page source, the result will be shown.
PS: it is quite important to not modify xml structure when you walk it. That's why i do only collection first, then the insert and delete actions.
I have this kind of XML:
<?xml version="1.0" encoding="utf-8"?>
<data>
<stats>
</stats>
<params>
</params>
<results>
<record id='SJDGH'>
<item>abc</item>
<item>def</item>
<item>ghi</item>
</record>
<record id='OIIO'>
<item>abc</item>
<item>def</item>
<item>ghi</item>
</record>
</results>
</data>
I'm generating a new <item> for every <record> in <results> in a loop:
// $data is SimpleXml objec from XML above
foreach ($data->results->record as $record)
{
$newitem = 'New item!'.time().$record->attributes()->id;
}
Somehow in this loop i need to change the SimpleXML object ($data) to contain new items in every <record>.
is it possible?
I needed a little guessing, but this might what you're looking for:
$records = $data->results->record;
foreach($records as $record)
{
$value = sprintf('New Item! %s / id:%s', time(), $record['id']);
$record->item[] = $value;
}
$data->asXML('php://output');
See it in action.
I think you might want to use addChild.
Check it out here: http://php.net/manual/en/simplexmlelement.addchild.php
$xml = new DOMDocument();
$root=$xml->createElement("ROOT");
$xml->appendChild($root);
$data=$xml->createElement("DATA");
while($row=db_fetch_object($result))
{
$data=$xml->createElement("ITEM");
$item->setAttribute("COMPANY",$row->field_windmill_fabrikant_value);
$item->setAttribute("HEIGHT",$row->field_windmill_ashoogte_value);
$item->setAttribute("POWER",$row->field_windmill_vermogen_value);
$item->setAttribute("LOCATION",$row->field_windmill_provincie_value);
$item->setAttribute("START_YEAR",$row->field_windmill_startjaar_value);
$data->appendChild($item);
}
$root->appendChild($data);
echo $xml->saveXML();
Here I want to append ITEM as a child node to data but ITEM is getting appended to item and not to data. I'm using PHP.
Can anyone help in it.
Thanks.
Just replace
$data=$xml->createElement("ITEM");
with
$item=$xml->createElement("ITEM");
result of this will be
<?xml version="1.0"?>
<ROOT>
<DATA>
<ITEM COMPANY="COMPANY0" HEIGHT="HEIGHT0" POWER="POWER0" LOCATION="LOCATION0" START_YEAR="START_YEAR0"/>
<ITEM COMPANY="COMPANY1" HEIGHT="HEIGHT1" POWER="POWER1" LOCATION="LOCATION1" START_YEAR="START_YEAR1"/>
<ITEM COMPANY="COMPANY2" HEIGHT="HEIGHT2" POWER="POWER2" LOCATION="LOCATION2" START_YEAR="START_YEAR2"/>
</DATA>
</ROOT>
I have an xml document with the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<id>1</id>
<url>www.test.com</url>
</item>
<item>
<id>2</id>
<url>www.test2.com</url>
</item>
</items>
I would like to be able to search for a node value, such as the value of 1 for the id field. Then, once that node is found, select the parent node, which would be < item > and insert a new child within.
I know the concept of using dom document, but not sure how to do it in this instance.
This should be a start:
$dom = new DOMDocument;
$dom->loadXML($input);
$ids = $dom->getElementsByTagName('id');
foreach ($ids as $id) {
if ($id->nodeValue == '1') {
$child = $dom->createElement('tagname');
$child->appendChild($dom->createTextNode('some text'));
$id->parentNode->appendChild($child);
}
}
$xml = $dom->saveXML();
or something close to it.
You can do the same thing in a simpler way. Instead of looking for an <id/> node whose value is 1 then selecting its parent, you can reverse the relation and look for any node which has an <id/> child whose value is 1.
You can do that very easily in XPath, and here's how to do it in SimpleXML:
$items = simplexml_load_string(
'<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<id>1</id>
<url>www.test.com</url>
</item>
<item>
<id>2</id>
<url>www.test2.com</url>
</item>
</items>'
);
$nodes = $items->xpath('*[id = "1"]');
$nodes[0]->addChild('new', 'value');
echo $items->asXML();