Unable to get node from simplexml_load_string - php

I understand this question has been asked, but all solutions I've found have not worked for me.
Given this:
SimpleXMLElement Object
(
[0] =>
<test>
<blah>
<x>
filler
</x>
</blah>
</test>
)
How do I get the <x> value?
I've tried
$doc = simplexml_load_string($xml_response);
print_r($doc->test->blah->x);
print_r($doc[0]->test->blah->x);
print_r($doc->{'0'}->test->blah->x);
print_r((string)$doc->{'0'}->test->blah->x);
print_r((string)$doc[0]->test->blah->x);
print_r((string)$doc->test->blah->x);
Here's the raw xml:
1.0" encoding="UTF-8" ?>
<xxx>
<test>
<blah>
<x>fillertexthere</x>
</blah>
<fillertexthere</Reason>
</test>
</xxx>%

Your SimpleXMLElement contains one string. I think that is because your xml contains < and >.
What you could do is first use htmlspecialchars_decode and then load your string.
(I have removed this line <fillertexthere</Reason> and % from your raw xml)
$xml_response = <<<DATA
<?xml version="1.0" encoding="UTF-8" ?>
<xxx>
<test>
<blah>
<x>fillertexthere</x>
</blah>
</test>
</xxx>
DATA;
$xml_response = htmlspecialchars_decode($xml_response);
var_dump($xml_response);
This will look like:
object(SimpleXMLElement)#1 (1) {
["test"]=>
object(SimpleXMLElement)#2 (1) {
["blah"]=>
object(SimpleXMLElement)#3 (1) {
["x"]=>
string(14) "fillertexthere"
}
}
}
You can echo the value of x like this:
echo $doc->test->blah->x;

Related

PHP simplexmlelement fails to find attribute when attribute path is assigned to variable

My code parses an array to get the paths to various attributes in an xml file. I load the xml as a simplexmlelement like so:
$xml = simplexml_load_string($xml_string);
$address = $xml->response->addressinformation->records->record[0];
This code will work and displays the correct value of '03':
$from = $address->{'from-date'}['month'];
echo $from;
//prints '03'
The {} around from-date are necessary because of the - in the element name.
The following code blocks seem like they should be functionally equivalent to the above code, but when I assign the path to a $variable it does not work.
$path = "{'from-date'}['month']";
$from = $address->$path;
echo $from;
//prints ''
$path = "from-date['month']";
$from = $address->$path;
echo $from;
//prints ''
This code will work.
//normal path
$from = $address->{'from-date'};
//path assigned to variable
$path = "from-date";
$from2 = $address->$path;
var_dump($from);
var_dump($from2);
//both correctly output - object(SimpleXMLElement)#1587 (1)
//{ ["#attributes"]=> array(2) { ["year"]=> string(4) "2003" ["month"]=> string(2) "03" } }
It only seems to fail when I target the specific attribute with ['month']. I know the answer to this question is just going to be some silly thing that I'm doing wrong. Can anyone tell me what it is?
Sample XML
$xml_string = '
<?xml version="1.0" encoding="utf-8"?>
<root>
<response>
<addressinformation>
<records>
<record id="1">
<fullname>JOHN E DOE</fullname>
<firstname>JOHN</firstname>
<middlename>E</middlename>
<lastname>DOE</lastname>
<fulldob>01/01/1970</fulldob>
<from-date year="2003" month="03"/>
<to-date year="2010" month="09"/>
</record>
<record id="2">
<fullname>JOHN E DOE</fullname>
<firstname>JOHN</firstname>
<from-date year="2003" month="03"/>
<to-date year="2010" month="09"/>
</record>
</records>
</addressinformation>
<otherinformation>
<records>
<record id="3">
<fullname>JOHN DOE</fullname>
<firstname>JOHN</firstname>
<lastname>DOE</lastname>
<fulldob>01/01/1970</fulldob>
</record>
<record id="4">
<fullname>JOHN EDWARD DOE</fullname>
<firstname>JOHN</firstname>
<middlename>EDWARD</middlename>
<lastname>DOE</lastname>
<fulldob>19700000</fulldob>
</record>
<record id="5">
<fullname>JOHN EDWARD DOE</fullname>
<firstname>JOHN</firstname>
<middlename>EDWARD</middlename>
<lastname>DOE</lastname>
<fulldob>19830000</fulldob>
</record>
</records>
</otherinformation>
</response>
</root>
';
What you're trying to do is not possible; you can only use a variable for the name of the property you are trying to access, not to then reference that as an array. When you try to use
$path = "{'from-date'}['month']";
$from = $address->$path;
you are actually trying to access an object property whose name is {'from-date'}['month'], which is why you get no result.
What you could do is specify the attribute using a variable as well:
$path = "from-date";
$attr = 'month';
$from2 = $address->$path[$attr];
echo $from2;
which will output
03
as desired.
You can't use something like "from-date['month']" as a path to an element, the same way that you couldn't use it in a normal array. It is expecting it to be 1 level of the array only.
An alternative with XML though is that you could use an XPath expression (you would need to learn XPath, but it may solve a lot of other problems if you are working with XML).
In your case...
$path = "./from-date/#month";
$from = $address->xpath($path)[0];
echo $from;
gives 03.
The reason for the [0] in the XPath line is that xpath() returns an array of matches, so this just picks the first one.
(BTW, you also shouldn't have any whitespace before the <?xml ..., so make sure you have something like
$xml_string = '<?xml version="1.0" encoding="utf-8"?>
)

get xml element using php

I have an XML page called (www.example.com/name.xml)
it contains the bellow elements :
<xml>
<names>
<name id='6' >Name 1 </name>
<name id='7'>Name 2</name>
<name id='8'>Name 3</name>
</names>
</xml>
and here is my PHP script :
<?php
$id='6';
$url = "www.example.come/name.xml";
$xml = simplexml_load_file($url);
$position ="$xml->name id='$id' ";
?>
So how can I get it ?
You can simply use xpath for this:
$id='6';
$xml = simplexml_load_file($url);
$position = (string)$xml->xpath("//name[#id='$id']")[0];
echo $position;
Output:
Name 1
This uses xpath to get the text from the <name> where id is 6. Then it casts the SimpleXMLElement to (string), thus providing the output.
Based on your provided data, you could do it like this:
$id='6';
foreach($xml->names->name as $name) {
if ((string)$name->attributes()->id === $id) {
// here $name will be the element for which the attribute id='6'
var_dump($name);
}
}
Will result in:
object(SimpleXMLElement)#3 (2) {
["#attributes"]=>
array(1) {
["id"]=>
string(1) "6"
}
[0]=>
string(7) "Name 1 "
}
$name is of type SimpleXMLElement and the value you are looking for is in the attributes.

PHP XML to array and back to XML

Here's the problem:
I have a given XML structure, similar to this one:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<programs>
<program>data_one</program>
<program>data_two</program>
<program>data_three</program>
</programs>
<periods>
<period date="2015-10-31T00:00:00+08:00">
<data_one>12.33</data_one>
<data_two>5.11</data_two>
<data_three>1544.22</data_three>
</period>
<period date="2015-12-31T00:00:00+08:00">
<data_one>19.33</data_one>
<data_two>123.44</data_two>
<data_three>999.12</data_three>
</period>
<period date="2015-11-30T00:00:00+08:00">
<data_one>1.01</data_one>
<data_two>19.23</data_two>
<data_three>234.11</data_three>
</period>
<period date="2016-01-31T00:00:00+08:00">
<data_one>69.33</data_one>
<data_two>80.12</data_two>
<data_three>65.87</data_three>
</period>
</periods>
</root>
I then used this little trick to convert part of the XML to an array (thanks to the answers I found here):
// convert periods from XML to array
$data = simplexml_load_string($this->xml->periods->asXML(), null, LIBXML_NOCDATA);
$json = json_encode($data);
$xml_as_array = json_decode($json, true);
That was done, because I needed to sort the the 'period' by date. This was quit simple done like this:
// create the sort-keys
foreach ($xml_as_array['period'] as $key => $row) {
$dates[$key] = $row['#attributes']['date'];
}
// sort by date
array_multisort($dates, SORT_ASC, $xml_as_array['period']);
A check with var_dump() shows, that the array is correctly sorted:
array(1) {
["period"]=>
array(4) {
[0]=>
array(4) {
["#attributes"]=>
array(1) {
["date"]=>
string(25) "2015-10-31T00:00:00+08:00"
}
["data_one"]=>
string(5) "12.33"
["data_two"]=>
string(4) "5.11"
["data_three"]=>
string(7) "1544.22"
}
[1]=>
array(4) {
["#attributes"]=>
array(1) {
["date"]=>
string(25) "2015-11-30T00:00:00+08:00"
}
["data_one"]=>
string(4) "1.01"
["data_two"]=>
string(5) "19.23"
["data_three"]=>
string(6) "234.11"
}
[2]=>
array(4) {
["#attributes"]=>
array(1) {
["date"]=>
string(25) "2015-12-31T00:00:00+08:00"
}
["data_one"]=>
string(5) "19.33"
["data_two"]=>
string(6) "123.44"
["data_three"]=>
string(6) "999.12"
}
[3]=>
array(4) {
["#attributes"]=>
array(1) {
["date"]=>
string(25) "2016-01-31T00:00:00+08:00"
}
["data_one"]=>
string(5) "69.33"
["data_two"]=>
string(5) "80.12"
["data_three"]=>
string(5) "65.87"
}
}
}
Now we come to the real problem: converting the array back to an XML structure similar as the original one (and as a bonus to replace the original '' with it).
I tried the usual recursive approach, but badly failed on creating the '' nodes with the 'date' attribute. There are some related questions here on stackoverflow addressing the array to xml conversion issue, but none of them worked...(therefor I'm not going to post them. Maybe its easier to come up with something fresh instead of trying to make my really bad code somehow to work ;-)
For any solutions, ideas or tipps I'd be very thankful.
Cheers,
hopfi2k
Consider using XSLT which you could bypass the PHP array sort altogether. As information, XSLT is a special-purpose declarative language (same type as SQL, sadly not as popular) used primarily to restructure/re-format/re-design XML files which your sort is exactly a native need as xslt maintains the <xslt:sort> method. Also, PHP like other general purpose languages (C#, Java, Python) maintains an XSLT 1.0 processor and so can run the transform to even overwrite the original XML file:
XSLT Script (save .xsl or .xslt file)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Identity Transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Sort periods by individual period's date attribute -->
<xsl:template match="periods">
<xsl:copy>
<xsl:apply-templates select="period">
<xsl:sort select="#date" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:transform>
PHP Script
// LOAD FILES
$doc = new DOMDocument();
$doc->load('Input.xml');
$xsl = new DOMDocument;
$xsl->load('XSLTfile.xsl');
// CONFIGURE TRANSFORMER
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// TRANSFORM XML SOURCE
$newXml = $proc->transformToXML($doc);
// SAVE OUTPUT TO FILE
$xmlfile ='Output.xml'; // USE SAME NAME TO OVERWRITE ORIGINAL
file_put_contents($xmlfile, $newXml);
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<programs>
<program>data_one</program>
<program>data_two</program>
<program>data_three</program>
</programs>
<periods>
<period date="2015-10-31T00:00:00+08:00">
<data_one>12.33</data_one>
<data_two>5.11</data_two>
<data_three>1544.22</data_three>
</period>
<period date="2015-11-30T00:00:00+08:00">
<data_one>1.01</data_one>
<data_two>19.23</data_two>
<data_three>234.11</data_three>
</period>
<period date="2015-12-31T00:00:00+08:00">
<data_one>19.33</data_one>
<data_two>123.44</data_two>
<data_three>999.12</data_three>
</period>
<period date="2016-01-31T00:00:00+08:00">
<data_one>69.33</data_one>
<data_two>80.12</data_two>
<data_three>65.87</data_three>
</period>
</periods>
</root>

SimpleXMLElement and xpath [duplicate]

This question already has an answer here:
SimpleXmlElement and XPath, getting empty array()
(1 answer)
Closed 8 years ago.
I have an XML doc similar to:
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:zapi="http://zotero.org/ns/api">
<link rel="up" type="application/atom+xml" href="some link"/>
</entry>
I want the "up" (rel attribute) of the link element, so I do this:
$xml = new SimpleXMLElement($body);
$php_up = $xml->xpath("//link[#rel='up']");
var_dump($php_up); exit;
which just gives me array(0) { }.
What am I doing wrong?
The declaration in the second line of your XML gives two XML namespaces:
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:zapi="http://zotero.org/ns/api">
The first xmlns attribute, with no colon-separated suffix, sets the "default" namespace for the entry element as being http://www.w3.org/2005/Atom, and descendant nodes with no prefix are in that namespace by default. To be able to access these elements with a "default" namespace using XPath, you need to set a namespace using registerXPathNamespace, and then perform your query, prefixing the element you're looking for (link) with the namespace:
$xml->registerXPathNamespace('d', 'http://www.w3.org/2005/Atom');
$php_up = $xml->xpath("//d:link[#rel='up']");
var_dump($php_up);
Output:
array(1) {
[0] =>
class SimpleXMLElement#2 (1) {
public $#attributes =>
array(3) {
'rel' =>
string(2) "up"
'type' =>
string(20) "application/atom+xml"
'href' =>
string(9) "some link"
}
}
}
You don't need XPATH to get the REL attribute. This should be as simple as:
$php_up = $xml->link->attributes()->rel;
Updated: To get the attributes of multiple link elements:
The XML:
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:zapi="http://zotero.org/ns/api">
<link rel="up" type="application/atom+xml" href="some link"/>
<link rel="down" type="application/atom+xml" href="some link"/>
</entry>
The PHP:
$xml = new SimpleXMLElement($body);
foreach ($xml->link as $link) {
$rels[] = (string)$link->attributes()->rel;
}
var_dump($rels); exit;
It's not going to be obvious, but you need to use str_replace on your xmlns :)
$body= str_replace('xmlns=', 'ns=', $body);
Before you call your $xml = new SimpleXMLElement($body);
Here's your eval.in

Need to extract values from a XML file

I have a XML file, and the layout is such
<author>
<name></name>
<iso></iso>
<price></price>
</author>
I know how it loops. I want to know how I can extract the value of
<name>
Thanks
Jean
[edit]
my apologies, if in the
<author>
<name>
<first_name></first_name>
<last_name></lastname>
</name>
</author>
I want to extract first_name
Use simplexml or similar:
<?php
$string = <<<XML
<author>
<name>
<first_name>John</first_name>
<last_name>Smith</last_name>
</name>
</author>
XML;
$xml = simplexml_load_string($string);
var_dump($xml);
?>
Will output something like this:
object(SimpleXMLElement)#1 (1) {
["name"]=>
object(SimpleXMLElement)#2 (2) {
["first_name"]=>
string(4) "John"
["last_name"]=>
string(5) "Smith"
}
}
And you can access the name like this:
echo $xml->name->first_name; // outputs 'John'
echo $xml->name->last_name; // outputs 'Smith'
Use SimpleXML.
Edit: I see. That wasn't showing up before. Try this:
$xml = simple_xml_load_string([your XML string])
echo $xml->name;
Does that work?

Categories