我需要解析这个包含一些自定义标记的XML文件,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<glz:Config xmlns:glz="http://www.glizy.org/dtd/1.0/">
<glz:Import src="config.xml" />
<glz:Group name="thumbnail">
<glz:Param name="width" value="200" />
<glz:Param name="height" value="*" />
</glz:Group>
</glz:Config>
当它到达标记<glz:Import src="config.xml" />
时,它需要解析文件 config.xml ,其中包含如下内容:
<?xml version="1.0" encoding="utf-8"?>
<glz:Config xmlns:glz="http://www.glizy.org/dtd/1.0/">
<glz:Group name="folder">
<glz:Param name="width" value="100" />
<glz:Param name="height" value="200" />
</glz:Group>
</glz:Config>
最终结果应该是这样的数组。它包含两个已解析文件的值:
$result['thumbnail/width'] = 200;
$result['thumbnail/height'] = '*';
$result['folder/width'] = 100;
$result['folder/height'] = 200;
这就是我管理XML解析的方法。我的问题是我不知道如何将新结果与已经(旧)解析的结果合并。在这里你可以看到我的代码:
function parseFile(){
$reader = new XMLReader;
$reader->open($this->fileName);
while ($reader->read()){
if ($reader->name == 'glz:Group')
{
$groupName = $reader->getAttribute('name');
$reader->read();
$reader->read();
while ($reader->name == 'glz:Param')
{
if (strpos($reader->getAttribute('name'),'[]') == true)
{
$arrayGroupName = substr($reader->getAttribute('name'), 0, -2);
if(empty($filters[$groupName.'/'.$arrayGroupName]))
{
$filters[$groupName.'/'.$arrayGroupName] = array();
array_push($filters[$groupName.'/'.$arrayGroupName],$this->castValue($reader->getAttribute('value')));
$this->result[$groupName."/".$arrayGroupName] = $filters[$groupName.'/'.$arrayGroupName];
}
else
{
array_push($filters[$groupName.'/'.$arrayGroupName],$this->castValue($reader->getAttribute('value')));
$this->result[$groupName."/".$arrayGroupName] = $filters[$groupName.'/'.$arrayGroupName];
}
}
else
{
$this->result[$groupName."/".$reader->getAttribute('name')] = $this->castValue($reader->getAttribute('value'));
}
$reader->read();
$reader->read();
}
}
else if ($reader->name == 'glz:Param')
{
if (strpos($reader->getAttribute('name'),'[]') == true)
{
$arrayGroupName = substr($reader->getAttribute('name'), 0, -2);
if(empty($filters[$arrayGroupName]))
{
$filters[$arrayGroupName] = array();
array_push($filters[$arrayGroupName],$this->castValue($reader->getAttribute('value')));
$this->result[$$arrayGroupName] = $filters[$arrayGroupName];
}
else
{
array_push($filters[$arrayGroupName],$this->castValue($reader->getAttribute('value')));
$this->result[$arrayGroupName] = $filters[$arrayGroupName];
}
}
else
{
$this->result[$reader->getAttribute('name')] = $this->castValue($reader->getAttribute('value'));
}
}
else if ($reader->name == 'glz:Import')
{
$file = $reader->getAttribute('src');
$newConfig = new Config($file);
$newConfig->parseFile();
}
}
return $this->result;
}
当我找到标记 时,如何每次合并解析文件时得到的结果?
非常感谢你!
答案 0 :(得分:1)
据我了解你的问题,你需要稍微重构一下代码。
重写解析器函数,而不引用$ this-&gt; result和$ this-&gt; fileName。
将您函数中的这些变量重新声明为$ result和$ fileName。 将$ fileName添加为函数参数。
在函数中添加另一个变量$ result_config。
当你阅读配置标签时, 以递归方式调用函数而不是创建新类:
-$file = $reader->getAttribute('src');
- $newConfig = new Config();
+ $file = $reader->getAttribute('src');
+ $result_config = $this->parseFile($file);
然后在完成两个文件后最终合并两个结果:
if ($result_config) {
$this->result = array_merge($result_config, $this->result);
}
return $this->result;
答案 1 :(得分:1)
您需要将读取逻辑放入一个以文件名作为参数的函数中,以便在找到Import
元素时调用自身。让函数将值作为数组返回并合并结果。
在DOM中,这不太复杂:
function readConfigurationFile($fileName) {
$document = new DOMDocument();
$document->load($fileName);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('g', 'http://www.glizy.org/dtd/1.0/');
$result = [];
foreach ($xpath->evaluate('/g:Config/*[self::g:Import or self::g:Group]') as $node) {
switch ($node->localName) {
case 'Import' :
$result = array_merge($result, readConfigurationfile($node->getAttribute('src')));
break;
case 'Group' :
$groupName = $node->getAttribute('name');
foreach ($xpath->evaluate('g:Param', $node) as $paramNode) {
$result[
sprintf('%s/%s', $groupName, $paramNode->getAttribute('name'))
] = $paramNode->getAttribute('value');
}
break;
}
}
return $result;
}
var_dump(readConfigurationFile('main.xml'));
输出:
array(4) {
["folder/width"]=>
string(3) "100"
["folder/height"]=>
string(3) "200"
["thumbnail/width"]=>
string(3) "200"
["thumbnail/height"]=>
string(1) "*"
}
XMLReader中的方法相同,但有点复杂。
function readLargeConfigurationFile($fileName) {
$reader = new XMLReader();
$reader->open($fileName);
$xmlns = 'http://www.glizy.org/dtd/1.0/';
$document = new DOMDocument();
$xpath = new DOMXpath($document);
$xpath->registerNamespace('g', $xmlns);
$result = [];
// find the first Import or Group in the namespace
do {
$found = $reader->read();
} while(
$found &&
!(
$reader->namespaceURI === $xmlns &&
($reader->localName === 'Import' || $reader->localName === 'Group')
)
);
while ($found) {
switch ($reader->localName) {
case 'Import' :
$result = array_merge($result, readLargeConfigurationFile($reader->getAttribute('src')));
break;
case 'Group' :
// expand Group into DOM for easier access
$groupNode = $reader->expand($document);
$groupName = $groupNode->getAttribute('name');
foreach ($xpath->evaluate('g:Param', $groupNode) as $paramNode) {
// read a Param
$result[
sprintf('%s/%s', $groupName, $paramNode->getAttribute('name'))
] = $paramNode->getAttribute('value');
}
break;
}
// iterate sibling nodes to find the next Import or Group
do {
$found = $reader->next();
} while(
$found &&
!(
$reader->namespaceURI === $xmlns &&
($reader->localName === 'Import' || $reader->localName === 'Group')
)
);
}
return $result;
}
var_dump(readLargeConfigurationFile('main.xml'));
请注意,该示例不使用$name
属性。它包含名称空间别名/前缀glz
。命名空间前缀是可选的,可以更改 - 即使在单个文档中也是如此。使用$localName
和$namespaceURI
属性。
使用XMLReader::expand()
,您可以将当前节点扩展为DOM。一种典型的方法是使用XML阅读器仅迭代外部节点。如果您知道节点及其后代足够小,则将它们扩展为DOM以便于访问。