【问题标题】:Parse XML Document recursive递归解析 XML 文档
【发布时间】:2017-05-16 08:29:42
【问题描述】:

我有包含文章信息的 XML 文档,它们有一种层次结构:

<?xml version="1.0" encoding="UTF-8"?>

<page>
<elements>

<element>
<type>article</type>
<id>1</id>
<parentContainerID>page</parentContainerID>
<parentContainerType>page</parentContainerType>
</element>

<element>
<type>article</type>
<id>2</id>
<parentContainerID>1</parentContainerID>
<parentContainerType>article</parentContainerType>
</element>

<element>
<type>photo</type>
<id>3</id>
<parentContainerID>2</parentContainerID>
<parentContainerType>article</parentContainerType>
</element>

<... more elements ..>

</elements>
</page>

该元素具有节点 parentContainerID 和节点 parentContainerType。如果 parentContainerType == page,这是主元素。 parentContainerID 显示元素的主容器是什么。所以它应该看起来像:1

现在我需要为这些东西构建一个新页面 (html),如下所示: ID 1 的内容,ID 2 的内容,ID 3 的内容(ID 没有进行中)。

我想这可以通过递归函数来完成。但我不知道如何管理?

【问题讨论】:

  • “现在我需要为这些东西构建一个新页面 (html),如下所示:ID 1 的内容、ID 2 的内容、ID 3 的内容” – 只是顺序的 – 还是嵌套的,根据 XML 定义的父/子关系?如果没有嵌套,为什么要在这里使用递归——XML 中的元素也没有嵌套,它们都在同一级别。我可能只是先把这些东西读入一个数组,然后对它进行相应的排序......然后循环它以生成想要的输出。
  • CBroe,根据父子关系。

标签: php xml recursion xml-parsing domdocument


【解决方案1】:

XML 中没有嵌套/递归。 &lt;element/&gt; 节点是兄弟节点。要构建父子关系,我建议循环 XML 并构建两个数组。一个用于关系,一个用于引用元素。

$xml = file_get_contents('php://stdin');

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);

$relations = [];
$elements = [];
foreach ($xpath->evaluate('//element') as $element) {
  $id = (int)$xpath->evaluate('string(id)', $element);
  $parentId = (int)$xpath->evaluate('string(parentContainerID)', $element);
  $relations[$parentId][] = $id;
  $elements[$id] = $element;
}

var_dump($relations);

输出:

array(3) {
  [0]=>
  array(1) {
    [0]=>
    int(1)
  }
  [1]=>
  array(1) {
    [0]=>
    int(2)
  }
  [2]=>
  array(1) {
    [0]=>
    int(3)
  }
}

关系数组现在包含任何父级的子 ID,没有父级的元素在索引 0 中。这允许您使用递归函数将元素作为树访问。

function traverse(
  int $parentId, callable $callback, array $elements, array $relations, $level = -1
) {
  if ($elements[$parentId]) {
     $callback($elements[$parentId], $parentId, $level);
  }
  if (isset($relations[$parentId]) && is_array($relations[$parentId])) {
    foreach ($relations[$parentId] as $childId) {
      traverse($childId, $callback, $elements, $relations, ++$level);
    }
  }
}

这会为每个节点执行回调。正确的实现是RecursiveIterator,但该函数应该用于示例。

traverse(
  0,
  function(DOMNode $element, int $id, int $level) use ($xpath) {
    echo str_repeat(' ', $level);
    echo $id, ": ", $xpath->evaluate('string(type)', $element), "\n";
  },
  $elements,
  $relations
);

输出:

1: article
 2: article
  3: photo

请注意,$xpath 对象是作为回调的上下文提供的。因为$elements数组包含原始节点,所以可以使用Xpath表达式从DOM中获取与当前元素节点相关的详细数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-14
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-29
    • 1970-01-01
    相关资源
    最近更新 更多