XMl parsing query in php - php

I'm parsing xml nodes and I'm doing something wrong but I don't know what.
I have this xml:
$xml=" <root>
<info>
<company/>
<user_id>43</user_id>
<currency>EUR</currency>
</info>
<products>
<product>
<id>1336</id>
<pn>NX.RZNAA.003</pn>
<stock>1.00</stock>
</product>
<product>
<id>1337</id>
<pn>NX.RZNAA.004</pn>
<stock>4.00</stock>
<item_number>5</item_number>
</product>
</products>
</root>";
As you can see I have two "product" nodes with child nodes. But in first node "product" I have one node less then in second. In second the node "item_number" is added (it's optional node, if it has value it is in xml otherwise not) . When I'm parsing this nodes my parser returns value from second nodes "product" even if I'm on first node.
Anyone know what is the problem here?
Here is my code:
$xmlDoc = new DOMDocument();
$xmlDoc->load($xml) ;
$xpath = new DOMXPath($xmlDoc);
$tagForCount="count(//".$arrayPolja[0].")";
$x=$xmlDoc->getElementsByTagName("product");
$xpath = new DomXpath($xmlDoc);
$count = 3;
$arrayPolja[0] = "id";
$arrayPolja[1] = "pn";
$arrayPolja[2] = "stock";
$arrayPolja[3] = "item_number";
$r=0;
foreach ($x as $product) {
$i=0;
while ($i<=$count)
{
$polje=$arrayPolja[$i];
$values[$i]=$xpath->query($polje, $product)->item($r)->textContent;
$i++;
}
$r++;
}

For one, the second iteration in the loop is overriding the values of your array "$values" which is why you'd only be seeing the values from the second product node (if you are inspecting the "$values" array which is what I'm assuming you are doing).
Try this:
$xmlDoc = new DOMDocument();
$xmlDoc->load($xml);
$xpath = new DOMXPath($xmlDoc);
$x = $xmlDoc->getElementsByTagName("product");
$array = array('id','pn','stock','item_number');
$values = array();
foreach ($x as $product) {
$data = array();
// node name here
$data['node'] = $product->nodeName;
foreach ($array as $v){
$obj = $xpath->query($v, $product)->item(0);
if (gettype($obj) == 'object'){
$data[$v] = $obj->textContent;
}
}
$values[] = $data;
}
echo '<pre>' . print_r($values, true). '</pre>';
That should produce this:
Array
(
[0] => Array
(
[node] => product
[id] => 1336
[pn] => NX.RZNAA.003
[stock] => 1.00
)
[1] => Array
(
[node] => product
[id] => 1337
[pn] => NX.RZNAA.004
[stock] => 4.00
[item_number] => 5
)

Related

adding node in xml file via php

I have a php array and I need to convert it into xml file but I cant get the format I want.
Array:
Array
(
[1] => Array
(
[2000307] => Array
(
[eventid] => 2000307
[eveseq] => 100
[fee] => 200
)
[2000310] => Array
(
[eventid] => 2000310
[eveseq] =>101
[fee] => 300
)
)
)
convert array to xml:
$xml = new SimpleXMLElement('<Event/>');
event_to_xm($array, $xml);
function event_to_xml($array, &$xml) {
$array = json_decode($array,TRUE);
foreach($array as $key => $value) {
foreach($value as $id => $index) {
foreach($index as $title => $result) {
if(is_array($result)) {
if(!is_numeric($title)){
$subnode = $xml->addChild($title);
array_to_xml($result, $xml);
} else {
array_to_xml($result, $xml);
}
} else {
$xml->addChild($title, htmlspecialchars($result));
}
}
}
}
}
event.xml:
<?xml version="1.0"?>
<Event>
<eventid>2000307</eventid>
<eveseq>100</eveseq>
<fee>zz</fee>
<eventid>2000310</eventid>
<eveseq>101</eveseq>
<fee>0</fee>
</Event>
What I expect is that it will create a cd tag when a new array begin:
xml:
<?xml version="1.0"?>
<Event>
<cd>
<eventid>2000307</eventid>
<eveseq>100</eveseq>
<fee>200</fee>
</cd>
<cd>
<eventid>2000310</eventid>
<eveseq>101</eveseq>
<fee>300</fee>
</cd>
</Event>
What I tried:
I tried to direct add a attribute but I encounter this error Call to a member function addChild() on null
$xml=new SimpleXMLElement("event.xml", 0, TRUE);
$child = $xml->event[0]->addChild("cd");
I would take a somewhat different approach - first, use DOMDocument instead of SimpleXML, and, second, use xpath and a fragment to insert elements into the document. Something like this:
$events = array(
"2000307" => array(
"eventid" => 2000307,
"eveseq" => 100,
"fee" => 200,
),
"2000310" => array(
"eventid" => 20003010,
"eveseq" => 101,
"fee" => 300,
)
);
#create a blank document with a root element
$xml = <<<'XML'
<?xml version="1.0"?>
<Event></Event>
XML;
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
$expr = "//Event";
#indicate where to insert the new elements
$dest = $xpath->query($expr);
foreach($events as $event) {
$fragment = $document->createDocumentFragment();
#this is the new element to be inserted
$instance = " <cd>
<eventid>{$event['eventid']}</eventid>
<eveseq>{$event['eveseq']}</eveseq>
<fee>{$event['fee']}</fee>
</cd>\n";
$fragment->appendXML($instance);
$dest[0]->appendChild($fragment);
}
$document->formatOutput = TRUE;
echo $document->saveXML();
Output should be your expected output.
You can iterate array data and construct new simplexml object with new 'cd' child. Try like this:
$data = array (
"2000307" => array(
"eventid" => 2000307,
"eveseq" => 100,
"fee" => 200,
),
"2000310" => array(
"eventid" => 20003010,
"eveseq" => 101,
"fee" => 300,
)
);
function to_xml(SimpleXMLElement $object, array $data)
{
foreach ($data as $key => $value) {
if (is_array($value)) {
$new_object = $object->addChild('cd');
to_xml($new_object, $value);
} else {
$object->addChild($key, $value);
}
}
}
$xml = new SimpleXMLElement('<event/>');
to_xml($xml, $data);
print $xml->asXML();
I found a way of doing it and without iterate array data
$xml = new SimpleXMLElement('<Event/>');
event_to_xm($array, $xml);
function event_to_xml($array, &$xml) {
$array = json_decode($array,TRUE);
foreach($array as $key => $value) {
foreach($value as $id => $index) {
if(is_array($value)){
$neweve = $xml->addChild('cd');
}
foreach($index as $title => $result) {
if(is_array($result)) {
$subnode = $xml->addChild($title);
event_to_xml($subnode, $xml);
} else {
$neweve->addChild($title, htmlspecialchars($result));
}
}
}
}
}

Get values from SimpleXMLElement Object for php array

I need help with this issue, it seems I can't get targetCurrency out of the SimpleXMLElement Object
$xmlString = <<<XML
<channel>
<title>XML ~~ Exchange Rates ~~</title>
<language>en</language>
<item>
<baseCurrency>USD</baseCurrency>
<targetCurrency>EUR</targetCurrency>
<targetName>Euro</targetName>
<exchangeRate>0.90900497</exchangeRate>
</item>
</channel>
XML;
$xml = simplexml_load_string($xmlString);
foreach($xml->item as $rate){
$rate = (string) $rate->exchangeRate;
$curr_code = (string) $rate->targetCurrency;
$money[] = array('rate' => $rate, 'curr_code' => $curr_code);
}
print_r($money);
This outputs:
Array
(
[0] => Array
(
[rate] => 0.90947603
[curr_code] =>
)
)
[curr_code] should output 'EUR'.
How can I fix it?
You are using the same variable name for two different things:
foreach($xml->item as $rate){
// at this point, $rate is the <item> element
$rate = (string) $rate->exchangeRate;
// now $rate is a string with the exchange rate
$curr_code = (string) $rate->targetCurrency;
// so now this won't work
$money[] = array('rate' => $rate, 'curr_code' => $curr_code);
}
If you were running with display_errors switched on or checking your logs, you would have seen a message like this:
Notice: Trying to get property 'targetCurrency' of non-object
Or in PHP 8, this:
Warning: Attempt to read property "targetCurrency" on string
The fix is simply to name your variables more carefully:
foreach($xml->item as $itemElement){
$rate = (string) $itemElement->exchangeRate;
$curr_code = (string) $itemElement->targetCurrency;
$money[] = array('rate' => $rate, 'curr_code' => $curr_code);
}
Try it using xpath instead:
$items = $xml->xpath("//item");
foreach($items as $item){
$rate = $item->xpath('.//exchangeRate')[0];
$curr_code = $item->xpath('.//targetCurrency')[0];
$money[] = array('rate' => $rate, 'curr_code' => $curr_code);
}
print_r($money);
The output should be as exptected.

How to get all element in xml file using with CURL and DOM put into the array?

I need some help with code. I want to get all element in xml file using with CURL and DOM into the array.
Can you help me with this?
My PHP with CURL code shown in below:
<?php
$key = 'ad12rqwagst3ewtgsdhdsdsfsdgsd';
$url = 'http://api.11street.my/rest/cateservice/category';
$request_body = 'xml data';
$headers = array(
'openapikey: ' . $key,
'Accept-Charset: utf-8',
'Content-Type: application/xml'
);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
$result = curl_exec($curl);
$arrXml = array();
$dom = new DOMDocument;
$dom->loadXML($result);
foreach ($dom->getElementsByTagName('dispEngNm') as $item) {
$arrXml[] = $dom->saveXML($item) . "<br>" . "<hr>";
}
print_r($arrXml);
?>
The example code of xml file show in below, these are three items I have:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
</ns2:categorys>
<ns2:category>
<depth>1</depth>
<dispEngNm>Women Clothing</dispEngNm>
<dispNm>Women Clothing</dispNm>
<dispNo>2021</dispNo>
<parentDispNo>0</parentDispNo>
</ns2:category>
<ns2:category>
<depth>2</depth>
<dispEngNm>Women Tops</dispEngNm>
<dispNm>Women Tops</dispNm>
<dispNo>2051</dispNo>
<parentDispNo>2021</parentDispNo>
</ns2:category>
<ns2:category>
<depth>3</depth>
<dispEngNm>Tanks & Camisoles</dispEngNm>
<dispNm>Tanks & Camisoles</dispNm>
<dispNo>2209</dispNo>
<parentDispNo>2051</parentDispNo>
</ns2:category>
</ns2:categorys>
The output is shown me below:
Array ( [0] => Women Clothing
[1] => Women Tops
[2] => Tanks & Camisoles
[3] => T-shirts
I just can get element "dispEngNm" into the array. Anyone know how can get other element "dispNm","dispNo", "parentDispNo" together with "dispEngNm" into the array?
Thanks for your helping! Wish you have a nice day!
Combining the code you have so far and using the ability of SimpleXML to iterate through the child elements easily, the code below first looks for the <category> elements (getElementsByTagName() doesn't worry about namespaces), then import it into SimpleXML so that you can just foreach() over the child elements and add them into a work array (using the tag name as the key). This just copies all of the child data into an associative array...
$dom = new DOMDocument;
$dom->loadXML($result);
foreach ($dom->getElementsByTagName('category') as $item) {
$sxml = simplexml_import_dom($item);
$newData = [];
foreach ( $sxml as $tag => $value ) {
$newData[$tag] = (string)$value;
}
$arrXml[] = $newData;
}
print_r($arrXml);
gives...
Array
(
[0] => Array
(
[depth] => 1
[dispEngNm] => Women Clothing
[dispNm] => Women Clothing
[dispNo] => 2021
[parentDispNo] => 0
)
[1] => Array
(
[depth] => 2
[dispEngNm] => Women Tops
[dispNm] => Women Tops
[dispNo] => 2051
[parentDispNo] => 2021
)
[2] => Array
(
[depth] => 3
[dispEngNm] => Tanks & Camisoles
[dispNm] => Tanks & Camisoles
[dispNo] => 2209
[parentDispNo] => 2051
)
)
Just change this code:
foreach ($dom->getElementsByTagName('dispEngNm') as $item) {
$arrXml[] = $dom->saveXML($item) . "<br>" . "<hr>";
}
by this code:
$allTags = $dom->getElementsByTagName('ns2:category');
for ($i =0; $i < $allTags.length; $i++) {
$childLen = $allTags[i].childNodes.length;
$innerChilds = $allTags[i].firstChild;
for ($j = 0; $j < $childLen; $j++) {
$arrXml[] = $dom->saveXML($innerChilds.innerHTML) . "<br>" . "<hr>";
$innerChilds = $innerChilds.nextSibling;
}
}
In the above code, I am just loading all the child nodes of ns2:category which may works.
Note: I have not tested the code. You may test this and make changes if required.
maybe you're looking for the childNodes property?
$arrXml = array();
foreach($dom->getElementsByTagName("category") as $cat){
$curr=array();
foreach($cat->childNodes as $child){
$curr[$child->nodeName]=$dom->saveXML($child);
}
$arrXml[]=$curr;
}
unset($cat,$curr,$child);
print_r($arrXml);
working example:
<?php
$xml=<<<'XML'
<ns2:category>
<depth>1</depth>
<dispEngNm>Women Clothing</dispEngNm>
<dispNm>Women Clothing</dispNm>
<dispNo>2021</dispNo>
<parentDispNo>0</parentDispNo>
</ns2:category>
<ns2:category>
<depth>2</depth>
<dispEngNm>Women Tops</dispEngNm>
<dispNm>Women Tops</dispNm>
<dispNo>2051</dispNo>
<parentDispNo>2021</parentDispNo>
</ns2:category>
<ns2:category>
<depth>3</depth>
<dispEngNm>Tanks & Camisoles</dispEngNm>
<dispNm>Tanks & Camisoles</dispNm>
<dispNo>2209</dispNo>
<parentDispNo>2051</parentDispNo>
</ns2:category>
XML;
$dom = new DOMDocument;
#$dom->loadHTML($xml);
$arrXml = array();
foreach($dom->getElementsByTagName("category") as $cat){
$curr=array();
foreach($cat->childNodes as $child){
$curr[$child->nodeName]=$dom->saveXML($child);
}
$arrXml[]=$curr;
}
unset($cat,$curr,$child);
print_r($arrXml);
btw if you don't actually want the xml but just the text content, then change
$curr[$child->nodeName]=$dom->saveXML($child);
with
$curr[$child->nodeName]=$child->textContent;
instead, and it will contain the text of the child, rather than the xml
btw here is what the sample code above with the textContent patch outputs:
Array
(
[0] => Array
(
[depth] => 1
[dispengnm] => Women Clothing
[dispnm] => Women Clothing
[dispno] => 2021
[parentdispno] => 0
)
[1] => Array
(
[depth] => 2
[dispengnm] => Women Tops
[dispnm] => Women Tops
[dispno] => 2051
[parentdispno] => 2021
)
[2] => Array
(
[depth] => 3
[dispengnm] => Tanks & Camisoles
[dispnm] => Tanks & Camisoles
[dispno] => 2209
[parentdispno] => 2051
)
)
Generic conversion into an array are possible, but most of the time you just loose features and data. DOM includes Xpath, so most of the time it makes more sense to read the data directly into a SPECIFIC array/object structure. In you case you have a list of records with parent-child relations.
Your XML includes a namespace prefix ns2 but the definition for it is missing. Look for an attribute xmlns:ns2 - its value is the actual namespace.
$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:categorys xmlns:ns2="urn:ns2">
<ns2:category>
<depth>1</depth>
<dispEngNm>Women Clothing</dispEngNm>
<dispNm>Women Clothing</dispNm>
<dispNo>2021</dispNo>
<parentDispNo>0</parentDispNo>
</ns2:category>
<ns2:category>
<depth>2</depth>
<dispEngNm>Women Tops</dispEngNm>
<dispNm>Women Tops</dispNm>
<dispNo>2051</dispNo>
<parentDispNo>2021</parentDispNo>
</ns2:category>
<ns2:category>
<depth>3</depth>
<dispEngNm>Tanks & Camisoles</dispEngNm>
<dispNm>Tanks & Camisoles</dispNm>
<dispNo>2209</dispNo>
<parentDispNo>2051</parentDispNo>
</ns2:category>
</ns2:categorys>
XML;
// this needs to be the value of the xmlns:ns2 attribute
$ns_categories = 'urn:ns2';
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
// register an prefix for the namespace
$xpath->registerNamespace('c', $ns_categories);
$categories = [];
$tree = [];
// iterate the categories
foreach ($xpath->evaluate('//c:categorys/c:category') as $category) {
// casting a node list in Xpath will return the content of the first node
$id = (int)$xpath->evaluate('number(dispNo)', $category);
$parentId = (int)$xpath->evaluate('number(parentDispNo)', $category);
// store category data
$categories[$id] = [
'id' => $id,
'parent_id' => $parentId,
'depth' => (int)$xpath->evaluate('number(depth)', $category),
'caption' => $xpath->evaluate('string(dispNm)', $category),
'caption_english' => $xpath->evaluate('string(dispEngNm)', $category),
];
// store child ids for each parent
if (!isset($tree[$parentId])) {
$tree[$parentId] = [];
}
$tree[$parentId][] = $id;
}
var_dump($categories, $tree);
The main differences that if you read the result of a generic conversion the array keys can have different names or can be missing. The result structure depends directly on the input. If you read the data into a specific structure (like my example) you know which array keys are there.

how to pass XML setAttribute in php foreach

I want to generate XML for an array which has many field and i want to set that fields in a single XML element as attributes of that element below is my php code.
<?php
$rs=array();//values come in $rs from database, it has many fields
$doc = new DOMDocument();
$doc->formatOutput = true;
$root = $doc->createElement( "slides" );
$doc->appendChild( $root );
$firstchild = $doc->createElement( "device" );
$fs=$doc->appendChild( $firstchild );
foreach( $rs as $key=>$value ){
$fs->setAttribute($key,$value);
}
$xml_string = $doc->saveXML();
echo $xml_string;
But this is not working for me, it gives me an error like:
DOMElement:setAttribute() expects parameter 2 to be string,array given
My $rs array structure is like:
Array
(
[0] => Array
(
[id] => 1
[name] => dfd
[width] => 2
[height] => 1
[resolution] =>
[space] =>
)
)
, i want output like:
<slides>
<device id="12" name="mydevice" color="red" .....and so on></device>
</slides>
You did some mistakes, the main of them that you appendchild element to Document but not to parent
$doc = new DOMDocument();
$doc->formatOutput = true;
$root = $doc->createElement( "slides" );
$doc->appendChild( $root );
$fs = $doc->createElement( "device" );
$root->appendChild( $fs );
foreach( $rs as $key=>$value ){
$fs->setAttribute($key,$value);
}
$xml_string = $doc->saveXML();
echo $xml_string;
working example
You need to create each attribute and assign it to the correct node, something like this:
foreach( $rs as $key => $value ){
$attrib=$doc->createAttribute($key);
$attrib->nodeValue=$value;
$fs->appendChild( $attrib );
}
The $rs variable is an array of the records with an array of the fields as an element. So you need two nested loops.
The outer loop iterates the records and creates an device element node for each record. The inner loop iterates the fields and adds the attributes.
$rs = [
[
'id' => 1,
'name' => 'dfd',
'width' => 2,
'height' => 1,
'resolution' => '',
'space' => ''
]
];
$document = new DOMDocument();
$slides = $document->appendChild(
$document->createElement("slides")
);
foreach ($rs as $record) {
$device = $slides->appendChild(
$document->createElement("device")
);
foreach($record as $key => $value){
$device->setAttribute($key,$value);
}
}
$document->formatOutput = true;
echo $document->saveXML();
Output:
<?xml version="1.0"?>
<slides>
<device id="1" name="dfd" width="2" height="1" resolution="" space=""/>
</slides>

PHP XMLReader dealing with missing element

I'm using XMLReader to parse a file which i don't control. The file is missing some elements, example below. Where book 2 is missing an info element, i still want to add this to the array. I realise i could use a combination of XMLReader and SimpleXML... but i'd like to see if this is possible?
<book>
<title>book 1</title>
<info>book 1 info</info>
</book>
<book>
<title>book 2</title>
</book>
<book>
<title>book 3</title>
<info>book 3 info</info>
</book>
I have the following XMLReader function:
<?php
function parseXML($args){
extract($args);
$xml = new XMLReader();
$xml->open($file) or die('Cannot open file');
while($xml->read()){
switch($xml->nodeType){
case(XMLREADER::ELEMENT):{
if(in_array($xml->localName, $nodes, true)){
$lName = $xml->localName;
$val = $xml->readString();
$xml->read();
$child[$lName] = trim($val);
if($lName == end($nodes)){
$arr[] = $child;
}
}
}
break;
}
}
$xml->close($file);
return $arr;
}
$settings = array(
'file' => 'books.xml',
'nodes' => array('name', 'info')
);
$books = parseXML($settings);
var_dump($books);
?>
which produces:
Array
(
[0] => Array
(
[name] => book 1
[info] => book 1 info
)
[1] => Array
(
[name] => book 3
[info] => book 3 info
)
)
You could rethink your logic a little. The snippet below builds up a $book array for each <book> element, starting each time with default values (an empty string) in case a <name> or <info> does not exist for the book.
As <name> and <info> elements are reached, their contents are added to the current $book array.
When reaching a </book>, the $book array is added to the main array of $books.
Building $books
function parseXML($args){
extract($args);
$xml = new XMLReader();
$xml->open($file) or die('Cannot open file');
$books = array();
while ($xml->read()) {
// <book> - start a new child array with default values
if ($xml->nodeType === XMLReader::ELEMENT
&& $xml->localName === 'book') {
$book = array_fill_keys($nodes, '');
// </book> - add the child array to main array
} elseif ($xml->nodeType === XMLReader::END_ELEMENT
&& $xml->localName === 'book') {
$books[] = $book;
// <info> or <title> - add to child array
} elseif ($xml->nodeType === XMLReader::ELEMENT
&& in_array($xml->localName, $nodes, TRUE)) {
$name = $xml->localName;
$val = $xml->readString();
$book[$name] = trim($val);
}
}
$xml->close();
return $books;
}
Resulting array
$books = array(
array('title' => 'book 1', 'info' => 'book 1 info'),
array('title' => 'book 2', 'info' => ''),
array('title' => 'book 3', 'info' => 'book 3 info'),
);

Categories