What's wrong with this attempt to use SimpleXML? - php

I can't seem to get anything to work with simpleXML for PHP. What is wrong with the following:
$xml = simplexml_load_string('<book><title>The Title</title></book>');
$title = $xml->book->title;
echo "<pre>title = $title\n</pre>";
The resulting output is:
title =
Why isn't the output as follows?
title = The Title
Please advise.

Since <book> is the root node of this snippet, you need $xml->title rather than $xml->book->title.
$xml = simplexml_load_string('<book><title>The Title</title></book>');
$title = $xml->title;
echo "<pre>title = $title\n</pre>";
// Prints
<pre>title = The Title
</pre>
The structure is more easily discovered if you var_dump() it:
var_dump($xml);
object(SimpleXMLElement)#1 (1) {
["title"]=>
string(9) "The Title"
}

Try
$str = '<book><title>The Title</title></book>';
$xml = new SimpleXMLElement($str);
$title = $xml->book->title;
echo $title;
What I suspect the problem being is that you haven't created the XML object and are trying to use a method from that object. Thats my assumption given your code snippet.
Take a look at PHP: Simple XML

Related

How to extract the text in a SimpleXmlElement object? [duplicate]

Given the php code:
$xml = <<<EOF
<articles>
<article>
This is a link
<link>Title</link>
with some text following it.
</article>
</articles>
EOF;
function traverse($xml) {
$result = "";
foreach($xml->children() as $x) {
if ($x->count()) {
$result .= traverse($x);
}
else {
$result .= $x;
}
}
return $result;
}
$parser = new SimpleXMLElement($xml);
traverse($parser);
I expected the function traverse() to return:
This is a link Title with some text following it.
However, it returns only:
Title
Is there a way to get the expected result using simpleXML (obviously for the purpose of consuming the data rather than just returning it as in this simple example)?
There might be ways to achieve what you want using only SimpleXML, but in this case, the simplest way to do it is to use DOM. The good news is if you're already using SimpleXML, you don't have to change anything as DOM and SimpleXML are basically interchangeable:
// either
$articles = simplexml_load_string($xml);
echo dom_import_simplexml($articles)->textContent;
// or
$dom = new DOMDocument;
$dom->loadXML($xml);
echo $dom->documentElement->textContent;
Assuming your task is to iterate over each <article/> and get its content, your code will look like
$articles = simplexml_load_string($xml);
foreach ($articles->article as $article)
{
$articleText = dom_import_simplexml($article)->textContent;
}
node->asXML();// It's the simple solution i think !!
So, the simple answer to my question was: Simplexml can't process this kind of XML. Use DomDocument instead.
This example shows how to traverse the entire XML. It seems that DomDocument will work with any XML whereas SimpleXML requires the XML to be simple.
function attrs($list) {
$result = "";
foreach ($list as $attr) {
$result .= " $attr->name='$attr->value'";
}
return $result;
}
function parseTree($xml) {
$result = "";
foreach ($xml->childNodes AS $item) {
if ($item->nodeType == 1) {
$result .= "<$item->nodeName" . attrs($item->attributes) . ">" . parseTree($item) . "</$item->nodeName>";
}
else {
$result .= $item->nodeValue;
}
}
return $result;
}
$xmlDoc = new DOMDocument();
$xmlDoc->loadXML($xml);
print parseTree($xmlDoc->documentElement);
You could also load the xml using simpleXML and then convert it to DOM using dom_import_simplexml() as Josh said. This would be useful, if you are using simpleXml to filter nodes for parsing, e.g. using XPath.
However, I don't actually use simpleXML, so for me that would be taking the long way around.
$simpleXml = new SimpleXMLElement($xml);
$xmlDom = dom_import_simplexml($simpleXml);
print parseTree($xmlDom);
Thank you for all the help!
You can get the text node of a DOM element with simplexml just by treating it like a string:
foreach($xml->children() as $x) {
$result .= "$x"
However, this prints out:
This is a link
with some text following it.
TitleTitle
..because the text node is treated as one block and there is no way to tell where the child fits in inside the text node. The child node is also added twice because of the other else {}, but you can just take that out.
Sorry if I didn't help much, but I don't think there's any way to find out where the child node fits in the text node unless the xml is consistent (but then, why not use tags). If you know what element you want to strip the text out of, strip_tags() will work great.
This has already been answered, but CASTING TO STRING ( i.e. $sString = (string) oSimpleXMLNode->TagName) always worked for me.
Try this:
$parser = new SimpleXMLElement($xml);
echo html_entity_decode(strip_tags($parser->asXML()));
That's pretty much equivalent to:
$parser = simplexml_load_string($xml);
echo dom_import_simplexml($parser)->textContent;
Like #tandu said, it's not possible, but if you can modify your XML, this will work:
$xml = <<<EOF
<articles>
<article>
This is a link
</article>
<link>Title</link>
<article>
with some text following it.
</article>
</articles>

get the value of the <doc> xml element to use in query

I have this code
$xml = $mbpay->processPaymentInfo();
Log::info('Showing $mbpay:' . var_export($xml, true));
The log shows like:
[2018-07-06 12:40:56] local.INFO: Showing $mbpay: '<?xml version="1.0" encoding="ISO-8859-1" ?>
<getmb_id>
<status>ok</status>
<!---->
<doc>000...</doc>
</getmb_id>'
With this xml, I want to get from the notifications table the value of the column "r_id" where the value of the "doc" column is equal to the "doc" element of the $xml above.
Do you know how to properly get the value of the xml element so is possible to do the query to get the r_id?
The query should be something like " $rID = Notifications::where('doc', $doc)->pluck('r_id')->first();
" but how to get the $doc?
updated:
$mbpay = new MBPay($payment_info);
$processed_payment = $mbpay->processPaymentInfo();
$xml = simplexml_load_string($processed_payment);
$doc = $xml->doc;
$rID = DB::table('notifications')->where('doc', $doc)->pluck('r_id')->first();
Log::info('Showing $mbpay: ' . var_export($xml, true));
Log::info('doc: ' . var_export($doc, true)); // shows SimpleXMLElement::__set_state(array(
))
Log::info('rID: ' . var_export($rID, true)); // shows NULL
Log::info('xml: ' . var_export($xml, true));
return \Response::make($xml, '200')->header('Content-Type', 'text/xml');
The $mbpay->processPaymentInfo() shows like:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<getmb_key>
<status>ok</status>
<message>...</message>
<user>...</user>
<doc>...</doc>
</getmb_key>
Assuming you will only ever expect one doc node, there are a few ways you can go about getting the value of the node. The first thing that comes to mind is simplexml_load_string():
$processed_payment = $mbpay->processPaymentInfo();
$xml = simplexml_load_string($processed_payment);
$doc = (string) $xml->doc;
We can also parse it with DOMDocument's loadXML():
$dom = new \DOMDocument();
$dom->loadXML($processed_payment);
$docs = $dom->getElementsByTagName('doc');
$doc = $docs->item(0)->nodeValue;
As well as with SimpleXMLElement::
$element = new \SimpleXMLElement($processed_payment);
$doc = (string) $element->doc[0];
Finally, there's good old regex with preg_match:
$result = preg_match("/<doc>(.*?)<\\/doc>/", $processed_payment, $matches);
var_dump($matches[1]);
Which method you use depends on the structure of your document and, somewhat, your personal preference. For me, simplexml_load_string() was the first thing that came to mind, but after considering alternatives and given the knowledge of the expected structure, I think I'd prefer SimpleXMLElement().
Here are working examples of each method above.

How do I parse an XML file with SimpleXMLElement and multiple namespaces?

I have an XML file that looks like the example on this site: http://msdn.microsoft.com/en-us/library/ee223815(v=sql.105).aspx
I am trying to parse the XML file using something like this:
$data = file_get_contents('http://mywebsite here');
$xml = new SimpleXMLElement($data);
$str = $xml->Author;
echo $str;
Unfortunately, this is not working, and I suspect it is due to the namespaces. I can dump the $xml using asXML() and it correctly shows the XML data.
I understand I need to insert namespaces somehow, but I'm not sure how. How do I parse this type of XML file?
All you need is to register the namespace
$sxe = new SimpleXMLElement($data);
$sxe->registerXPathNamespace("diffgr", "urn:schemas-microsoft-com:xml-diffgram-v1");
$data = $sxe->xpath("//diffgr:diffgram") ;
$data = $data[0];
echo "<pre>";
foreach($data->Results->RelevantResults as $result)
{
echo $result->Author , PHP_EOL ;
}
Output
Ms.Kim Abercrombie
Mr.GustavoAchong
Mr. Samuel N. Agcaoili
See Full code In Action

Getting the text portion of a node using php Simple XML

Given the php code:
$xml = <<<EOF
<articles>
<article>
This is a link
<link>Title</link>
with some text following it.
</article>
</articles>
EOF;
function traverse($xml) {
$result = "";
foreach($xml->children() as $x) {
if ($x->count()) {
$result .= traverse($x);
}
else {
$result .= $x;
}
}
return $result;
}
$parser = new SimpleXMLElement($xml);
traverse($parser);
I expected the function traverse() to return:
This is a link Title with some text following it.
However, it returns only:
Title
Is there a way to get the expected result using simpleXML (obviously for the purpose of consuming the data rather than just returning it as in this simple example)?
There might be ways to achieve what you want using only SimpleXML, but in this case, the simplest way to do it is to use DOM. The good news is if you're already using SimpleXML, you don't have to change anything as DOM and SimpleXML are basically interchangeable:
// either
$articles = simplexml_load_string($xml);
echo dom_import_simplexml($articles)->textContent;
// or
$dom = new DOMDocument;
$dom->loadXML($xml);
echo $dom->documentElement->textContent;
Assuming your task is to iterate over each <article/> and get its content, your code will look like
$articles = simplexml_load_string($xml);
foreach ($articles->article as $article)
{
$articleText = dom_import_simplexml($article)->textContent;
}
node->asXML();// It's the simple solution i think !!
So, the simple answer to my question was: Simplexml can't process this kind of XML. Use DomDocument instead.
This example shows how to traverse the entire XML. It seems that DomDocument will work with any XML whereas SimpleXML requires the XML to be simple.
function attrs($list) {
$result = "";
foreach ($list as $attr) {
$result .= " $attr->name='$attr->value'";
}
return $result;
}
function parseTree($xml) {
$result = "";
foreach ($xml->childNodes AS $item) {
if ($item->nodeType == 1) {
$result .= "<$item->nodeName" . attrs($item->attributes) . ">" . parseTree($item) . "</$item->nodeName>";
}
else {
$result .= $item->nodeValue;
}
}
return $result;
}
$xmlDoc = new DOMDocument();
$xmlDoc->loadXML($xml);
print parseTree($xmlDoc->documentElement);
You could also load the xml using simpleXML and then convert it to DOM using dom_import_simplexml() as Josh said. This would be useful, if you are using simpleXml to filter nodes for parsing, e.g. using XPath.
However, I don't actually use simpleXML, so for me that would be taking the long way around.
$simpleXml = new SimpleXMLElement($xml);
$xmlDom = dom_import_simplexml($simpleXml);
print parseTree($xmlDom);
Thank you for all the help!
You can get the text node of a DOM element with simplexml just by treating it like a string:
foreach($xml->children() as $x) {
$result .= "$x"
However, this prints out:
This is a link
with some text following it.
TitleTitle
..because the text node is treated as one block and there is no way to tell where the child fits in inside the text node. The child node is also added twice because of the other else {}, but you can just take that out.
Sorry if I didn't help much, but I don't think there's any way to find out where the child node fits in the text node unless the xml is consistent (but then, why not use tags). If you know what element you want to strip the text out of, strip_tags() will work great.
This has already been answered, but CASTING TO STRING ( i.e. $sString = (string) oSimpleXMLNode->TagName) always worked for me.
Try this:
$parser = new SimpleXMLElement($xml);
echo html_entity_decode(strip_tags($parser->asXML()));
That's pretty much equivalent to:
$parser = simplexml_load_string($xml);
echo dom_import_simplexml($parser)->textContent;
Like #tandu said, it's not possible, but if you can modify your XML, this will work:
$xml = <<<EOF
<articles>
<article>
This is a link
</article>
<link>Title</link>
<article>
with some text following it.
</article>
</articles>

php SimpleXMLElement set text

how to set text for a SimpleXMLElement in php?
Let's say $node is an SimpleXMLElement. This code:
$node->{0} = "some string"
will result in an extra childNode in PHP 7, instead of setting the text content for $node. Use:
$node[0] = "some string"
instead.
Did you look at the basic documentation examples?
From there:
include 'example.php';
$xml = new SimpleXMLElement($xmlstr);
$xml->movie[0]->characters->character[0]->name = 'Miss Coder';
echo $xml->asXML();
$xml = SimpleXMLElement('<a><b><c></c></b></a>');
$foundNodes = $xml->xpath('//c');
$foundNode = $foundNodes[0];
$foundNode->{0} = "This text will be put inside of c tag.";
$xml->asXML();
// will output <a><b><c>This text will be put inside of c tag.</c></b></a>
More on my search of this answer here:
How can I set text value of SimpleXmlElement without using its parent?

Categories