issues in writing xml files in php - php

I wrote a code in which i m writing new xml files depending on content of one xml file i have with me right now.
In xml file from which i m reading,there are 14 fragments with 3 -4 nodes each.I have to right 7 new xml files in this case,each containing 2 fragments from first file.
So that all xml files after writing should have 2 fragments each,totalling all to 14 fragments when all files combined.
But the code is not giving me the correct output
following is the code
<?php
$docOutput = new DOMDocument("1.0");
$root = $docOutput ->createElement("data");
$docOutput ->appendChild($root);
if(!$xml=simplexml_load_file('xmlfile.xml')){
trigger_error('Error reading XML file',E_USER_ERROR);
}
$array1=array();
$x=0;
foreach($xml as $syn)
{
for($i=$x*2;$i<($x+1)*2;$i++)
{
//$array1[] = $syn->productId;
echo $i;
echo "<br />";
echo $syn->productId;
$id = $docOutput ->createElement("PID");
$idText = $docOutput ->createTextNode($syn->productId);
$id->appendChild($idText);
$title = $docOutput ->createElement("PNAME");
$titleText = $docOutput ->createTextNode($syn->productname);
$title->appendChild($titleText);
$book = $docOutput ->createElement("Product");
$book->appendChild($id);
$book->appendChild($title);
$root->appendChild($book);
}
$docOutput ->formatOutput = true;
echo "<xmp>". $docOutput ->saveXML() ."</xmp>";
$docOutput ->save("mybooks$x.xml") or die("Error");
$x++;
}
//echo count($array1, COUNT_RECURSIVE);
?>
Following is one fragment of file,which i m reading
<?xml version="1.0"?>
<data>
<Product>
<PID>SGLDN7XJ2FPZH8G8</PID>
<PNAME>Miami Blues Aviator Sunglasses</PNAME>
</Product>
</data>
There are 14 Product fragments in file from which i m reading.After writing i should get 7 files with two fragments each.
Please help in correcting the code

The problem you've got is less with the code to read/write the XML but more just with the basic looping logic.
What you perhaps actually want is while iterating over the product nodes, to move the iteration ahead so you can put two product elements together. This works with a SimpleXMLIterator:
$xml = simplexml_load_string($buffer, 'SimpleXMLIterator');
foreach ($xml as $product) {
// output current element
printf("-+-%s\n", $product->asXML());
// move to next element
$xml->next();
$product = $xml->current();
// output next element
printf(" \- %s\n\n", $product->asXML());
}
With this XML input:
<?xml version="1.0"?>
<data>
<Product>1</Product>
<Product>2</Product>
<Product>3</Product>
<Product>4</Product>
</data>
The example outputs:
-+-<Product>1</Product>
\- <Product>2</Product>
-+-<Product>3</Product>
\- <Product>4</Product>
Online-Demo: https://eval.in/172918

Related

How to remove XML tag based on child attribute using php?

I have an XML like below
<entries>
<entry>
<title lang="en">Sample</title>
<entrydate>0</entrydate>
<contents>0</contents>
<entrynum>0</entrynum>
</entry>
<entry>
<title lang="fr">Sample</title>
<entrydate>1</entrydate>
<contents>1</contents>
<entrynum>1</entrynum>
</entry>
</entries>
Is there a way in PHP to delete the parent node (entry) based on the title lang attribute? I need to keep only the en ones, so in this case I would need to get the XML without the second entry node.
I tried looking around but couldn't find any solution...
You need to use DOMDocument class to parse string to XML document. Then use DOMXpath class to find target element in document and use DOMNode::removeChild() to remove selected element from document.
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXpath($doc);
// select target entry tag
$entry = $xpath->query("//entry[title[#lang='fr']]")->item(0);
// remove selected element
$entry->parentNode->removeChild($entry);
$xml = $doc->savexml();
You can check result in demo
You could also read your file and generated new one with your modification
<?php
$entries = array('title' => "What's For Dinner",
'link' => 'http://menu.example.com/',
'description' => 'Choose what to eat tonight.');
print "<entries>\n";
foreach ($entries as $element => $content) {
print " <$element>";
print htmlentities($content);
print "</$element>\n";
}
print "</entries>";
?>
Use the method described in this answer, i.e.
<?php
$xml = simplexml_load_file('1.xml');
$del_items = [];
foreach ($xml->entry as $e) {
$attr = $e->title->attributes();
if ($attr && $attr['lang'] != 'en') {
$del_items []= $e;
}
}
foreach ($del_items as $e) {
$dom = dom_import_simplexml($e);
$dom->parentNode->removeChild($dom);
}
echo $xml->asXML();
Output
<?xml version="1.0" encoding="UTF-8"?>
<entries>
<entry>
<title lang="en">Sample</title>
<entrydate>0</entrydate>
<contents>0</contents>
<entrynum>0</entrynum>
</entry>
</entries>
The items cannot be removed within the first loop, because otherwise we may break the iteration chain. Instead, we collect the entry objects into $del_items array, then remove them from XML in separate loop.

for loop putting two similar entries in one loop in php

I wrote a code according to which I can write one XML file into number of XML files depending on number of records.
I have 14 records in one XML file having different tags in XML file.
I am trying to create XML files having having 2 records each, so in the end I should be having 7 XML files.
But instead that I'm getting 14 files only, each file having two similar records.
Following is my XML format, which is getting created dynamically. Format is coming correctly. But two similar entries are coming in each file. So instead of 7 its writing 14 files.
<?xml version="1.0"?>
<data>
<Product>
<PID>SGLDN7XJ2FPZH8G8</PID>
<PNAME>Miami Blues Aviator Sunglasses</PNAME>
</Product>
</data>
Following is my file which is making XML files. I think something is wrong in initializing the for loop:
$docOutput = new DOMDocument("1.0");
$root = $docOutput->createElement("data");
$docOutput->appendChild($root);
if (!$sxmlReading = simplexml_load_file('firstxml.xml')) {
throw new RuntimeException('Error reading XML file');
}
$x = 0;
foreach ($sxmlReading as $syn) {
for ($i = $x * 2; $i < ($x + 1) * 2; $i++) {
$id = $docOutput->createElement("PID");
$idText = $docOutput->createTextNode($syn->productId);
$id->appendChild($idText);
$title = $docOutput->createElement("PNAME");
$titleText = $docOutput->createTextNode($syn->productname);
$title->appendChild($titleText);
$book = $docOutput->createElement("Product");
$book->appendChild($id);
$book->appendChild($title);
$root->appendChild($book);
$docOutput->formatOutput = true;
echo "<xmp>" . $docOutput->saveXML() . "</xmp>";
$docOutput->save("xml$x.xml") or die("Error");
}
$x++;
}
Please check the code and highlight my mistake...
It is writing 14 files because you are calling the DOMDocument::save() method 14 times.
Sounds more like a typo to me honestly, I can not see anything wrong with that. PHP just does as you've written it to do.

xml and php getting tag elements with certain element and outputting

I am have two xml files.. I first get one and loop through it then I need to take an id from the first xml file and find it in the second one and echo out the results associated with that id. If I were to do this with SQL I would simply do this:
$query = (SELECT * FROM HotelSummary WHERE roomTypeCode = '$id') or die();
while($row=mysql_fetch_array($query)){
$name = $row['Name'];
}
echo $name;
How can I do this is in xml and php??
I recommend you to read the DOMDocument documentation.
It's quite heavy but also powerful (not always clear what happens, but the Internet shold always give you a solution)
You can simply walk through your first document, finding your Id and then find your DOMElement via an XPath.
<?php
$dom = new DOMDocument();
$dom->load('1.xml');
foreach ($dom->getElementsByTagName('article') as $node) {
// your conditions to find out the id
$id = $node->getAttribute('id');
}
$dom = new DOMDocument();
$dom->load('2.xml');
$xpath = new DOMXPath($dom);
$element = $xpath->query("//*[#id='".$id."']")->item(0);
// would echo "top_2" based on my example files
echo $element->getAttribute('name');
Based on following test files:
1.xml
<?xml version="1.0" encoding="UTF-8"?>
<articles>
<article id="foo_1">
<title>abc</title>
</article>
<article id="foo_2">
<title>def</title>
</article>
</articles>
2.xml
<?xml version="1.0" encoding="UTF-8"?>
<tests>
<test id="foo_1" name="top_1">
</test>
<test id="foo_2" name="top_2">
</test>
</tests>
Use SimpleXML to create an object representation of the file. You can then loop through the elements of the Simple XML object.
Depending on the format of the XML file:
Assuming it is:
<xml>
<roomTypeCode>
<stuff>stuff</stuff>
<name>Skunkman</name>
</roomTypeCode>
<roomTypeCode>
<stuff>other stuff</stuff>
<name>Someone Else</name>
</roomTypeCode>
</xml>
It would be something like this:
$xml = simplexml_load_file('xmlfile.xml');
for($i = 0; $i < count($xml->roomTypeCode); $i++)
{
if($xml->roomTypeCode[$i]->stuff == "stuff")
{
$name = $xml->roomTypeCode[$i]->name;
}
}
That connects to the XML file, finds how many roomTypeCode entries there are, searches for the value of "stuff" within and when it matches it correctly, you can access anything having to do with that XML entry.

Hide XML declaration in files generated using PHP

I was tesing with a simple example of how to display XML in browser using PHP and found this example which works good
<?php
$xml = new DOMDocument("1.0");
$root = $xml->createElement("data");
$xml->appendChild($root);
$id = $xml->createElement("id");
$idText = $xml->createTextNode('1');
$id->appendChild($idText);
$title = $xml->createElement("title");
$titleText = $xml->createTextNode('Valid');
$title->appendChild($titleText);
$book = $xml->createElement("book");
$book->appendChild($id);
$book->appendChild($title);
$root->appendChild($book);
$xml->formatOutput = true;
echo "<xmp>". $xml->saveXML() ."</xmp>";
$xml->save("mybooks.xml") or die("Error");
?>
It produces the following output:
<?xml version="1.0"?>
<data>
<book>
<id>1</id>
<title>Valid</title>
</book>
</data>
Now I have got two questions regarding how the output should look like.
The first line in the xml file '', should not be displayed, that is it should be hidden
How can I display the TextNode in the next line. In total I am exepecting an output in this fashion
<data>
<book>
<id>1</id>
<title>
Valid
</title>
</book>
</data>
Is that possible to get the desired output, if so how can I accomplish that.
Thanks
To skip the XML declaration you can use the result of saveXML on the root node:
$xml_content = $xml->saveXML($root);
file_put_contents("mybooks.xml", $xml_content) or die("cannot save XML");
Please note that saveXML(node) has a different output from saveXML().
First question:
here is my post where all usable threads with answers are listed: How do you exclude the XML prolog from output?
Second question:
I don't know of any PHP function that outputs text nodes like that.
You could:
read xml using DomDocument and save each node as string
iterate trough nodes
detect text nodes and add new lines to xml string manually
At the end you would have the same XML with text node values in new line:
<node>
some text data
</node>

php only appending 3 out of 5 child nodes

This code is only appending 3 of the 5 name nodes. Why is that?
Here is the original XML:
It has 5 name nodes.
<?xml version='1.0'?>
<products>
<product>
<itemId>531670</itemId>
<modelNumber>METRA ELECTRONICS/MOBILE AUDIO</modelNumber>
<categoryPath>
<category><name>Buy</name></category>
<category><name>Car, Marine & GPS</name></category>
<category><name>Car Installation Parts</name></category>
<category><name>Deck Installation Parts</name></category>
<category><name>Antennas & Adapters</name></category>
</categoryPath>
</product>
</products>
Then is run this PHP code. which is suppossed to appened ALL name nodes into the product node.
<?php
// load up your XML
$xml = new DOMDocument;
$xml->load('book.xml');
// Find all elements you want to replace. Since your data is really simple,
// you can do this without much ado. Otherwise you could read up on XPath.
// See http://www.php.net/manual/en/class.domxpath.php
//$elements = $xml->getElementsByTagName('category');
// WARNING: $elements is a "live" list -- it's going to reflect the structure
// of the document even as we are modifying it! For this reason, it's
// important to write the loop in a way that makes it work correctly in the
// presence of such "live updates".
foreach ($xml->getElementsByTagName('product') as $product ) {
foreach($product->getElementsByTagName('name') as $name ) {
$product->appendChild($name );
}
$product->removeChild($xml->getElementsByTagName('categoryPath')->item(0));
}
// final result:
$result = $xml->saveXML();
echo $result;
?>
The end result is this and it only appends 3 of the name nodes:
<?xml version="1.0"?>
<products>
<product>
<itemId>531670</itemId>
<modelNumber>METRA ELECTRONICS/MOBILE AUDIO</modelNumber>
<name>Buy</name>
<name>Antennas & Adapters</name>
<name>Car Installation Parts</name>
</product>
</products>
Why is it only appending 3 of the name nodes?
You can temporarily add the name elements to an array before appending them, owing to the fact that you're modifying the DOM in real time. The node list generated by getElementsByTagName() may change as you are moving nodes around (and indeed that appears to be what's happening).
<?php
// load up your XML
$xml = new DOMDocument;
$xml->load('book.xml');
// Array to store them
$append = array();
foreach ($xml->getElementsByTagName('product') as $product ) {
foreach($product->getElementsByTagName('name') as $name ) {
// Stick $name onto the array
$append[] = $name;
}
// Now append all of them to product
foreach ($append as $a) {
$product->appendChild($a);
}
$product->removeChild($xml->getElementsByTagName('categoryPath')->item(0));
}
// final result:
$result = $xml->saveXML();
echo $result;
?>
Output, with all values appended:
<?xml version="1.0"?>
<products>
<product>
<ItemId>531670</ItemId>
<modelNumber>METRA ELECTRONICS/MOBILE AUDIO</modelNumber>
<name>Buy</name><name>Car, Marine & GPS</name><name>Car Installation Parts</name><name>Deck Installation Parts</name><name>Antennas & Adapters</name></product>
</products>
You're modifying the DOM tree as you're pulling results from it. Any modifications to the tree that cover the results of a previous query operation (your getElementsByTagName) invalidate those results, so you're getting undefined results. This is especially true of operations that add/remove nodes.
You're moving nodes as you're iterating through them so 2 are being skipped. I'm not a php guy so I can't give you the code to do this, but what you need to do is build a collection of the name nodes and iterate through that collection in reverse.
A less complicated way to do it is to manipulate the nodes with insertBefore
foreach($xml->getElementsByTagName('name') as $node){
$gp = $node->parentNode->parentNode;
$ggp = $gp->parentNode;
// move the node above gp without removing gp or parent
$ggp->insertBefore($node,$gp);
}
// remove the empty categoryPath node
$ggp->removeChild($gp);

Categories