我正在尝试解析这样的数据:
<vin:layout name="Page" xmlns:vin="http://www.example.com/vin">
<header>
{someText}
<div>
<!-- some invalid xml code -->
<aas>
<nav class="main">
<vin:show section="Menu" />
</nav>
</div>
</header>
</vin:layout>
如何在PHP中解析这样的数据?
我尝试过DOM,但它不起作用,因为根元素中的xml格式不正确。我可以告诉解析器,没有vin
命名空间的任务是文本吗?
答案 0 :(得分:1)
我可能会在其上抛出一种Tagsoup解析器。可以读取你的格式的东西,除了那些缺点看起来很好写。对于简单的基于正则表达式的扫描程序而言,没有什么能够在文本上留下来。我只用你得到的四种节点类型称我的Tagsoup
:Starttag,Endtag,Text和Comment。对于标签,您需要了解其标记名和NamespacePrefix。为了方便起见,它的命名类似于XML / HTML,但事实上这完全是“自己动手”,所以不要将这些术语扩展到任何标准。
用于更改没有名称空间前缀的每个标记(开头或结尾)的用法看起来像($string
包含您在问题中的数据):
$scanner = new TagsoupIterator($string);
$nsPrefix = 'vin';
foreach ($scanner as $node) {
$isTag = $node instanceof TagsoupTag;
$isOfNs = $isTag && $node->getTagNsPrefix() === $nsPrefix;
if ($isTag && !$isOfNs) {
$node = strtr($node, ['&' => '&', '<' => '<']);
}
echo $node;
}
输出:
<vin:layout name="Page" xmlns:vin="http://www.example.com/vin">
<header>
{someText}
<div>
<!-- some invalid xml code -->
<aas>
<nav class="main">
<vin:show section="Menu" />
</nav>
</div>
</header>
</vin:layout>
用于提取命名空间的某个标记内的所有内容的用法可能如下所示:
$scanner = new TagsoupIterator($string);
$parser = new TagsoupForwardNavigator($scanner);
$startTagWithNsPrefix = function ($namespace) {
return function (TagsoupNode $node) use ($namespace) {
/* @var $node TagsoupTag */
return $node->getType() === Tagsoup::NODETYPE_STARTTAG
&& $node->getTagNsPrefix() === $namespace;
};
};
$start = $parser->nextCondition($startTagWithNsPrefix('vin'));
$tag = $start->getTagName();
$parser->next();
echo $html = implode($parser->getUntilEndTag($tag));
输出:
<header>
{someText}
<div>
<!-- some invalid xml code -->
<aas>
<nav class="main">
<vin:show section="Menu" />
</nav>
</div>
</header>
下一部分是替换$string
的那一部分。由于Tagsoup提供二进制偏移和长度,这很容易(我通过SimpleXML快捷方式):
$xml = substr($string, 0, $start->getEnd()) . substr($string, $parser->getOffset());
$doc = new SimpleXMLElement($xml);
$doc[0] = $html;
echo $doc->asXML();
输出:
<vin:layout xmlns:vin="http://www.example.com/vin" name="Page">
<header>
{someText}
<div>
<!-- some invalid xml code -->
<aas>
<nav class="main">
<vin:show section="Menu" />
</nav>
</div>
</header>
</vin:layout>
根据具体需要,这需要更改实施。例如,这个不允许将相同的标签彼此放在一起。它不会让你失望,但它没有处理。不知道你是否有这种情况,如果你需要添加一些打开/关闭计数器,导航器类可以很容易地扩展,甚至提供两种结束标记查找方法。
此处给出的示例使用的是Tagsoup,您可以在此要点看到:https://gist.github.com/4415105