递归解析 XML 文件
Parse XML Document recursive
我有 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 <- 2 <- 3
现在我需要为这些内容创建一个新页面 (html),如下所示:
ID 1的内容,ID 2的内容,ID 3的内容(ID不在进行中)。
我想这可以用递归函数来完成。但我不知道如何管理它?
XML中没有nesting/recursion。 <element/>
节点是兄弟节点。要建立父子关系,我建议循环遍历 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中获取与当前元素节点相关的详细数据。
我有 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 <- 2 <- 3
现在我需要为这些内容创建一个新页面 (html),如下所示: ID 1的内容,ID 2的内容,ID 3的内容(ID不在进行中)。
我想这可以用递归函数来完成。但我不知道如何管理它?
XML中没有nesting/recursion。 <element/>
节点是兄弟节点。要建立父子关系,我建议循环遍历 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中获取与当前元素节点相关的详细数据。