在有效的 PHP query() XPath 中转换 Javascript XPath |规范化 JS XPath --> PHP

Posted

技术标签:

【中文标题】在有效的 PHP query() XPath 中转换 Javascript XPath |规范化 JS XPath --> PHP【英文标题】:Transform Javascript XPath in valid PHP query() XPath | normalize JS XPath --> PHP 【发布时间】:2012-08-01 12:55:42 【问题描述】:

这是 javascript 中的有效 XPath:

id("priceInfo")/div[@class="standardProdPricingGroup"]/span[1]

这变成了与 DOMXPath 一起使用的有效 php XPath->query() 是

//*[@id="priceInfo"]//div[@class="standardProdPricingGroup"]//span[1]
    您知道任何已经进行此转换的库或自定义组件吗? 您知道列出这两种语法差异的可用文档吗?

我主要担心可能存在很多差异,我正在寻找这些差异,但我在识别这些差异时遇到了问题。

这个问题也可以用不同的方式提出:由于 Javascript 可以有不同的有效 XPath 格式,如何规范它们以与 PHP 一起使用。

其中一个更新还提到,如果存在包含此定义的有效 DTD,则 id() 函数是有效的 XPath。我对输入 DTD 没有控制权,如果有办法找到无需任何特定 DTD 即可工作的解决方案,那就太棒了。

更新:

我想用算法将第一种格式转换为第二种格式。我的输入是第一个而不是第二个。无法更改。

正如@Nison Maël 所指出的,第二种格式是有效的 Javascript XPath,如下所示:http://jsbin.com/elatum/2/edit 不幸的是,这只会增加 Javascript XPath“碎片化”的问题。

@salathe 指出,如果记录的输入具有有效的 DTD(@Dimitre Novatchev 在评论中提到这一点,但忽略了重要性),则有效的 Javascript XPath 查询在 PHP 中可以正常工作。不幸的是,我无法控制输入 DTD,所以现在我必须研究一种方法来克服这个问题,或者找到一个即使没有有效 DTD 也能工作的解决方案。

【问题讨论】:

这是一个很好的问题!看起来那里没有任何文档(至少不是通过粗略的谷歌搜索)。我很高兴看到这个问题的答案。 第一个表达式是合法的 XPath 表达式。但是,要使 Xpath 函数 id() 起作用,XML 必须具有 DTD,并且 DTD 中的元素定义必须具有具有 ID 关键字的属性。 @DimitreNovatatchev:那么/// 的翻译呢? @choroba Java 在整个问题中没有被提及一次。此外,id() 是您链接到的规范中提到的 节点集函数 我不认为 javascript 的 xpath 与 php 有很大不同。我的意思是xpath语言应该是一样的,对吧?您能否添加您具体指的是哪个javascript xpath?对于 php,很明显,只有一个。但是等等,不止一个,但你已经写过你指的是标准的 DOMDocument 扩展,对吧。 【参考方案1】:

刚刚看到 Salathe 实际上回答了同样的问题,但考虑到您的评论并更加强调这一点:

您不需要指定任何 DTD。只要您使用DOMDocument::loadhtmlDOMDocument::loadHTMLFile 函数,HTML id 属性实际上是为xpath id() 函数注册的。使用http://jsbin.com/elatum/2/edit 中给出的演示 HTML,您甚至会在加载文档时遇到错误:

警告:DOMDocument::loadHTMLFile(): ID priceInfo 已在...中定义

这已经表明这是一个真正的 ID 属性,因为它抱怨重复。相关示例代码如下所示:

$xpath = 'id("priceInfo")/div[@class="standardProdPricingGroup"]/span[1]';

$doc = new DOMDocument();
$doc->loadHTMLFile(__DIR__ . '/../data/file-11796340.html');
$xp = new DOMXPath($doc);

$r = $xp->query($xpath);
echo $xpath, "\n";
echo $r ? $r->length : 0, ' elements found', "\n";
if (!$r) return;
foreach($r as $node) 
    echo " - ", $node->nodeValue, "\n";

输出是:

id("priceInfo")/div[@class="standardProdPricingGroup"]/span[1]
1 elements found
 - hello

如果您需要更多控制,请先运行 xpath 以将所有 HTML id 属性标记为 xpath 的 ID:

$r = $xp->query("//*[@id]");
if ($r) foreach($r as $node) 
    $node->setIdAttribute('id', true);

然后您可以使用与 id() 函数相同的 xpath,无需更改它。

【讨论】:

【参考方案2】:

您不能在表达式的开头将id("...") 翻译成//*[@id="..."][1] 吗?

例如,如果可以假设您在 id(...) 表达式中没有任何括号:

$queryRewritten =   preg_replace('/^id\(([^\)]+)\)/','//*[@id=$1][1]',$query);

Sample code

编辑:更正了替换,id() 必须是表达式中的第一个

【讨论】:

它有帮助,但我很好奇未来还会出现什么其他未记录的东西。 这并不是真正的无证资料,XPATH 规范对这种行为非常清楚。未记录的部分是关于浏览器 DOM 引擎隐式地将 Doctype 应用于 HTML DOM。 @Pentium10 也许您需要更明确地说明您要实现的目标 正如我更新问题的最后一行所说,我想找到一个独立于 DTD 的解决方案。 除了使用不同命名空间前缀的文档外,这可能会欺骗 wpath 引擎。或为属性指定默认隐含值的 DTD(HTML 绝不应如此)。你应该是安全的【参考方案3】:

这不是一个完整的答案,但它太大而无法作为评论发表,它可能会对您有所帮助。

如果您可以控制输入 XML,那么您可以通过在 id 属性前加上 xml: 前缀,在 XML 文档本身中显式声明它们,而不是使用 DTD 来声明 id 属性。

例如,如果你有 XML

<foo id="x27"/>

改成

<foo xml:id="x27"/>

那么 id() 函数会将该属性识别为正式的 XML id 类型,而不仅仅是名称为 id 的属性。

我知道这个“技巧”在 Saxon 处理器上有效,但我必须承认我没有在 PHP 上尝试过。

W3C xml:id

【讨论】:

PHP 的DOMElement::setIdAttribute 允许指定xml:id 属性的名称,而不管输入如何(并且无需更改它)。一个例子是my answer

以上是关于在有效的 PHP query() XPath 中转换 Javascript XPath |规范化 JS XPath --> PHP的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PHP 的 mysqli_query() 中转义双引号字符?

php 8 中的 xpath 如何处理?

如何在 PHP 中使用 XPath 设置 (not(contains))

如何在php中转义字符串中的哈希符号

使用 php 和 xpath 获取父元素的内部 HTML

如何使用php在xpath中使用变量作为属性值?