I have a simple xml object like this as a result of Zend_Rest_Client_Result Object.
<userdetails>
<name value="jake"/>
<type value="user"/>
<attribute name="fname">
<value>Jake</value>
</attribute>
<attribute name="lname">
<value>Gordon</value>
</attribute>
<attribute name="phone">
<value>123-555-1234</value>
<value>999-888-7777</value>
</attribute>
</userdetails>
How can I create an array like this from the above XML object using PHP?
$userDetails = array();
$userDetails["name"] = "jake";
$userDetails["type"] = "user";
$userDetails["fname"] = "Jake";
$userDetails["lname"] = "Gordon";
$userDetails["phone"][0] = "123-555-1234";
$userDetails["phone"][1] = "123-555-1234";
Thanks,
$xml = '<userdetails><name value="jake"/><type value="user"/><attribute name="fname"><value>Jake</value></attribute><attribute name="lname"><value>Gordon</value></attribute><attribute name="phone"><value>123-555-1234</value><value>999-888-7777</value></attribute></userdetails>';
$simple = new SimpleXMLElement($xml);
$array = array();
if(isset($simple->name))
$array['name'] = (string) $simple->name->attributes()->value;
if(isset($simple->type))
$array['type'] = (string) $simple->type->attributes()->value;
if($foreach = $simple->xpath('//attribute[#name="fname"]/value'))
{
foreach($foreach as $node)
{
$array['fname'] = (string) $node;
}
}
if($foreach = $simple->xpath('//attribute[#name="lname"]/value'))
{
foreach($foreach as $node)
{
$array['lname'] = (string) $node;
}
}
if($foreach = $simple->xpath('//attribute[#name="phone"]/value'))
{
$array['phone'] = array();
foreach($simple->xpath('//attribute[#name="phone"]/value') as $node)
{
$array['phone'][] = (string) $node;
}
}
print_r($array);
function xmlstr_to_array($xmlstr) {
$doc = new DOMDocument();
$doc->loadXML($xmlstr);
return domnode_to_array($doc->documentElement);
}
function domnode_to_array($node) {
$output = array();
switch ($node->nodeType) {
case XML_CDATA_SECTION_NODE:
case XML_TEXT_NODE:
$output = trim($node->textContent);
break;
case XML_ELEMENT_NODE:
for ($i=0, $m=$node->childNodes->length; $i<$m; $i++) {
$child = $node->childNodes->item($i);
$v = domnode_to_array($child);
if(isset($child->tagName)) {
$t = $child->tagName;
if(!isset($output[$t])) {
$output[$t] = array();
}
$output[$t][] = $v;
}
elseif($v) {
$output = (string) $v;
}
}
if(is_array($output)) {
if($node->attributes->length) {
$a = array();
foreach($node->attributes as $attrName => $attrNode) {
$a[$attrName] = (string) $attrNode->value;
}
$output['#attributes'] = $a;
}
foreach ($output as $t => $v) {
if(is_array($v) && count($v)==1 && $t!='#attributes') {
$output[$t] = $v[0];
}
}
}
break;
}
return $output;
}
?>
Another quick and dirty method is:
<?php
$a = json_decode(json_encode((array) simplexml_load_string($s)),1);
?>
This is less robust and will create problem you code where to contain CDATA nodes as well.
Related
I am working with the vCard Parser from Sourceforge https://sourceforge.net/projects/vcardphp/files/vcardphp/vcardphp-1.1.2/
I just post an excerpt here:
vcard.php
function parse(&$lines)
{
$this->_map = null;
$property = new VCardProperty();
while ($property->parse($lines)) {
if (is_null($this->_map)) {
if ($property->name == 'BEGIN') {
$this->_map = array();
}
} else {
if ($property->name == 'END') {
break;
} else {
$this->_map[$property->name][] = $property;
}
}
// MDH: Create new property to prevent overwriting previous one
// (PHP5)
$property = new VCardProperty();
}
return $this->_map != null;
}
function parse(&$lines)
{
while (list(, $line) = each($lines)) {
$line = rtrim($line);
$tmp = split_quoted_string(":", $line, 2);
if (count($tmp) == 2) {
$this->value = $tmp[1];
$tmp = strtoupper($tmp[0]);
$tmp = split_quoted_string(";", $tmp);
$this->name = $tmp[0];
$this->params = array();
for ($i = 1; $i < count($tmp); $i++) {
$this->_parseParam($tmp[$i]);
}
if ($this->params['ENCODING'][0] == 'QUOTED-PRINTABLE') {
$this->_decodeQuotedPrintable($lines);
}
if ($this->params['CHARSET'][0] == 'UTF-8') {
$this->value = utf8_decode($this->value);
}
return true;
}
}
return false;
}
and
vbook.php
function parse_vcards(&$lines)
{
$cards = array();
$card = new VCard();
while ($card->parse($lines)) {
$property = $card->getProperty('N');
if (!$property) {
return "";
}
$n = $property->getComponents();
$tmp = array();
if ($n[3]) $tmp[] = $n[3]; // Mr.
if ($n[1]) $tmp[] = $n[1]; // John
if ($n[2]) $tmp[] = $n[2]; // Quinlan
if ($n[4]) $tmp[] = $n[4]; // Esq.
$ret = array();
if ($n[0]) $ret[] = $n[0];
$tmp = join(" ", $tmp);
if ($tmp) $ret[] = $tmp;
$key = join(", ", $ret);
$cards[$key] = $card;
// MDH: Create new VCard to prevent overwriting previous one (PHP5)
$card = new VCard();
}
ksort($cards);
return $cards;
}
function print_vcard($card, $hide)
{
$names = array('N', 'FN', 'TITLE', 'TEL', 'EMAIL', 'URL', 'ADR', 'NOTE');
$row = 0;
foreach ($names as $name) {
if (in_array_case($name, $hide)) {
continue;
}
$properties = $card->getProperties($name);
if ($properties) {
foreach ($properties as $property) {
$show = true;
$types = $property->params['TYPE'];
if ($types) {
foreach ($types as $type) {
if (in_array_case($type, $hide)) {
$show = false;
break;
}
}
}
if ($show) {
$class = ($row++ % 2 == 0) ? "property-even" : "property-odd";
print_vcard_property($property, $class, $hide);
}
}
}
}
}
function print_vcard_property($property, $class, $hide)
{
$name = $property->name;
$value = $property->value;
$types = $property->params['TYPE'];
if ($types) {
print_r(array_filter($types));
}
switch ($name) {
case 'N':
$name = $property->getComponents();
print_r(array_filter($name));
break;
case 'TEL':
$tel = $property->getComponents();
print_r(array_filter($tel));
break;
case 'FN':
$company = $property->getComponents();
print_r(array_filter($company));
break;
case 'ADR':
$adr = $property->getComponents();
print_r(array_filter($adr));
break;
case 'EMAIL':
$email = $property->getComponents();
print_r(array_filter($email));
break;
case 'URL':
$url = $property->getComponents();
print_r(array_filter($url));
break;
default:
$components = $property->getComponents();
$lines = array();
foreach ($components as $component) {
if ($component) {
$lines[] = $component;
}
}
$html = join("\n", $lines);
break;
}
echo "<br>";
echo "<br><br>";
}
It works quite well when I am using the sample vcard file, which looks like this:
BEGIN:VCARD
N:Smith;Jim;Alvin;Mr.
FN:Jim A. Smith
CATEGORIES:Family
BDAY:1977-01-27
ADR;WORK:;Suite 900;11 5th Street;Coco Beach;FL;32082
ADR;HOME:;;198 Elm Street;Coco Beach;FL;32082
TEL;WORK:904-555-9384;
TEL;HOME:904-873-0394
TEL;CELL:904-934-3429
END:VCARD
But my vcard from Apple Contacts looks a little bit different:
BEGIN:VCARD
N:Underwood;Frank;;;
FN:Frank Underwood
item1.EMAIL;type=INTERNET;type=pref:frank.underwood#hoc.com
item2.TEL;type=pref:+01 321 323123123
item2.X-ABLabel:Mobil
item3.ADR;type=pref:;;Richmondstreet 21;Washington D.C;;12312;
item4.URL;type=pref:http://www.frankunderwood.com/
item4.X-ABLabel:_$!<HomePage>!$_
CATEGORIES:Friends
END:VCARD
So in my example only the name of the person is printed, not the email, phone, address or url. So in the parsing I need to remove item1,item2 and so on. But I do not know how to do that.
Here is a solution how to export the Apple Contacts into a proper vCard file, that can be parsed correctly:
Go to Contacts/Settings/vCard and change the format to 2.1 and Unicode (UTF-8)
I have a question regarding simplify my codes.
I have
public function getText($text){
if(!empty($text)){
$dom = new DomDocument();
$dom->loadHTML($text);
$xpath=new DOMXpath($dom);
$result = $xpath->query('//a');
if($result->length > 0){
$atags=$dom->getElementsByTagName('a');
foreach($atags as $atag){
$style = $atag ->getAttribute('style');
$atag->setAttribute('style',$style.' text-decoration:none;color:black;');
}
$returnText .= $dom->saveHTML();
return $returnText;
}
$result = $xpath->query('//table');
if($result->length > 0){
$tables = $dom->getElementsByTagName('table');
$inputs = $dom->getElementsByTagName('input');
foreach ($inputs as $input) {
$input->setAttribute('style','text-align:center;');
}
foreach ($tables as $table) {
$table->setAttribute('width',500);
$table->setAttribute('style','border:2px solid #8C8C8C;text-align:center;table-layout:fixed;');
}
$returnText .= $dom->saveHTML();
return $returnText;
}
}
return $text;
}
public function getTextwithIndex($text,$index=''){
if(!empty($text[$index])){
$dom = new DomDocument();
$dom->loadHTML($text[$index]);
$xpath=new DOMXpath($dom);
$result = $xpath->query('//a');
if($result->length > 0){
$atags=$dom->getElementsByTagName('a');
foreach($atags as $atag){
$style = $atag ->getAttribute('style');
$atag->setAttribute('style',$style.' text-decoration:none;color:black;');
}
$returnText .= $dom->saveHTML();
return $returnText;
}
$result = $xpath->query('//tbody');
if($result->length > 0){
$tbodies = $dom->getElementsByTagName('tbody');
$cells = $dom->getElementsByTagName('td');
$inputs = $dom->getElementsByTagName('input');
foreach ($inputs as $input) {
$input->setAttribute('style','text-align:center;');
}
foreach ($cells as $cell) {
$cell->setAttribute('style','border:1px solid black;');
}
foreach ($tbodies as $tbody) {
$table = $dom->createElement('table');
$table->setAttribute('width',500);
$table->setAttribute('style','border:2px solid #8C8C8C;text-align:center;table-layout:fixed;');
$tbody->parentNode->replaceChild($table, $tbody);
$table->appendChild($tbody);
}
$returnText .= $dom->saveHTML();
return $returnText;
}
}
return $text;
}
The difference between the method is $index and some modification of my domdocument. I feel like it's really cumbersome and could use some refactoring. Does anyone have any good suggestions? Thanks!
How about something like this:
public function getTextwithIndex($text,$index='') {
if (empty($index))
return getText($text); //not sure how $text works, so this line might be different.
return getText($text[$index]);
}
Or something like this:
public function getText($text, $index = false){
if ($index)
$text = $text[$index];
if(!empty($text)){
$dom = new DomDocument();
$dom->loadHTML($text);
$xpath=new DOMXpath($dom);
$result = $xpath->query('//a');
if($result->length > 0){
$atags=$dom->getElementsByTagName('a');
foreach($atags as $atag){
$style = $atag ->getAttribute('style');
$atag->setAttribute('style',$style.' text-decoration:none;color:black;');
}
$returnText .= $dom->saveHTML();
return $returnText;
}
$result = $xpath->query('//table');
if($result->length > 0){
if ($index) {
//do 'getTextWithIndex' dom stuff
} else {
$tables = $dom->getElementsByTagName('table');
$inputs = $dom->getElementsByTagName('input');
}
foreach ($inputs as $input) {
$input->setAttribute('style','text-align:center;');
}
foreach ($tables as $table) {
$table->setAttribute('width',500);
$table->setAttribute('style','border:2px solid #8C8C8C;text-align:center;table-layout:fixed;');
}
$returnText .= $dom->saveHTML();
return $returnText;
}
}
return $text;
}
I have two urls.
http://www.labs.skanetrafiken.se/v2.2/GetStartEndPoint.xsd
http://www.labs.skanetrafiken.se/v2.2/querypage.asp?inpPointFr=lund&inpPointTo=ystad
How do I get these two to collaborate so I can extract the information via PHP?
How does one extract all the information out of the XML file into a PHP object or array.
I just answered my own question with this code:
/**
* convert xml string to php array - useful to get a serializable value
*
* #param string $xmlstr
* #return array
* #author Adrien aka Gaarf
*/
function xmlstr_to_array($xmlstr) {
$doc = new DOMDocument();
$doc->loadXML($xmlstr);
return domnode_to_array($doc->documentElement);
}
function domnode_to_array($node) {
$output = array();
switch ($node->nodeType) {
case XML_CDATA_SECTION_NODE:
case XML_TEXT_NODE:
$output = trim($node->textContent);
break;
case XML_ELEMENT_NODE:
for ($i=0, $m=$node->childNodes->length; $i<$m; $i++) {
$child = $node->childNodes->item($i);
$v = domnode_to_array($child);
if(isset($child->tagName)) {
$t = $child->tagName;
if(!isset($output[$t])) {
$output[$t] = array();
}
$output[$t][] = $v;
}
elseif($v) {
$output = (string) $v;
}
}
if(is_array($output)) {
if($node->attributes->length) {
$a = array();
foreach($node->attributes as $attrName => $attrNode) {
$a[$attrName] = (string) $attrNode->value;
}
$output['#attributes'] = $a;
}
foreach ($output as $t => $v) {
if(is_array($v) && count($v)==1 && $t!='#attributes') {
$output[$t] = $v[0];
}
}
}
break;
}
return $output;
}
$xml = 'http://www.labs.skanetrafiken.se/v2.2/querypage.asp?inpPointFr=lund&inpPointTo=ystad';
$xmlstr = new SimpleXMLElement($xml, null, true);
$array = xmlstr_to_array($xmlstr->asXML());
print_r($array);
This returns an array with the XML, exactly what I want to be able to work with.
I'm trying to add the results of a script to an array, but once I look into it there is only one item in it, probably me being silly with placement
function crawl_page($url, $depth)
{
static $seen = array();
$Linklist = array();
if (isset($seen[$url]) || $depth === 0) {
return;
}
$seen[$url] = true;
$dom = new DOMDocument('1.0');
#$dom->loadHTMLFile($url);
$anchors = $dom->getElementsByTagName('a');
foreach ($anchors as $element) {
$href = $element->getAttribute('href');
if (0 !== strpos($href, 'http')) {
$href = rtrim($url, '/') . '/' . ltrim($href, '/');
}
if(shouldScrape($href)==true)
{
crawl_page($href, $depth - 1);
}
}
echo "URL:",$url;
echo http_response($url);
echo "<br/>";
$Linklist[] = $url;
$XML = new DOMDocument('1.0');
$XML->formatOutput = true;
$root = $XML->createElement('Links');
$root = $XML->appendChild($root);
foreach ($Linklist as $value)
{
$child = $XML->createElement('Linkdetails');
$child = $root->appendChild($child);
$text = $XML->createTextNode($value);
$text = $child->appendChild($text);
}
$XML->save("linkList.xml");
}
$Linklist[] = $url; will add a single item to the $Linklist array. This line needs to be in a loop I think.
static $Linklist = array(); i think, but code is awful
I need to build an tree (with arrays) from given urls.
I have the following list of urls:
http://domain.com/a/a.jsp
http://domain.com/a/b/a.jsp
http://domain.com/a/b/b.jsp
http://domain.com/a/b/c.jsp
http://domain.com/a/c/1.jsp
http://domain.com/a/d/2.jsp
http://domain.com/a/d/a/2.jsp
now i need an array like this:
domain.com
a
a.jsp
b
a.jsp
b.jsp
c.jsp
c
1.jsp
d
2.jsp
a
2.jsp
How can i do this with php?
i thought mark's solution was a bit complicated so here's my take on it:
(note: when you get to the filename part of the URI, I set it as both the key and the value, wasn't sure what was expected there, the nested sample didn't give much insight.)
<?php
$urls = array(
'http://domain.com/a/a.jsp',
'http://domain.com/a/b/a.jsp',
'http://domain.com/a/b/b.jsp',
'http://domain.com/a/b/c.jsp',
'http://domain.com/a/c/1.jsp',
'http://domain.com/a/d/2.jsp',
'http://domain.com/a/d/a/2.jsp'
);
$array = array();
foreach ($urls as $url)
{
$url = str_replace('http://', '', $url);
$parts = explode('/', $url);
krsort($parts);
$line_array = null;
$part_count = count($parts);
foreach ($parts as $key => $value)
{
if ($line_array == null)
{
$line_array = array($value => $value);
}
else
{
$temp_array = $line_array;
$line_array = array($value => $temp_array);
}
}
$array = array_merge_recursive($array, $line_array);
}
print_r($array);
?>
$urlArray = array( 'http://domain.com/a/a.jsp',
'http://domain.com/a/b/a.jsp',
'http://domain.com/a/b/b.jsp',
'http://domain.com/a/b/c.jsp',
'http://domain.com/a/c/1.jsp',
'http://domain.com/a/d/2.jsp',
'http://domain.com/a/d/a/2.jsp'
);
function testMapping($tree,$level,$value) {
foreach($tree['value'] as $k => $val) {
if (($val == $value) && ($tree['level'][$k] == $level)) {
return true;
}
}
return false;
}
$tree = array();
$i = 0;
foreach($urlArray as $url) {
$parsed = parse_url($url);
if ((!isset($tree['value'])) || (!in_array($parsed['host'],$tree['value']))) {
$tree['value'][$i] = $parsed['host'];
$tree['level'][$i++] = 0;
}
$path = explode('/',$parsed['path']);
array_shift($path);
$level = 1;
foreach($path as $k => $node) {
if (!testMapping($tree,$k+1,$node)) {
$tree['value'][$i] = $node;
$tree['level'][$i++] = $level;
}
$level++;
}
}
echo '<pre>';
for ($i = 0; $i < count($tree['value']); $i++) {
echo str_repeat(' ',$tree['level'][$i]*2);
echo $tree['value'][$i];
echo '<br />';
}
echo '</pre>';