Inject XML into a node using a string - php

I am rebuilding a nodes children by saving them out to an array as strings, trashing them in the XML, inserting a new child node into the array as a string... now I want to loop through the array and write them back out to the original node. The problem is I can't find anything on how to add a child node using a string.
See below for my code. Thanks!!!
$xml = simplexml_load_file($url);
$questionGroup = $xml->qa[intval($id)];
$children = array(); // create empty array
foreach ($questionGroup->children() as $element) { // loop thru children
array_push($children, $element->asXML()); // save XML into array
}
//unset($questionGroup->answer);
//unset($questionGroup->question);
//create new node
$newNode = '<answer><title>'.$title.'</title><description>'.$description.'</description><subName>'.$subName.'</subName><date>'.$date.'</date><timestamp>'.$timestamp.'</timestamp></answer>';
echo "children count: ".count($children);
echo "<br /><br />";
print_r($children);
echo "<br /><br />";
// insert new
array_splice($children,intval($elementIndex),0,$newNode);
echo "children count: ".count($children);
echo "<br /><br />";
print_r($children);
echo "<br /><br />";
echo $questionGroup->asXML();
foreach ($children as $element) { // loop thru array
echo "<br /><br />";
echo $element;
//$questionGroup->addChild(simplexml_load_string($element)); // add array element to the empty questionGroup
}
echo "<br /><br />";
echo "questionGroup: ".$questionGroup;
UPDATE:
I found a function that I modified and was able to get working:
function append_simplexml(&$simplexml_to, &$simplexml_from)
{
$childNode = $simplexml_to->addChild($simplexml_from->getName(), "");
foreach ($simplexml_from->children() as $simplexml_child)
{
$simplexml_temp = $childNode->addChild($simplexml_child->getName(), (string) $simplexml_child);
foreach ($simplexml_child->attributes() as $attr_key => $attr_value)
{
$simplexml_temp->addAttribute($attr_key, $attr_value);
}
// append_simplexml($simplexml_temp, $simplexml_child);
}
}
With this usage in my foreach() loop:
foreach ($children as $element) { // loop thru array
append_simplexml($questionGroup, new SimpleXMLElement($element));
}

You can't do that with SimpleXML alone. There is a nice way to do this with the DOM extension and the DOMDocumentFragment class. (Please note that I didn't try to understand your logic in the example provided, but you should be able to implement the simple sample below into your code).
$xml = simplexml_load_string('<root><parent/></root>');
// get the parent node under which you want to insert your XML fragement
$parent = dom_import_simplexml($xml->parent);
// create the XML fragment
$fragment = $parent->ownerDocument->createDocumentFragment();
// append the XML literal to your fragment
$fragment->appendXML('<child id="1"/><child id="2"><grandchild/></child>');
// append the fragment to the parent node
$parent->appendChild($fragment);
echo $xml->asXML();
/*
* <?xml version="1.0"?>
* <root><parent><child id="1"/><child id="2"><grandchild/></child></parent></root>
*/
Links:
dom_import_simplexml()
DOMDocument::createDocumentFragment()
DOMDocumentFragment::appendXML()
DOMNode::appendChild()
SimpleXMLElement::asXML()

I found a function that I modified and was able to get working:
function append_simplexml(&$simplexml_to, &$simplexml_from)
{
$childNode = $simplexml_to->addChild($simplexml_from->getName(), "");
foreach ($simplexml_from->children() as $simplexml_child)
{
$simplexml_temp = $childNode->addChild($simplexml_child->getName(), (string) $simplexml_child);
foreach ($simplexml_child->attributes() as $attr_key => $attr_value)
{
$simplexml_temp->addAttribute($attr_key, $attr_value);
}
// append_simplexml($simplexml_temp, $simplexml_child);
}
}
With this usage in my foreach() loop:
foreach ($children as $element) { // loop thru array
append_simplexml($questionGroup, new SimpleXMLElement($element));
}

You should be able to do something like this:
$child = new SimpleXMLElement($newNode);
$yourxmlobject->addChild($child);

Related

Getting XML node names without duplicating in simplexml_load_file

I'm getting the node names of an XML using this code:
$url = 'https://www.toptanperpa.com/xml.php?c=shopphp&xmlc=e7ef2a0122';
$xml = simplexml_load_file($url) or die("URL Read Error");
echo $xml->getName() . "<br>";
foreach ($xml->children() as $child) {
echo $child->getName() . "<br>";
foreach ($child->children() as $child2) {
echo $child2->getName() . "<br>";
foreach ($child2->children() as $child3) {
echo $child3->getName() . "<br>";
foreach ($child3->children() as $child4) {
echo $child4->getName() . "<br>";
}
}
}
}
I'm getting the nodes and children correctly, however, it's duplicating.
Result is as below:
urunler
urun
urun_aktif
urun_metaKeywords
urun_metaDescription
urun_url
urun
urun_aktif
urun_metaKeywords
urun_metaDescription
urun_url
Should I just use array_unique or is there a better method?
Thanks
i used recursive function this is simple
function getChildrens($x) {
$children = array();
array_push($children, $x->getName());
foreach ($x->children() as $chld) {
$children = array_merge($children, getChildrens($chld));
}
return array_unique($children);
}
echo implode("<br>", getChildrens($xml));

Missing children in XML parsed by SimpleXML in PHP

Here is the XML I am parsing:
https://seekingalpha.com/api/sa/combined/AAPL.xml
When I grab and parse the XML with simplexml_load_file($url) and then do a var_dump on that, it shows that the only children of every "item" are "title", "link", "guid", and "pubDate."
I am trying to access the node "sa:author_name." Why isn't it a child of "item"? Maybe I am misunderstanding something about how XML files are structured. Help me my children are missing lol
To get the data in sa:author_name you have to use the namespace https://seekingalpha.com/api/1.0.
You can for example use a foreach and loop the children using the namespace.
$url = "https://seekingalpha.com/api/sa/combined/AAPL.xml";
$xml = simplexml_load_file($url);
foreach ($xml->channel->item as $item) {
foreach ($item->children("https://seekingalpha.com/api/1.0") as $child) {
if ($child->getName() === "author_name") {
echo $child . "<br>";
}
}
}
Another way you could do it is using an xpath expression:
$authorNames = $xml->xpath('/rss/channel/item/sa:author_name');
foreach ($authorNames as $authorName) {
echo $authorName . "<br>";
}
Which will result in:
Yoel Minkoff
DoctoRx
SA Transcripts
Bill Maurer
etc..

Parsing XLM with PHP

I have an XML product feed that I am parsing with PHP to load products into a database.
I need to get each element into an array of $products = array() such as:
$products[AttributeID] = value
This is what I have so far:
I am using simplexml and I have got most of it:
$xml = simplexml_load_file($CatalogFileName) or die("can't open file " . $CatalogFileName);
foreach($xml->children() as $products) {
foreach($products->children() as $product) {
$new_product = array();
$new_product[sku] = "AS-" . $product->Name;
foreach($product->Values->Value as $node) {
$node_name = preg_replace('/\s+/', '', $node[AttributeID]);
$new_product[$node_name] = $node[0]; **<--THIS IS NOT WORKING: $node[0] returns an array I only want the data in each attribute.**
}
foreach($product->AssetCrossReference as $node) {
$new_product[image] = "http://www.xxxxxxxx.com/images/items/fullsize/" . $node[AssetID] . ".jpg";
}
print_r($new_product);
}
}
Here is an image of one product node: XML
Can someone provide me with a little help here? I do a lot of PHP programming but this is the first time I am dealing with XML
A possbile solution is to use the xpath method to find the <Value> elements.
The xpath method will return an array of SimpleXMLElement objects.
These SimpleXMLElement objects have an attributes method which you can use to access the 'AttributeID' attribute.
I hope this example can help you:
$xml = simplexml_load_file($CatalogFileName) or die("can't open file " . $CatalogFileName);
$productsArray = array();
foreach($xml->children() as $products) {
foreach($products->children() as $product) {
$new_product = array();
$productId = (string)$product->attributes()->ID;
$new_product['NAME'] = "AS-" . (string)$product->Name;
// xpath will return an array of SimpleXMLElement objects
$values = $product->Values->xpath('//Value');
// use the attributes method to access the AttributeID
foreach ($values as $node) {
$attributeID = (string)$node->attributes()->AttributeID;
$new_product[$attributeID] = trim((string)$node);
}
$new_product['AssetID'] = sprintf(
"http://www.xxxxxxxx.com/images/items/fullsize/%s.jpg",
(string)$product->AssetCrossReference->attributes()->AssetID
);
// Add $new_product to $productsArray using the $productId as the array key
$productsArray[$productId] = $new_product;
}
}

PHP how to count xml elements in object returned by simplexml_load_file(),

I have inherited some PHP code (but I've little PHP experience) and can't find how to count some elements in the object returned by simplexml_load_file()
The code is something like this
$xml = simplexml_load_file($feed);
for ($x=0; $x<6; $x++) {
$title = $xml->channel[0]->item[$x]->title[0];
echo "<li>" . $title . "</li>\n";
}
It assumes there will be at least 6 <item> elements but sometimes there are fewer so I get warning messages in the output on my development system (though not on live).
How do I extract a count of <item> elements in $xml->channel[0]?
Here are several options, from my most to least favourite (of the ones provided).
One option is to make use of the SimpleXMLIterator in conjunction with LimitIterator.
$xml = simplexml_load_file($feed, 'SimpleXMLIterator');
$items = new LimitIterator($xml->channel->item, 0, 6);
foreach ($items as $item) {
echo "<li>{$item->title}</li>\n";
}
If that looks too scary, or not scary enough, then another is to throw XPath into the mix.
$xml = simplexml_load_file($feed);
$items = $xml->xpath('/rss/channel/item[position() <= 6]');
foreach ($items as $item) {
echo "<li>{$item->title}</li>\n";
}
Finally, with little change to your existing code, there is also.
$xml = simplexml_load_file($feed);
for ($x=0; $x<6; $x++) {
// Break out of loop if no more items
if (!isset($xml->channel[0]->item[$x])) {
break;
}
$title = $xml->channel[0]->item[$x]->title[0];
echo "<li>" . $title . "</li>\n";
}
The easiest way is to use SimpleXMLElement::count() as:
$xml = simplexml_load_file($feed);
$num = $xml->channel[0]->count();
for ($x=0; $x<$num; $x++) {
$title = $xml->channel[0]->item[$x]->title[0];
echo "<li>" . $title . "</li>\n";
}
Also note that the return of $xml->channel[0] is a SimpleXMLElement object. This class implements the Traversable interface so we can use it directly in a foreach loop:
$xml = simplexml_load_file($feed);
foreach($xml->channel[0] as $item {
$title = $item->title[0];
echo "<li>" . $title . "</li>\n";
}
You get count by count($xml).
I always do it like this:
$xml = simplexml_load_file($feed);
foreach($xml as $key => $one_row) {
echo $one_row->some_xml_chield;
}

PHP: documentElement->childNodes warning

$xml = file_get_contents(example.com);
$dom = new DomDocument();
$dom->loadXML($xml);
$items = $dom->documentElement;
foreach($items->childNodes as $item) {
$childs = $item->childNodes;
foreach($childs as $i) {
echo $i->nodeValue . "<br />";
}
}
Now I get this warning in every 2nd foreach:
Warning: Invalid argument supplied for foreach() in file_example.php on line 14
Please help guys. Thanks!
Some nodes don't have children, so you're passing a null (invalid) argument to the foreach (just like the warning says).
To avoid the warnings you need to check if the current node has any children. For that you can use the DOMNode::hasChildNodes() method:
foreach($items->childNodes as $item) {
if ($item->hasChildNodes()) {
$childs = $item->childNodes;
foreach($childs as $i) {
echo $i->nodeValue . "<br />";
}
}
}

Categories