PHP SimpleXMLElement object get data - php

I have the following XML:
<Root>
<personalData>
<userName>John Tom</userName>
<email>mail#example.com</email>
</personalData>
<profesionalData>
<job>engineer</job>
<jobId>16957</jobId>
</profesionalData>
</Root>
Doing in my debugger:
$myObject->xpath('//Root/profesionalData')
I have:
: array =
0: object(SimpleXMLElement) =
job: string = engineer
jobId: string = 16957
I cannot get hold of the jobId 16957.
What do I have to do?

$root = simplexml_load_file('file.xml');
$job_ids = $root->xpath('//profesionalData/jobId');
if (!$job_ids) {
die("Job IDs not found");
}
foreach ($job_ids as $id) {
// SimpleXmlElement implements __toString method, so
// you can fetch the vlaue by casting the object to string.
$id = (string)$id;
var_dump($id);
}
Sample Output
string(5) "16957"
Notes
You don't need to specify Root in the XPath expression, if you are going to fetch all profesionalData/jobId tags no matter where they are in the document, just use the double slash (//) expression. This approach may be convenient in cases, when you want to avoid registering the XML namespaces. Otherwise, you can use a strict expression like /Root/profesionalData/jobId (path from the root). By the way, your current expression (//Root/profesionalData/jobId) matches all occurrences of /Root/profesionalData/jobId in the document, e.g. /x/y/z/Root/profesionalData/jobId.
Since SimpleXmlElement::xpath function returns an array on success, or FALSE on failure, you should iterate the value with a loop, if it is a non-empty array.
SimpleXmlElement implements __toString method. The method is called when the object appears in a string context. In particular, you can cast the object to string in order to fetch string content of the node.

Related

In the second foreach loop strpos not working proberly [duplicate]

Let's say I have some XML like this
<channel>
<item>
<title>This is title 1</title>
</item>
</channel>
The code below does what I want in that it outputs the title as a string
$xml = simplexml_load_string($xmlstring);
echo $xml->channel->item->title;
Here's my problem. The code below doesn't treat the title as a string in that context so I end up with a SimpleXML object in the array instead of a string.
$foo = array( $xml->channel->item->title );
I've been working around it like this
$foo = array( sprintf("%s",$xml->channel->item->title) );
but that seems ugly.
What's the best way to force a SimpleXML object to a string, regardless of context?
Typecast the SimpleXMLObject to a string:
$foo = array( (string) $xml->channel->item->title );
The above code internally calls __toString() on the SimpleXMLObject. This method is not publicly available, as it interferes with the mapping scheme of the SimpleXMLObject, but it can still be invoked in the above manner.
You can use the PHP function
strval();
This function returns the string values of the parameter passed to it.
There is native SimpleXML method SimpleXMLElement::asXML
Depending on parameter it writes SimpleXMLElement to xml 1.0 file or just to a string:
$xml = new SimpleXMLElement($string);
$validfilename = '/temp/mylist.xml';
$xml->asXML($validfilename); // to a file
echo $xml->asXML(); // to a string
Another ugly way to do it:
$foo = array( $xml->channel->item->title."" );
It works, but it's not pretty.
The accepted answer actually returns an array containing a string, which isn't exactly what OP requested (a string).
To expand on that answer, use:
$foo = [ (string) $xml->channel->item->title ][0];
Which returns the single element of the array, a string.
To get XML data into a php array you do this:
// this gets all the outer levels into an associative php array
$header = array();
foreach($xml->children() as $child)
{
$header[$child->getName()] = sprintf("%s", $child);
}
echo "<pre>\n";
print_r($header);
echo "</pre>";
To get a childs child then just do this:
$data = array();
foreach($xml->data->children() as $child)
{
$header[$child->getName()] = sprintf("%s", $child);
}
echo "<pre>\n";
print_r($data);
echo "</pre>";
You can expand $xml-> through each level until you get what you want
You can also put all the nodes into one array without the levels or
just about any other way you want it.
Not sure if they changed the visibility of the __toString() method since the accepted answer was written but at this time it works fine for me:
var_dump($xml->channel->item->title->__toString());
OUTPUT:
string(15) "This is title 1"
Try strval($xml->channel->item->title)
There is native SimpleXML method SimpleXMLElement::asXML Depending on parameter it writes SimpleXMLElement to xml 1.0 file, Yes
$get_file= read file from path;
$itrate1=$get_file->node;
$html = $itrate1->richcontent->html;
echo $itrate1->richcontent->html->body->asXML();
print_r((string) $itrate1->richcontent->html->body->asXML());
Just put the ''. before any variable, it will convert into string.
$foo = array( ''. $xml->channel->item->title );
The following is a recursive function that will typecast all single-child elements to a String:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION - CLEAN SIMPLE XML OBJECT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function cleanSimpleXML($xmlObject = ''){
// LOOP CHILDREN
foreach ($xmlObject->children() as $child) {
// IF CONTAINS MULTIPLE CHILDREN
if(count($child->children()) > 1 ){
// RECURSE
$child = cleanSimpleXML($child);
}else{
// CAST
$child = (string)$child;
}
}
// RETURN CLEAN OBJECT
return $xmlObject;
} // END FUNCTION

Xpath regex functionality [duplicate]

I am trying to filter html tables with regex matching their id attribute. What am i doing wrong? Code i am trying to implement:
$this->xpath = new DOMXPath($this->dom);
$this->xpath->registerNamespace("php", "http://php.net/xpath");
$this->xpath->registerPHPFunctions();
foreach($xpath->query("//table[php:function('preg_match', '/post\d+/', #id)]") as $key => $row)
{
}
Error that i get: preg_match expects second param to be a string, array given.
An attribute is still a complex element according to DOM (has a namespace etc.). Use:
//table[php:function('preg_match', '/post\d+/', string(#id))]
Now, we need a boolean return, so:
function booleanPregMatch($match,$string){
return preg_match($match,$string)>0;
}
$xpath->registerPHPFunctions();
foreach($xpath->query("//table[#id and php:function('booleanPregMatch', '/post\d+/', string(#id))]") as $key => $row){
echo $row->ownerDocument->saveXML($row);
}
BTW: for more complex issues, you can of course sneakily check what's happening with this:
//table[php:function('var_dump',#id)]
It's a shame we don't have XPATH 2.0 functions available, but if you can handle this requirement with a more unreliable starts-with, I'd always prefer that over importing PHP functions.
What am i doing wrong?
The xpath expression #id (second parameter) returns an array but preg_match expects a string.
Convert it to string first: string(#id).
Next to that you need to actually compare the output to 1 as preg_match returns 1 when found:
foreach($xpath->query("//table[#id and 1 = php:function('preg_match', '/post\d+/', string(#id))]") as $key => $row)
{
var_dump($key, $row, $row->ownerDocument->saveXml($row));
}
Explanation/What happens here?:
A xpath expression will by default return a node-list (more precisely node-set). If you map a PHP function onto such expressions these sets are represented in form of an array. You can easily tests that by using var_dump:
$xpath->query("php:function('var_dump', //table)");
array(1) {
[0]=>
object(DOMElement)#3 (0) {
}
}
Same for the xpath expression #id in the context of each table element:
$xpath->query("//table[php:function('var_dump', #id)]");
array(1) {
[0]=>
object(DOMAttr)#3 (0) {
}
}
You can change that into a string typed result by making use of the xpath string function:
A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.
$xpath->query("//table[php:function('var_dump', string(#id))]");
string(4) "test"
(the table has id="test")

How do I access this numeric element in a SimpleXML object?

I'm parsing XML from a data source we use in our web application and I'm having some trouble accessing data from a specific part in the XML.
First, here's the output of a print_r on what I'm trying to access.
SimpleXMLElement Object
(
[0] =>
This is the value I'm trying to get
)
Then, here's the XML I'm trying to get.
<entry>
<activity:object>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<id>542</id>
<title>
Title string is a string
</title>
<content>
This is the value I'm trying to get
</content>
<link rel="alternate" type="html" href="#"/>
<link rel="via" type="text/html" href="#"/>
</activity:object>
</entry>
The content element is what I'm after.
When I access it with $post->xpath('activity:object')[0]->content I end up with what's above.
I've tried using $zero = 0; as well as ->content->{'0'} to access this element, but each time I just get an empty SimpleXML object returned, like below.
SimpleXMLElement Object
(
)
Is there another way to access this that I haven't found yet?
Thanks!
xpath returns a simpleXMLElement type, which has a function to convert it into a string. Try this function:
http://php.net/manual/en/simplexmlelement.tostring.php
You should just be able to access it directly:
$content = $post->xpath('//content');
echo $content[0];
With PHP 5.4 or higher you might be able to do do this:
$content = $post->xpath('//content')[0];
Or if you convert the XML to string like #kkhugs says you can use
/**
* substr_delimeters
*
* a quickly written, untested function to do some string manipulation for
* not further dedicated and unspecified things, especially abused for use
* with XML and from http://stackoverflow.com/a/27487534/367456
*
* #param string $string
* #param string $delimeterLeft
* #param string $delimeterRight
*
* #return bool|string
*/
function substr_delimeters($string, $delimeterLeft, $delimeterRight)
{
if (empty($string) || empty($delimeterLeft) || empty($delimeterRight)) {
return false;
}
$posLeft = stripos($string, $delimeterLeft);
if ($posLeft === false) {
return false;
}
$posLeft += strlen($delimeterLeft);
$posRight = stripos($string, $delimeterRight, $posLeft + 1);
if ($posRight === false) {
return false;
}
return substr($string, $posLeft, $posRight - $posLeft);
}
$content = substr_delimeters($xmlString, "<content>", "</content>");
print_r is always misleading regarding SimpleXMLElement. The output you have for example:
Code Reference:
$testate = $post->xpath('activity:object')[0]->content;
print_r($testate);
Output:
SimpleXMLElement Object
(
[0] =>
This is the value I'm trying to get
)
It does not mean that you would need to access the text you're looking for by using array index zero ([0]). Well actually, while it doesn't mean that, it does not mean that it's not possible. Confusing, I know.
However, you're looking for a string value, not the object (value). All you need to do here is to cast to string:
$testate = $post->xpath('activity:object')[0]->content;
$text = (string) $testate;
########
The important part here is really the casting to string. Like print_r already suggested to you, using the zero index would work, too:
$text = (string) $testate[0];
But the zero-index is just not necessary and only internal information.
Just important to keep with you: Don't rely to print_r with SimpleXMLElement. It is an object and print_r merely tells you here that it's one and which name it has (which object type it is), the rest it outputs within the curly brackets is internal information of that object. It's never the whole picture of the XML you have. Even if it first seems so.
Read more about that here: php SimpleXML attributes are missing
So just big warning here, keep that in mind. Consider casting to string (or use with string functions like trim()) and you're fine.
Also do not forget to read through Basic SimpleXML usage in the PHP manual.
P.S.: As the other answer shows, you're not the only one having problems to describe this magic nature of SimpleXML.
P.P.S.: you might want to learn about XML-Namespaces soon, that is when elements names have colons (you perhaps already did, I can not see it for sure from your code).

XPATH - get single value returned instead of array php

I am using Xpath in PHP - I know that my query will return either 0 or 1 results.
If 1 result is returned I do not want it as an array - which is what is returned right now. I simply want the value without having to access the [0] element of the result and cast to a string.
Is this possible?
If 1 result is returned I dont want it as an array - which is what is returned. I simply want the value without having to access the [0] element of the result and cast to a string.
That is possible with XPath's string function
A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.
and DOMXPath's evaluate method:
Returns a typed result if possible or a DOMNodeList containing all nodes matching the given XPath expression.
Example:
$dom = new DOMDocument;
$dom->loadXML('<root foo="bar"/>');
$xp = new DOMXPath($dom);
var_dump($xp->evaluate('string(/root/#foo)')); // string(3) "bar"
If there was a built in xpath way of grabbing the first and only the first node value then that would be much more preferable over writing a function to do it
You can use the position function:
The position function returns a number equal to the context position from the expression evaluation context.
Example:
$dom = new DOMDocument;
$dom->loadXML('<root><foo xml:id="f1"/><foo xml:id="f2"/></root>');
$xp = new DOMXPath($dom);
var_dump($xp->evaluate('string(/root/foo[position() = 1]/#xml:id)')); // string(2) "f1"
or the abbreviated syntax
$dom = new DOMDocument;
$dom->loadXML('<root><foo xml:id="f1"/><foo xml:id="f2"/></root>');
$xp = new DOMXPath($dom);
var_dump($xp->evaluate('string(/root/foo[1]/#xml:id)')); // string(2) "f1"
Note that when querying for descendants with // using the position function might yield multiple result due to the way the expression is evaluated.
Using 'evaluate' instead of 'query', you can do things like casting.
DOMXPath::evaluate()
Also, if you're just annoyed with doing stuff a lot of times, just write a function that does it ... that is the whole idea behind functions, right?
probably
if ($array[0]){
$string = $array[0];
}
?
if $array[0] is an array, you can rename string to new_array
if ($array[0]){
$new_array = $array[0];
}
Your question suggests that you are using SimpleXML because you talk about an array. However long-time ago you accepted an answer giving an answer with DOMDocument. In any case other users go here looking for a solution in SimpleXML it works a little differently:
list($first) = $xml->xpath('//element') + array(NULL);
The element in $first if not NULL (for no elements) then still will be of type SimpleXMLElement (either an element node or an attribute node depending on the xpath query), however you can just cast it to string in PHP and done or you just use it in string context, like with echo:
echo $first;
You can write it most simply like this:
$string = #$array[0];
The # operator will suppress errors, making $string null if $array is empty.

Using regex to filter attributes in xpath with php

I am trying to filter html tables with regex matching their id attribute. What am i doing wrong? Code i am trying to implement:
$this->xpath = new DOMXPath($this->dom);
$this->xpath->registerNamespace("php", "http://php.net/xpath");
$this->xpath->registerPHPFunctions();
foreach($xpath->query("//table[php:function('preg_match', '/post\d+/', #id)]") as $key => $row)
{
}
Error that i get: preg_match expects second param to be a string, array given.
An attribute is still a complex element according to DOM (has a namespace etc.). Use:
//table[php:function('preg_match', '/post\d+/', string(#id))]
Now, we need a boolean return, so:
function booleanPregMatch($match,$string){
return preg_match($match,$string)>0;
}
$xpath->registerPHPFunctions();
foreach($xpath->query("//table[#id and php:function('booleanPregMatch', '/post\d+/', string(#id))]") as $key => $row){
echo $row->ownerDocument->saveXML($row);
}
BTW: for more complex issues, you can of course sneakily check what's happening with this:
//table[php:function('var_dump',#id)]
It's a shame we don't have XPATH 2.0 functions available, but if you can handle this requirement with a more unreliable starts-with, I'd always prefer that over importing PHP functions.
What am i doing wrong?
The xpath expression #id (second parameter) returns an array but preg_match expects a string.
Convert it to string first: string(#id).
Next to that you need to actually compare the output to 1 as preg_match returns 1 when found:
foreach($xpath->query("//table[#id and 1 = php:function('preg_match', '/post\d+/', string(#id))]") as $key => $row)
{
var_dump($key, $row, $row->ownerDocument->saveXml($row));
}
Explanation/What happens here?:
A xpath expression will by default return a node-list (more precisely node-set). If you map a PHP function onto such expressions these sets are represented in form of an array. You can easily tests that by using var_dump:
$xpath->query("php:function('var_dump', //table)");
array(1) {
[0]=>
object(DOMElement)#3 (0) {
}
}
Same for the xpath expression #id in the context of each table element:
$xpath->query("//table[php:function('var_dump', #id)]");
array(1) {
[0]=>
object(DOMAttr)#3 (0) {
}
}
You can change that into a string typed result by making use of the xpath string function:
A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.
$xpath->query("//table[php:function('var_dump', string(#id))]");
string(4) "test"
(the table has id="test")

Categories