how i can store xml dom object in array - php

i want to store xml Dom object in array and retrieve them back from array using array index
for example
arrayoftags[index] = $this->dom->createElement("plist");
index++;
// and retrive back
$dict = $this->dom->createElement("dict");
arrayoftags[index]->appendChild($dict);
/* some thing like that
<plist>
<dict>
</dict>
</plist>
*/
what i am doing wrong please guide me in right direction and thanks in advance

I am not sure why you want to use an array. So the answer is a little more generic. But yes you can store XML nodes in variables including arrays.
$dom = new DOMDocument();
$created = [];
$created['plist'] = $dom->appendChild($dom->createElement('plist'));
$created['dict'] = $dom->createElement('dict');
$created['plist']->appendChild($created['dict']);
echo $dom->saveXml();
Output:
<?xml version="1.0"?>
<plist><dict/></plist>
appendChild() returns the node it appended. So it is possible to use it directly on a createElement() (or other create* call) and assign the result to a variable. So if the parent node is just stored in a variable the example will be cleaner.
$dom = new DOMDocument();
$plist = $dom->appendChild($dom->createElement('plist'));
$plist->appendChild($dom->createElement('dict'));
echo $dom->saveXml();
Now the DOM already is a data structure, you can use Xpath to fetch some nodes from it, why store the nodes in a second structure (the array)?
$dom = new DOMDocument();
$plist = $dom->appendChild($dom->createElement('plist'));
$plist->appendChild($dom->createElement('dict'));
$xpath = new DOMXpath($dom);
foreach ($xpath->evaluate('//*') as $node) {
var_dump($node->nodeName);
}
Output:
string(5) "plist"
string(4) "dict"

Please refer this code, I think this will help you.
<!-- suppose this is book.xml file -->
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
<catalog>
//PHP file
$dom = new DOMDocument();
$dom->loadXml('book.xml');
$xpath = new DOMXpath($dom);
$result = [];
foreach ($xpath->evaluate('//book') as $book) {
$result[] = [
'id' => $xpath->evaluate('string(#id)', $book),
'Author' => $xpath->evaluate('string(author)', $book),
'Title' => $xpath->evaluate('string(title)', $book),
'Genre' => $xpath->evaluate('string(genre)', $book),
'Price' => $xpath->evaluate('number(price)', $book),
'Publish Date' => $xpath->evaluate('string(publish_date)', $book),
'Description' => $xpath->evaluate('string(description)', $book)
];
}
var_dump($result);

Related

Using variables as object reference for XML

Let's say I have a XML file like this one:
<?xml version="1.0" encoding="utf-8"?>
<test>
<foo>
<bar>Hello, World!</bar>
</foo>
</test>
So, if I use something like this I can echo Hello, World!:
<?php
$xml = simplexml_load_file("myxml.xml");
echo $xml->foo->bar;
?>
But, what if I want to refer to bar with a variable?
<?php
$xml = simplexml_load_file("myxml.xml");
$reference = "foo->bar";
echo $xml->$reference;
?>
That won't work. Any solution?
It's because you are trying to access 3rd level in one variable. PHP couldn't handle -> in variable.
$level1 = 'foo';
$level2 = 'bar';
echo $xml->$level1->$level2;
You can't do that because it will look for a property with the literal foo->bar, and not bar inside foo.
You could do it like this:
$xml = simplexml_load_file("myxml.xml");
$reference = "foo->bar";
$tmp = $xml;
foreach(explode('->', $reference) as $v){
$tmp = $tmp->$v;
}
echo $tmp;
Output:
Hello, World!
This would work even if you don't want to go until the last element. Take a look at the following example.
Test with this XML:
<?xml version="1.0" encoding="utf-8"?>
<test>
<foo>
<bar>Hello, World!</bar>
<something>
<values>
<v1>Some value here (1)</v1>
<v2>Some value here (2)</v2>
<v3>Some value here (3)</v3>
<v4>Some value here (4)</v4>
</values>
</something>
</foo>
</test>
Now, change to $reference = "foo->something->values"; and from echo $tmp; to print_r($tmp);. This will be the output:
SimpleXMLElement Object
(
[v1] => Some value here (1)
[v2] => Some value here (2)
[v3] => Some value here (3)
[v4] => Some value here (4)
)
Use Xpath. It allows you to use expressions to fetch parts of an XML.
$xml = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<test>
<foo>
<bar>Hello, World!</bar>
</foo>
</test>
XML;
$xml = new SimpleXMLElement($xml);
$expression = 'foo/bar';
var_dump(
(string)$xml->xpath($expression)[0]
);
Xpath is a really powerful tool. However to use the full potential you will have to use DOMXpath::evaluate(). SimpleXMLElement::xpath() can only return node lists as arrays of SimpleXMLElement objects. DOMXpath:evaluate() can return node lists or scalar values.
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$expression = 'string(foo/bar)';
var_dump(
$xpath->evaluate($expression)
);

How to generate namespace prefixed xml elements using SimpleXMLElement in PHP

I'm trying to generate a RSS feed using PHP SimpleXMLElement, the problem is that i need to prefix elements and can't find a way to do this using the SimpleXMLElement class.
I've tried using $item->addChild('prefix:element', 'value') but in the result xml it strips the prefix, any idea why this happens ?.
I wonder if there is a way to solve this using the SimpleXMLElement or any other cleaner way than just echoing the XML.
For clarification, this is my PHP code:
$xml = new SimpleXMLElement('<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0"/>');
$channel = $xml->addChild('channel');
$channel->addChild('title', 'Text');
$channel->addChild('link', 'http://example.com');
$channel->addChild('description', 'An example item from the feed.');
foreach($this->products as $product) {
$item = $channel->addChild('item');
foreach($product as $key => $value)
$item->addChild($key, $value);
}
return $xml->asXML();
And this is the example XML i'm trying to generate:
<?xml version="1.0"?>
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
<channel>
<title>Test Store</title>
<link>http://www.example.com</link>
<description>An example item from the feed</description>
<item>
<g:id>DB_1</g:id>
<g:title>Dog Bowl In Blue</g:title>
<g:description>Solid plastic Dog Bowl in marine blue color</g:description>
...
</item>
...
Thanks in advance
You need to pass the namespace uri of the prefix to add child element with prefix :
$item->addChild($key, $value, 'http://base.google.com/ns/1.0');
eval.in demo :
$xml = new SimpleXMLElement('<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0"/>');
$channel = $xml->addChild('channel');
$channel->addChild('title', 'Text');
$channel->addChild('link', 'http://example.com');
$channel->addChild('description', 'An example item from the feed.');
$item = $channel->addChild('item');
$item->addChild('g:foo', 'bar', 'http://base.google.com/ns/1.0');
print $xml->asXML();

How to use xquery to find node and addChild to it?

Is it possible to use xpath/xquery to query for a specific xml node, and then import/add a child node to it?
Example (code taken from http://codepad.org/gJ1Y2LjM , that was presented in similar question, but not the same):
1.
I want to add an array
$book = array('isbn'=>123456789099, 'title' => 'Harry Potter 3', 'author' => 'J K. Rowling', 'edition' => '2007');
2.
To an existing XML.
$doc = new DOMDocument();
$doc->loadXML('<?xml version="1.0"?>
<books>
<book>
<isbn>123456789098</isbn>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<edition>2005</edition>
</book>
<book>
<placeItHere></placeItHere>
<isbn>1</isbn>
<title>stuffs</title>
<author>DA</author>
<edition>2014</edition>
</book>
</books>');
3.
To do that it is used the following fragment.
$fragment = $doc->createDocumentFragment();
$fragment->appendXML(" <book>
<isbn>{$book['isbn']}</isbn>
<title>{$book['title']}</title>
<author>{$book['author']}</author>
<edition>{$book['edition']}</edition>
</book>
");
4.
But instead of appending it to the root node, has almost every example i found on internet:
$doc->documentElement->appendChild($fragment);
5.
I want to append it (p.ex) to the node found in /books/book/placeItHere and not using getElementbyId or tagName, but a xpath/xquery. I tryed
$xp = new domxpath($doc);
$parent = $xp->query("books/book/placeItHere");
to reach the node, but never managed to use it as the parent.
Question: How to use that location to appendChild $fragment? Is it possible?
**YOUR BEAUTIFUL MAGIC**
In the end I will just save it.
echo $doc->saveXML();
Thank you for any help you can give to me.
A couple problems:
Your xpath expression should be /books/book/placeItHere (with the leading /).
DOMXPath::query() returns a DOMNodeList rather than a DOMNode, so you'll need to grab your item() from it.
I rarely recommend using document fragments as playing fast and loose with raw XML regularly leads to problems. For example if your book title included an ampersand appendXML() would crap out with a parser error.
Instead I suggest createElement() and createTextNode(), which will handle transforming things like & into & automatically.
Example:
$xml = <<<'XML'
<?xml version="1.0"?>
<books>
<book>
<isbn>123456789098</isbn>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<edition>2005</edition>
</book>
<book>
<placeItHere></placeItHere>
<isbn>1</isbn>
<title>stuffs</title>
<author>DA</author>
<edition>2014</edition>
</book>
</books>
XML;
$book = [
'isbn' => 123456789099,
'title' => 'Harry Potter 3',
'author' => 'J K. Rowling',
'edition' => '2007'
];
$dom = new DOMDocument();
$dom->formatOutput = true;
$dom->preserveWhiteSpace = false;
$dom->loadXML($xml);
$xpath = new DOMXPath($dom);
$placeItHere = $xpath->query('/books/book/placeItHere')->item(0);
$newBook = $placeItHere->appendChild($dom->createElement('book'));
foreach ($book as $part => $value) {
$element = $newBook->appendChild($dom->createElement($part));
$element->appendChild($dom->createTextNode($value));
}
echo $dom->saveXML();
Output:
<?xml version="1.0"?>
<books>
<book>
<isbn>123456789098</isbn>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<edition>2005</edition>
</book>
<book>
<placeItHere>
<book>
<isbn>123456789099</isbn>
<title>Harry Potter 3</title>
<author>J K. Rowling</author>
<edition>2007</edition>
</book>
</placeItHere>
<isbn>1</isbn>
<title>stuffs</title>
<author>DA</author>
<edition>2014</edition>
</book>
</books>

Simplexml is giving me the wrong results

I have a simple xml below:
<?xml version="1.0" encoding="utf-8"?>
<catalogue>
<category name="textbook" id="100" parent="books">
<product id="20000">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</product>
<product id="20001">
<author>Gambardellas, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</product>
</category>
<category name="fiction" id="101" parent="books">
<product id="2001">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<type>Fiction</type>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen
of the world.</description>
</product>
</category>
</catalogue>
I am using php simplexml library to parse it as follows: (note there are two category nodes. The first category contains two 'product' children. My aim is to get an array that contains those two children of first 'category'
$xml = simplexml_load_file($xml_file) or die ("unable to load XML File!".$xml_file);
//for each product, print out info
$cat = array();
foreach($xml->category as $category)
{
if($category['id'] == 100)
{
$cat = $category;
break;
}
}
$prod_arr = $category->product;
Here is the problem. I am expecting an array with two products children but its only returning one product. What am I doing wrong or is this a php bug? Please help!
You can use SimpleXMLElement::xpath() to get all product elements that are children of a specific category element. E.g.
// $catalogue is your $xml
$products = $catalogue->xpath('category[#id="100"]/product');
foreach($products as $p) {
echo $p['id'], ' ', $p->title, "\n";
}
prints
20000 XML Developer's Guide
20001 XML Developer's Guide
For start, your XML file is not well defined. You should probably start and end it with <categories> tag.
Replace the last assignment with the following:
$prod_array = array();
foreach ($cat->product as $p) {
$prod_array[] = $p;
}
$cat = array();
foreach ($xml->category as $category)
{
$attributes = $category->attributes();
if(isset($attributes['id']) && $attributes['id'] == 100)
{
$cat = $category;
break;
}
}

Retrieving a subset of XML nodes with PHP

Using PHP, how do I get an entire subset of nodes from an XML document? I can retrieve something like:
<?xml version="1.0" encoding="utf-8"?>
<people>
<certain>
<name>Jane Doe</name>
<age>21</age>
</certain>
<certain>
<certain>
<name>John Smith</name>
<age>34</age>
</certain>
</people>
But what if I only want to return the child nodes of like this?
<certain>
<name>Jane Doe</name>
<age>21</age>
</certain>
<certain>
<certain>
<name>John Smith</name>
<age>34</age>
</certain>
EDIT: I'm trying to get a subset of XML and pass that directly, not an object like simplexml would give me. I am basically trying to get PHP to do what .NET's OuterXml does... return literally the above subset of XML as is... no interpreting or converting or creating a new XML file or anything... just extract those nodes in situ and pass them on. Am I going to have to get the XML file, parse out what I need and then rebuild it as a new XML file? If so then I need to get rid of the <?xml version="1.0" encoding="utf-8"?> bit... ugh.
The answer would be to use XPath.
$people = simplexml_load_string(
'<?xml version="1.0" encoding="utf-8"?>
<people>
<certain>
<name>Jane Doe</name>
<age>21</age>
</certain>
<certain>
<name>John Smith</name>
<age>34</age>
</certain>
</people>'
);
// get all <certain/> nodes
$people->xpath('//certain');
// get all <certain/> nodes whose <name/> is "John Smith"
print_r($people->xpath('//certain[name = "John Smith"]'));
// get all <certain/> nodes whose <age/> child's value is greater than 21
print_r($people->xpath('//certain[age > 21]'));
Take 2
So apparently you want to copy some nodes from a document into another document? SimpleXML doesn't support that. DOM has methods for that but they're kind of annoying to use. Which one are you using? Here's what I use: SimpleDOM. In fact, it's really SimpleXML augmented with DOM's methods.
include 'SimpleDOM.php';
$results = simpledom_load_string('<results/>');
foreach ($people->xpath('//certain') as $certain)
{
$results->appendChild($certain);
}
That routine finds all <certain/> node via XPath, then appends them to the new document.
You could use DOMDocument.GetElementsByTagName or you could:
Use XPath?
<?php
$xml = simplexml_load_file("test.xml");
$result = $xml->xpath("//certain");
print_r($result);
?>
Use DOM and XPath. Xpath allows you to select nodes (and values) from an XML DOM.
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
$result = '';
foreach ($xpath->evaluate('/people/certain') as $node) {
$result .= $dom->saveXml($node);
}
echo $result;
Demo: https://eval.in/162149
DOMDocument::saveXml() has a context argument. If provided it saves that node as XML. Much like outerXml(). PHP is able to register your own classes for the DOM nodes, too. So it is even possible to add an outerXML() function to element nodes.
class MyDomElement extends DOMElement {
public function outerXml() {
return $this->ownerDocument->saveXml($this);
}
}
class MyDomDocument extends DOMDocument {
public function __construct($version = '1.0', $encoding = 'utf-8') {
parent::__construct($version, $encoding);
$this->registerNodeClass('DOMElement', 'MyDomElement');
}
}
$dom = new MyDomDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
$result = '';
foreach ($xpath->evaluate('/people/certain') as $node) {
$result .= $node->outerXml();
}
echo $result;
Demo: https://eval.in/162157
See http://www.php.net/manual/en/domdocument.getelementsbytagname.php
The answer turned out to be a combination of the xpath suggestion and outputting with asXML().
Using the example given by Josh Davis:
$people = simplexml_load_string(
<?xml version="1.0" encoding="utf-8"?>
<people>
<certain>
<name>Jane Doe</name>
<age>21</age>
</certain>
<certain>
<name>John Smith</name>
<age>34</age>
</certain>
</people>'
);
// get all <certain/> nodes
$nodes = $people->xpath('/people/certain');
foreach ( $nodes as $node ) {
$result .= $node->asXML()."\n";
}
echo $result;

Categories