Problems with a foreach loop in a SimpleXMLelement - php

I'm trying to teach myself to handle the SimpleXMP_read_file command / object.
So I have looked deeply into the problem at "simpleXMLElement attributes and foreach" (
simpleXMLElement attributes and foreach ).
copied it bit by bit into my PHP browser and ran it.
test.xml:
<?xml version="1.0" encoding="utf-8"?>
<response result="0">
<reports>
<get count="2">
<row a="first" b="second" comment="test" c=""/>
<row a="first1" b="second2" comment="test2" c=""/>
</get>
</reports>
</response>
modified the php like this:
PHP:
$xml = simplexml_load_file('test.xml');
$rows = $xml->xpath('reports/get/row');
foreach($rows as $row)
{
foreach($row->attributes() as $key)
{
echo ('test: '.$key['a'] .' '.$key['b'].' '.$key['comment'].' '.$key['c'].'<br>') ;
}
}
I get no errors but only 2 lines :
test
test
No data.
Can anyone tell me why ?

You are doing a foreach over $row->attributes(). Therefore each iteration of the loop is a different attribute. None of the attributes have a $key['a'] value set.
You probably want to do:
foreach($rows as $row){
$key = $row->attributes();
echo 'test: '.$key['a'] .' '.$key['b'].' '.$key['comment'].' '.$key['c'].'<br>';
}

after doing a print_r($rows); i have got the following. Now you can access the array elements and class objects directly by $row->attributes['a'] etc.
foreach($rows as $row){
$xmlObjElement = json_decode(json_encode((array)$row), TRUE);
foreach($xmlObjElement as $fo){
print_r( $fo );
}
}
Output:
Array
(
[a] => first1
[b] => second2
[comment] => test2
[c] =>
)
Now you can access like $fo['a'] etc...

Related

each node attribute value of xml in php

Im trying to get the catId value. But i can see only the category value.
My xml file looks below:
<sample>
<Item ItemNumber="00000088" FormattedItemNumber="00000-088">
<CompatibleModels />
<Category CatId="160" > test 123 </Category>
<Images />
<Documents />
<RequiredItems />
</Item>
</sample>
$xml = simplexml_load_file("test.xml");
print_r($xml);
[sample] => Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[ItemNumber] => 00000088
[FormattedItemNumber] => 00000-088
)
[Category] => Bags/Luggage 123
)
how can get the CatId value? Why the cateId value is missing?
You can do it by many ways. Let's try-
foreach ($xml as $items) {
echo $items->Category['CatId'];
}
WORKING DEMO: https://3v4l.org/Onqe2
print_r doesn't really work with SimpleXML objects. But from the sample data you have provided you can simply access the CatId attribute using
echo $xml->Item->Category['CatId'];
You can loop and get it by using following snippet, please refer inline documentation for explanation
$xml1 = simplexml_load_file("test.xml") or die("Error: Cannot create object");
foreach ($xml1->children() as $items1) { // children mean item
echo ($items1->category['catid']); // for category tag get catid attribute
}

Json Encode or Serialize an XML

I have some xml, this is a simple version of it.
<xml>
<items>
<item abc="123">item one</item>
<item abc="456">item two</item>
</items>
</xml>
Using SimpleXML on the content,
$obj = simplexml_load_string( $xml );
I can use $obj->xpath( '//items/item' ); and get access to the #attributes.
I need an array result, so I have tried the json_decode(json_encode($obj),true) trick, but that looks to be removing access to the #attributes (ie. abc="123").
Is there another way of doing this, that provides access to the attributes and leaves me with an array?
You need to call attributes() function.
Sample code:
$xmlString = '<xml>
<items>
<item abc="123">item one</item>
<item abc="456">item two</item>
</items>
</xml>';
$xml = new SimpleXMLElement($xmlString);
foreach( $xml->items->item as $value){
$my_array[] = strval($value->attributes());
}
print_r($my_array);
Eval
You can go the route with json_encode and json_decode and you can add the stuff you're missing because that json_encode-ing follows some specific rules with SimpleXMLElement.
If you're interested into the rules and their details, I have written two blog-posts about it:
SimpleXML and JSON Encode in PHP – Part I
SimpleXML and JSON Encode in PHP – Part II
For you perhaps more interesing is the third part which shows how you can modify the json serialization and provide your own format (e.g. to preserve the attributes):
SimpleXML and JSON Encode in PHP – Part III and End
It ships with a full blown example, here is an excerpt in code:
$xml = '<xml>
<items>
<item abc="123">item one</item>
<item abc="456">item two</item>
</items>
</xml>';
$obj = simplexml_load_string($xml, 'JsonXMLElement');
echo $json = json_encode($obj, JSON_PRETTY_PRINT), "\n";
print_r(json_decode($json, TRUE));
Output of JSON and the array is as following, note that the attributes are part of it:
{
"items": {
"item": [
{
"#attributes": {
"abc": "123"
},
"#text": "item one"
},
{
"#attributes": {
"abc": "456"
},
"#text": "item two"
}
]
}
}
Array
(
[items] => Array
(
[item] => Array
(
[0] => Array
(
[#attributes] => Array
(
[abc] => 123
)
[#text] => item one
)
[1] => Array
(
[#attributes] => Array
(
[abc] => 456
)
[#text] => item two
)
)
)
)
$xml = new SimpleXMLElement($xmlString);
$xml is now an object. To get the value of an attribute:
$xml->something['id'];
Where 'id' is the name of the attribute.
While it's theoretically possible to write a generic conversion from XML to PHP or JSON structures, it is very hard to capture all the subtleties that might be present - the distinction between child elements and attributes, text content alongside attributes (as you have here) or even alongside child elements, multiple child nodes with the same name, whether order of child elements and text nodes is important (e.g. in XHTML or DocBook), etc, etc.
If you have a specific format you need to produce, it will generally be much easier to use an API - like SimpleXML - to loop over the XML and produce the structure you need.
You don't specify the structure you want to achieve, but the general approach given your input would be to loop over each item, and either access known attributes, or loop over each attribute:
$sxml = simplexml_load_string( $xml );
$final_array = array();
foreach ( $sxml->items->item as $xml_item )
{
$formatted_item = array();
// Text content of item
$formatted_item['content'] = (string)$xml_item;
// Specifically get 'abc' attribute
$formatted_item['abc'] = (string)$xml_item['abc'];
// Maybe one of the attributes is an integer
$formatted_item['foo_id'] = (int)$xml_item['foo_id'];
// Or maybe you want to loop over lots of possible attributes
foreach ( $xml_item->attributes() as $attr_name => $attr_value )
{
$formatted_item['attrib:' . $attr_name] = (string)$attr_value;
}
// Add it to a final list
$final_array[] = $formatted_item;
// Or maybe you want that array to be keyed on one of the attributes
$final_array[ (string)$xml_item['key'] ] = $formatted_item;
}
Here is a class I've found that is able to process XML into array very nicely: http://outlandish.com/blog/xml-to-json/ (backup). Converting to json is a matter of a json_encode() call.

access php simplexml with namespaces

-
Hello Everyone,
I'm trying to access data in a XML file:
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://dublincore.org/documents/dcmi- namespace/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd";>
<responseDate>2013-04-15T12:14:31Z</responseDate>
<ListRecords>
<record>
<header>
<identifier>
a1b31ab2-9efe-11df-9922-efbb156aa6c1:01442b82-59a4-627e-800f-c63de74fc109
</identifier>
<datestamp>2012-08-16T14:42:52Z</datestamp>
</header>
<metadata>
<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd";>
<dc:description>...</dc:description>
<dc:date>1921</dc:date>
<dc:identifier>K11510</dc:identifier>
<dc:source>Waterschap Vallei & Eem</dc:source>
<dc:source>...</dc:source>
<dc:source>610</dc:source>
<dc:coverage>Bunschoten</dc:coverage>
<dc:coverage>Veendijk</dc:coverage>
<dc:coverage>Spakenburg</dc:coverage>
</oai_dc:dc>
</metadata>
<about>...</about>
</record>
This a a example of the XML.
I need to access data like dc:date dc:source etc.
Anyone any ideas?
Best regards,
Tim
-- UPDATE --
I'm now trying this:
foreach( $xml->ListRecords as $records )
{
foreach( $records AS $record )
{
$data = $record->children( 'http://www.openarchives.org/OAI/2.0/oai_dc/' );
$rows = $data->children( 'http://purl.org/dc/elements/1.1/' );
echo $rows->date;
break;
}
break;
}
You have nested elements that are in different XML namespaces. In concrete you have got two additional namespaces involved:
$nsUriOaiDc = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
$nsUriDc = 'http://purl.org/dc/elements/1.1/';
The first one is for the <oai_dc:dc> element which contains the second ones * <dc:*>* elements like <dc:description> and so on. Those are the elements you're looking for.
In your code you already have a good nose how this works:
$data = $record->children( 'http://www.openarchives.org/OAI/2.0/oai_dc/' );
$rows = $data->children( 'http://purl.org/dc/elements/1.1/' );
However there is a little mistake: the $data children are not children of $record but of $record->metadata.
You also do not need to nest two foreach into each other. The code example:
$nsUriOaiDc = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
$nsUriDc = 'http://purl.org/dc/elements/1.1/';
$records = $xml->ListRecords->record;
foreach ($records as $record)
{
$data = $record->metadata->children($nsUriOaiDc);
$rows = $data->children($nsUriDc);
echo $rows->date;
break;
}
/** output: 1921 **/
If you are running into problems like these, you can make use of $record->asXML('php://output'); to show which element(s) you are currently traversing to.
I think this is what you're looking for. Hope it helps ;)
use DomDocument for this like access to dc:date
$STR='
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://dublincore.org/documents/dcmi- namespace/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd";>
<responseDate>2013-04-15T12:14:31Z</responseDate>
<ListRecords>
<record>
<header> <identifier> a1b31ab2-9efe-11df-9922-efbb156aa6c1:01442b82-59a4-627e-800f-c63de74fc109 </identifier>
<datestamp>2012-08-16T14:42:52Z</datestamp>
</header>
<metadata>
<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd";>
<dc:description>...</dc:description>
<dc:date>1921</dc:date>
<dc:identifier>K11510</dc:identifier>
<dc:source>Waterschap Vallei & Eem</dc:source>
<dc:source>...</dc:source>
<dc:source>610</dc:source>
<dc:coverage>Bunschoten</dc:coverage>
<dc:coverage>Veendijk</dc:coverage>
<dc:coverage>Spakenburg</dc:coverage>
</oai_dc:dc>
</metadata>
<about>...</about>
</record>';
$dom= new DOMDocument;
$STR= str_replace("&", "&", $STR); // disguise &s going IN to loadXML()
// $dom->substituteEntities = true; // collapse &s going OUT to transformToXML()
$dom->recover = TRUE;
#$dom->loadHTML('<?xml encoding="UTF-8">' .$STR);
// dirty fix
foreach ($dom->childNodes as $item)
if ($item->nodeType == XML_PI_NODE)
$dom->removeChild($item); // remove hack
$dom->encoding = 'UTF-8'; // insert proper
print_r($doc->getElementsByTagName('dc')->item(0)->getElementsByTagName('date')->item(0)->textContent);
output:
1921
or access to dc:source
$source= $doc->getElementsByTagName('dc')->item(0)->getElementsByTagName('source');
foreach($source as $value){
echo $value->textContent."\n";
}
output:
Waterschap Vallei & Eem
...
610
or give you array
$array=array();
$source= $doc->getElementsByTagName('dc')->item(0)->getElementsByTagName("*");
foreach($source as $value){
$array[$value->localName][]=$value->textContent."\n";
}
print_r($array);
output:
Array
(
[description] => Array
(
[0] => ...
)
[date] => Array
(
[0] => 1921
)
[identifier] => Array
(
[0] => K11510
)
[source] => Array
(
[0] => Waterschap Vallei & Eem
[1] => ...
[2] => 610
)
[coverage] => Array
(
[0] => Bunschoten
[1] => Veendijk
[2] => Spakenburg
)
)
Using XPath makes dealing with namespaces more straightforward:
<?php
// load the XML into a DOM document
$doc = new DOMDocument;
$doc->load('oai-response.xml'); // or use $doc->loadXML($xml) for an XML string
// bind the DOM document to an XPath object
$xpath = new DOMXPath($doc);
// map all the XML namespaces to prefixes, for use in XPath queries
$xpath->registerNamespace('oai', 'http://www.openarchives.org/OAI/2.0/');
$xpath->registerNamespace('oai_dc', 'http://www.openarchives.org/OAI/2.0/oai_dc/');
$xpath->registerNamespace('dc', 'http://purl.org/dc/elements/1.1/');
// identify each record using an XPath query
// collect data as either strings or arrays of strings
foreach ($xpath->query('oai:ListRecords/oai:record/oai:metadata/oai_dc:dc') as $item) {
$data = array(
'date' => $xpath->evaluate('string(dc:date)', $item), // $item is the context for this query
'source' => array(),
);
foreach ($xpath->query('dc:source', $item) as $source) {
$data['source'][] = $source->textContent;
}
print_r($data);
}

dynamcially retrieve xml data

I am working with simplexml to retrieve data.
The data I am having trouble with looks like this:
SimpleXMLElement Object
(
[listing_pics_array] => SimpleXMLElement Object
(
[pic0] => http://imagepath.com_1.jpg
[pic1] => http://imagepath.com_2.jpg
[pic2] => http://imagepath.com_3.jpg
[pic3] => http://imagepath.com_4.jpg
[pic4] => http://imagepath.com_5.jpg
[pic5] => http://imagepath.com_6.jpg
[pic6] => http://imagepath.com_7.jpg
)
)
I am able to retrieve the url like this:
(string)$listing->listing_pics_array->pic0[0]
I want to dynamically loop over the listing_pic_array because I have no idea how many pics will be returned.
I want to do something like this:
foreach ($listing->listing_pics_array as $key => $value) {
echo '<img src="'.$value .'" alt="" />';
}
but I am getting nothing returned.
Thanks.
Try casting the SimpleXMLElement into an Array. This one works fine for me:
$sXml = <<<XML
<?xml version='1.0'?>
<root>
<listing_pics_array>
<pic0>pic0 foo</pic0>
<pic1>pic1 bar</pic1>
</listing_pics_array>
</root>
XML;
$oXml = simplexml_load_string($sXml);
foreach ((array)$oXml->listing_pics_array as $sCurrentValue) {
echo $sCurrentValue . PHP_EOL;
}
Yielded
pic0 foo
pic1 bar
HTH

XPath and PHP troubleshooting

I get this error:
Notice: Trying to get property of non-object in
Applies to:
echo $result->Data;
And this output:
Array ()
Background Informations
A function returns a string which contains an XML file.
I want to get some data from two tags and deal with them on their own.
String Data
$data="
<SyncML xmlns='SYNCML:SYNCML1.0'>
<SyncHdr>
</SyncHdr>
<SyncBody>
<betameta>
WANT 1
</betameta>
<Add>
<Data>
WANT 2
</Data>
</Add>
</SyncBody>
</SyncML>";
In the above data, I want values "WANT 1" and "WANT 2"
Code so far
$xml = simplexml_load_string($data);
$result = $xml->xpath("/SyncML/SyncBody");
print_r($result);
echo $result->Data;
$xml->registerXPathNamespace('default','SYNCML:SYNCML1.0');
$result = $xml->xpath("/default:SyncML/default:SyncBody");
Remove the trailing slash.
The only solution I can find is the following:
<?php
$data= <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<SyncML>
<SyncHdr>
</SyncHdr>
<SyncBody>
<betameta>
WANT 1
</betameta>
<Add>
<Data>
WANT 2
</Data>
</Add>
</SyncBody>
</SyncML>
XML;
$xml = simplexml_load_string($data);
$result = $xml->xpath("/SyncML/SyncBody");
print_r($result);
echo $result;
is there anyway you can loose the xmlns?
This will output:
Array
(
[0] => SimpleXMLElement Object
(
[betameta] =>
WANT 1
[Add] => SimpleXMLElement Object
(
[Data] =>
WANT 2
)
)
)

Categories