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
Post a Comment