php - Parse through unknown XML -


i've made simple tool lets fill in input field url xml file. it's supposed show nodes user can match them database fields, have working xml file has 2 "primary" nodes. example of xml file:

<foods>     <food>         <name>ravioli</name>         <recipe>food.com/ravioli</recipe>         <time>10 minutes</time>     </food>     <food>         <name>ravioli</name>         <recipe>food.com/ravioli</recipe>         <time>10 minutes</time>     </food> </foods> 

this returns me list says

name recipe time

the problem when wants use xml file doesn't have 2 "primary" nodes. example it's missing <food> node. in case wouldn't able show result because php code expecting 2 instead of 1 primary.

my code follows:

// fetch xml url if (!$xml = simplexml_load_file($_get['url'])) {     // xml file not reached     echo 'error loading xml. please check url.'; } else {     // parse through xml , fetch nodes     $child = $xml->children();     foreach($child->children() $key => $value) {         echo $key."<br>";     } } 

is there way nodes want xml file, regardless of amount of parent nodes?

you can query data xml dom, using xpath. accessible in php using domxpath::evaluate() method. second argument context, you're expressions can relative node. converting list of records (for database, csv, ...). require several steps. starting bootstrap:

$xml = <<<'xml' <foods>     <food>         <name>ravioli 1</name>         <recipe>food.com/ravioli-1</recipe>         <time unit="minutes">10</time>     </food>     <food>         <name>ravioli 2</name>         <recipe>food.com/ravioli-2</recipe>         <time unit="minutes">11</time>     </food> </foods> xml;  $dom = new domdocument(); $dom->loadxml($xml); $xpath = new domxpath($dom); 

first need define xml element defines record, elements define fields.

so let's build lists of possible record paths , field paths:

$paths = []; $leafs = []; foreach ($xpath->evaluate('//*|//@*') $node) {   $ispath = $xpath->evaluate('count(@*|*) > 0', $node);   $isleaf = !($xpath->evaluate('count(*) > 0', $node));   $path = '';   foreach ($xpath->evaluate('ancestor::*', $node) $parent) {     $path .= '/'.$parent->nodename;   }   $path .= '/'.($node instanceof domattr ? '@' : '').$node->nodename;   if ($isleaf) {     $leafs[$path] = true;   }   if ($ispath) {     $paths[$path] = true;   } } $paths = array_keys($paths); $leafs = array_keys($leafs); var_dump($paths, $leafs); 

output:

array(3) {   [0] =>   string(6) "/foods"   [1] =>   string(11) "/foods/food"   [2] =>   string(16) "/foods/food/time" } array(4) {   [0] =>   string(16) "/foods/food/name"   [1] =>   string(18) "/foods/food/recipe"   [2] =>   string(16) "/foods/food/time"   [3] =>   string(22) "/foods/food/time/@unit" } 

next show possible record paths user. user needs select one. knowing record path, build list of possible field paths leafs array:

$path = '/foods/food';  $fieldleafs = []; $pathlength = strlen($path) + 1; foreach ($leafs $leaf) {   if (0 === strpos($leaf, $path.'/')) {     $fieldleafs[] = substr($leaf, $pathlength);   } } var_dump($fieldleafs); 

output:

array(4) {   [0] =>   string(4) "name"   [1] =>   string(6) "recipe"   [2] =>   string(4) "time"   [3] =>   string(10) "time/@unit" } 

put dialog allows user select path each field.

$fielddefinition = [   'title' => 'name',   'url' => 'recipe',   'needed_time' => 'time',   'time_unit' => 'time/@unit' ]; 

now use path , mapping build records array:

$result = []; foreach ($xpath->evaluate($path) $node) {   $record = [];   foreach ($fielddefinition $field => $expression) {     $record[$field] = $xpath->evaluate(       'string('.$expression.')',       $node     );   }   $result[] = $record; } var_dump($result); 

output:

array(2) {   [0] =>   array(4) {     'title' =>     string(9) "ravioli 1"     'url' =>     string(18) "food.com/ravioli-1"     'needed_time' =>     string(2) "10"     'time_unit' =>     string(7) "minutes"   }   [1] =>   array(4) {     'title' =>     string(9) "ravioli 2"     'url' =>     string(18) "food.com/ravioli-2"     'needed_time' =>     string(2) "11"     'time_unit' =>     string(7) "minutes"   } } 

the full example can found at: https://eval.in/118012

the xml in example never converted generic array. doing mean loosing information , double storage. don't. extract structure information xml, let user define mapping. use xpath extract data , store them directly in result format.


Comments

Popular posts from this blog

Android layout hidden on keyboard show -

google app engine - 403 Forbidden POST - Flask WTForms -

c - Why would PK11_GenerateRandom() return an error -8023? -