通过未知的XML解析(Parse through unknown XML)

2019-10-19 12:55发布

我做了一个简单的工具,可以让你在输入栏填满,作为一个XML文件的URL。 它应该显示所有的节点,以便用户可以与数据库字段,这是我工作的有2个“初级”节点的XML文件匹配。 XML文件的实例:

<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>

这将返回我,说一个列表

name recipe time

问题是,当有人想使用不具有2“主”节点的XML文件。 例如,它缺少的<food>节点。 在这种情况下,将无法显示结果,因为我的PHP代码期待的,而不是1个主2。

我的代码如下:

// Fetch the XML from the URL
if (!$xml = simplexml_load_file($_GET['url'])) {
    // The XML file could not be reached
    echo 'Error loading XML. Please check the URL.';
} else {
    // Parse through the XML and fetch the nodes
    $child = $xml->children();
    foreach($child->children() as $key => $value) {
        echo $key."<br>";
    }
}

有没有办法让我从任何XML文件所需的节点,而不管父节点的量?

Answer 1:

您可以从XML DOM查询数据,使用XPath。 它使用DOMXpath :: evaluate()方法是在PHP访问。 第二个参数是上下文,所以你可以表达相对于另一个节点。 将其转换为记录的列表(数据库,CSV,...)。 将需要几个步骤。 一些引导开始:

$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);

首先,我们需要定义哪个XML元素定义了记录,那么该元素定义的字段。

因此,让我们建设的可能路径,记录和现场路径列表:

$paths = [];
$leafs = [];
foreach ($xpath->evaluate('//*|//@*') as $node) {
  $isPath = $xpath->evaluate('count(@*|*) > 0', $node);
  $isLeaf = !($xpath->evaluate('count(*) > 0', $node));
  $path = '';
  foreach ($xpath->evaluate('ancestor::*', $node) as $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);

输出:

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"
}

接下来显示可能记录路径给用户。 用户需要选择一个。 知道了记录的路径,建立从叶子阵列的可能的字段的路径列表:

$path = '/foods/food';

$fieldLeafs = [];
$pathLength = strlen($path) + 1;
foreach ($leafs as $leaf) {
  if (0 === strpos($leaf, $path.'/')) {
    $fieldLeafs[] = substr($leaf, $pathLength);
  }
}
var_dump($fieldLeafs);

输出:

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

把一些对话框,允许用户选择每个域的路径。

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

现在使用的路径,并建立记录阵列的映射:

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

输出:

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"
  }
}

:完整的例子可以在这里找到https://eval.in/118012

该示例中的XML从不转换为一个通用的阵列。 这样做将意味着信息丢失和双存储。 所以,不要。 从XML提取结构信息,让用户定义的映射。 使用XPath提取数据,并将它们的结果格式直接存储。



文章来源: Parse through unknown XML