我正在使用分层数据的网站。在努力使用MySQL数据库(真的很复杂......)之后,我决定深入研究XML,因为它听起来像XML完全符合我的需求。
现在我正在尝试使用XML文件和SimpleXML。但首先,这是我的XML文件的样子:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<content>
<parent>
<child id="1">
<title>child 1</title>
<child id="1">
<title>child 1.1</title>
<child id="1">
<title>child 1.1.1</title>
</child>
</child>
<child id="2">
<title>child 1.2</title>
<child id="1">
<title>child 1.2.1</title>
<child id="1">
<title>child 1.2.1.1</title>
</child>
</child>
<child id="2">
<title>child 1.2.2</title>
</child>
</child>
<child id="3">
<title>child 1.3</title>
</child>
</child>
</parent>
</content>
正如您所看到的,它具有子节点的变化“深度”。我也不知道孩子的深度,因为他们是由网络应用程序创建的。这个深度或“层数”可以变得非常高。
现在我想在我的网站上阅读这个XML文件。例如,我想将其可视化为树,所有子节点都表示为连接到其父圆的圆。
我已经设法让foreach获得所有第一层“子”元素,然后获得所有第二层“子”元素。问题是,这限制了我可以想象的层数,因为我不能有十几个嵌套的foreach'es。
现在我已经头脑中想到了一种“无限嵌套的foreach结构”,以获得“子”节点的所有层。但我无法找到办法。
你知道怎么做吗?请帮我!提前谢谢。
PS:对不起我的英语,我是德国青少年学生:)
编辑:这是我的test.php中的代码:
<?php
if (file_exists('mydata.xml'))
{
$xml = simplexml_load_file('mydata.xml');
?>
<ul>
<?php
foreach($xml->parent->child as $item) // Go through first layer
{
echo "<li>".$item->title;
echo "<ul>"; // Open second layer <ul>
foreach($item->child as $item) // Go through second layer
{
echo "<li>".$item->title."</li>";
}
echo "</ul>"; // Close second layer <ul>
echo "</li>"; // Close child <li>
}
}
else
{
exit('Konnte Datei nicht laden.');
}
?>
</ul>
这就是我期待的结果:
- child 1
- child 1.1
- child 1.2
- child 1.3
所以这很好用,但正如评论中所提到的,我不仅要求第1层到第2层,而且需要第1层到第n层。如果有人有想法,我会非常感激:)
答案 0 :(得分:1)
XML文件中的内容是元素的树结构。
在PHP中显示此类结构的一种常见方法是使用显示ASCII树的 RecursiveTreeIterator :
\-child 1
|-child 1.1
| \-child 1.1.1
|-Chapter 1.2
| |-child 1.2.1
| | \-child 1.2.1.1
| \-child 1.2.2
\-child 1.3
它的用法相对简单,但它要求您为自己的数据结构编写自己的 RecursiveIterator 。下面是使用这种递归迭代器的示例代码,即专门为您的用例创建的 RecursiveChildIterator :
<?php
/**
* recursive display of XML contents
*/
require 'RecursiveChildIterator.php';
$content = simplexml_load_file('content.xml');
$iterator = new RecursiveChildIterator($content->parent->child);
$tree = new RecursiveTreeIterator($iterator);
foreach ($tree as $line) {
echo $line, "\n";
}
正如此示例所示, RecursiveChildIterator 在顶部是必需的,其文件RecursiveChildIterator.php
包含以下代码,即类定义。
在构造函数中,大多数工作都是将$children
参数验证为false-y或foreach-able,并且如果每个迭代都提供 SimpleXMLElement :
/**
* Class RecursiveChildIterator
*/
class RecursiveChildIterator extends IteratorIterator implements RecursiveIterator
{
/**
* @var SimpleXMLElement
*/
private $children;
public function __construct($children)
{
if ($children) {
foreach ($children as $child) {
if (!$child instanceof SimpleXMLElement) {
throw new UnexpectedValueException(
sprintf('SimpleXMLElement expected, %s given ', var_export($child, true))
);
}
}
}
然后构造函数继续从参数中创建一个合适的 Traversable ,以便父类 IteratorIterator 可以将它用作依赖项:
if ($children instanceof Traversable) {
$iterator = $children;
} elseif (!$children) {
$iterator = new EmptyIterator();
} elseif (is_array($children) || is_object($children)) {
$iterator = new ArrayObject($children);
} else {
throw new UnexpectedValueException(
sprintf("Array or Object expected, %s given", gettype($children))
);
}
$this->children = $children;
parent::__construct($iterator);
}
然后它定义了当前元素的值是文本树的标题值:
public function current()
{
return parent::current()->title;
}
然后将所需的实现作为 RecursiveIterator 来处理使用接口的两个子方法的递归迭代:
public function hasChildren()
{
$current = parent::current();
return (bool)$current->child->count();
}
public function getChildren()
{
$current = parent::current();
return new self($current->child);
}
}
在实现接口 RecursiveIterator 的类中实现遍历子项的逻辑允许您将其传递给接受 RecursiveIterator 的所有内容,就像它的情况一样的 RecursiveTreeIterator 强>
答案 1 :(得分:0)
下面是两个基本相同的例子。在每一个中,我们定义一个函数renderNode()
,它被递归调用以呈现嵌套列表。没有很多代码,所以没有太多可说的。
一个基于SimpleXML,因为那是你目前正在尝试的。
另一个基于DOM extension,因为我个人认为它是一个更好的API(由于here列出的所有原因,然后是一些。)
对于你在这里所做的事情,你使用的并不是非常相关,但选择总是很好。
$dom = new DOMDocument();
$dom->load('mydata.xml');
$xpath = new DOMXPath($dom);
echo "<ul>";
foreach ($xpath->query('/content/parent/child') as $node) {
renderNode($node, $xpath);
}
echo "</ul>";
function renderNode(DOMElement $node, DOMXPath $xpath) {
echo "<li>", $xpath->evaluate('string(title)', $node);
$children = $xpath->query('child', $node);
if ($children->length) {
echo "<ul>";
foreach ($children as $child) {
renderNode($child, $xpath);
}
echo "</ul>";
}
echo "</li>";
};
$xml = simplexml_load_file('mydata.xml');
echo "<ul>";
foreach ($xml->parent->child as $node) {
renderNode($node);
}
echo "</ul>";
function renderNode($node) {
echo "<li>", $node->title;
if ($node->child) {
echo "<ul>";
foreach ($node->child as $child) {
renderNode($child);
}
echo "</ul>";
}
echo "</li>";
}
<ul>
<li>child 1
<ul>
<li>child 1.1
<ul><li>child 1.1.1</li></ul>
</li>
<li>child 1.2
<ul>
<li>child 1.2.1
<ul><li>child 1.2.1.1</li></ul>
</li>
<li>child 1.2.2</li>
</ul>
</li>
<li>child 1.3</li>
</ul>
</li>
</ul>
只是为了踢,这是使用XSLT的奖金选项。美化输出与上述相同。
<强> PHP:强>
$xmldoc = new DOMDocument();
$xmldoc->load('mydata.xml');
$xsldoc = new DOMDocument();
$xsldoc->load('example.xsl');
$xsl = new XSLTProcessor();
$xsl->importStyleSheet($xsldoc);
echo $xsl->transformToXML($xmldoc);
<强> example.xsl:强>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="no"/>
<xsl:template match="/content/parent">
<ul>
<xsl:apply-templates select="child"/>
</ul>
</xsl:template>
<xsl:template match="child">
<li>
<xsl:value-of select="title"/>
<xsl:if test="child">
<ul>
<xsl:apply-templates select="child"/>
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>