如何在xml php中使用行号进行搜索?

时间:2014-12-31 06:12:57

标签: php xml

我有这个示例xml文件:

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
<appConfig authenticate="true"></appConfig>
<device>J0A0AM049303</device><!--100001-->
<device>H0A0AG014628</device><!--100002-->
<device>N0A097016646</device><!--100003-->
</rss>

我正在使用以下代码搜索节点值:

<?PHP 
$doc = new DOMDocument;
$doc->load('authentication.xml');
$xpath = new DOMXPath($doc);
$searchTerm = "11C1AJ013635";
foreach ($xpath->query("//device[contains(text(), '$searchTerm')]/following::comment()") as $comment)
{
    echo "Line number: ".$comment->getLineNo(). '<br/>'.
    " Device number: ".$searchTerm.'<br/>'.
    " Comented number: ". $comment->textContent."<hr>";
    break;
}
?>

现在,我只想在将行号作为搜索词时搜索节点值。 请帮我解决这个问题。上面的代码工作正常,所以你可以复制并根据我的新请求编辑它。 尊重你的帮助。 谢谢!

1 个答案:

答案 0 :(得分:2)

XML文档中的行号没有太大意义,因为整个文档可以放在一行而不会改变其中编码的实际数据。

因此,行号或多或少提供了信息。更糟糕的是:它可能不明确,同一行号上可能有多个 device 元素。

您已经针对您的问题解决了一个最大的缺点:您无法在xpath中搜索行号。

因此,寻找一个没有所有这些负面属性的不同数字也许值得。

这里可能更有意义的是找到的<device>元素的数量或位置。它可以直接在XPath表达式中查询,“//device[1]”是文档中的第一个设备元素。

此外,首先检索您正在寻找的设备元素,然后检查您感兴趣的各种属性,您的代码可能会受益。

接下来,您需要正确引用所查找的搜索词,因为您将字符串直接注入xpath表达式(我的示例代码使用xpath_string() function)。

PHP XPath中元素位置的示例代码

我在变量$buffer中使用XML以使代码更具可移植性:

$buffer = <<<XML
<?xml version="1.0" encoding="utf-8"?>
    <rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
    <appConfig authenticate="true"></appConfig>
    <device>J0A0AM049303</device><!--100001-->
    <device>H0A0AG014628</device><!--100002-->
    <device>N0A097016646</device><!--100003-->
    <device>11C1AJ013635</device><!--100004-->
</rss>
XML;

然后在设置主要协作者 DOMDocument DOMXPath 时使用:

$doc = new DOMDocument;
$doc->loadXML($buffer);
$xpath = new DOMXPath($doc);

下一部分是根据搜索词查询设备元素。您会发现代码存在两个主要差异:首先,现在引用搜索词来缓解XPath注入,其次是XPath表达式正在寻找设备元素,而不是那些注释之后的注释第三,我不测试文本节点(text()),但搜索设备的文本内容:

$searchTerm = "3";

$expression = sprintf("//device[contains(., %s)]", xpath_string($searchTerm));
$devices    = $xpath->query($expression);

printf("Search term: %s; found %d device(s):\n", $searchTerm, $devices->length);

XPath表达式$expression是:

//device[contains(., '3')]

使用这个广泛的搜索词“3”和示例XML,它会创建两个结果,也会显示摘要输出:

Search term: 3; found 2 device(s):

由于搜索到的所有设备现在都位于$devices内,因此可以通过迭代它们并获取它们来获取您正在寻找的所有属性(甚至更多这些属性)。我有以下属性列表:

  • 搜索结果编号(新)
  • 评论行号
  • 评论文字
  • 设备位置(新)
  • 设备编号
foreach ($devices as $index => $device) {
    # ... obtain and process the infos from $device ...
}

可以通过直接在设备元素$device上操作或通过将XPath表达式作为上下文节点执行来获取这些信息(从相对于元素的角度来看)。例如,获取尚未丢失的注释节点:

    $comment = $xpath->query('following::comment()', $device);

xpath表达式“following::comment()”我从您的XPath中获取并且它在此处锚定到$device(请参阅$xpath->query()的第二个参数)。以下是对操作结果的处理:

    if (!$comment || !$comment->length) {
        throw new UnexpectedValueException('Unable to find comment.');
    }
    $comment  = $comment->item(0);
    $line     = $comment->getLineNo();
    $comment  = $comment->textContent;

这应该类似于您在代码中已有的内容,$comment此处暂时包含注释节点,因此可以从注释中获取行号和文本。

现在更有趣的是如何获取在文档中找到的设备元素$device的位置。再次使用带有$device的XPath表达式作为context-node:

$position = $xpath->evaluate("count(preceding::device) + 1", $device);

这非常简单:计算$device加1之前的设备元素的数量。这是搜索结果的位置。

要完成属性列表,只剩下设备编号或代码:

$number   = $device->textContent;

获取所有信息后,可以输出信息:

    echo "--------------------------------\n";
    echo "Device #" . (1 + $index) . ":\n" .
        " Line number: " . $line . "\n" .
        " Device position: " . $position . "\n" .
        " Device number: " . $number . "\n" .
        " Commented number: " . $comment . "\n";
} // foreach
echo "--------------------------------\n";

就是这样。这给出了以下输出:

--------------------------------
Device #1:
 Line number: 4
 Device position: 1
 Device number: J0A0AM049303
 Commented number: 100001
--------------------------------
Device #2:
 Line number: 7
 Device position: 4
 Device number: 11C1AJ013635
 Commented number: 100004
--------------------------------

使用设备位置,您现在可以通过将其用作XPath中的位置来更加独特地对设备进行寻址:

//device[1]

//device[4]

这甚至不像行号那么明显,它还允许您仅使用XPath表达式来解决 device 元素 - 这对于行号是不可能的。


完整示例代码(online demo):

<?php
/**
 * http://stackoverflow.com/questions/27717585/resolve-xml-php-node-seach-when-i-give-line-number-as-search-term
 *
 * 27717585
 */

/**
 * xpath string handling xpath 1.0 "quoting"
 *
 * @param string $input
 *
 * @return string
 */
function xpath_string($input)
{

    if (false === strpos($input, "'")) {
        return "'$input'";
    }

    if (false === strpos($input, '"')) {
        return "\"$input\"";
    }

    return "concat('" . strtr($input, array("'" => '\', "\'", \'')) . "')";
}

$buffer = <<<XML
<?xml version="1.0" encoding="utf-8"?>
    <rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
    <appConfig authenticate="true"></appConfig>
    <device>J0A0AM049303</device><!--100001-->
    <device>H0A0AG014628</device><!--100002-->
    <device>N0A097016646</device><!--100003-->
    <device>11C1AJ013635</device><!--100004-->
</rss>
XML;

$doc = new DOMDocument;
$doc->loadXML($buffer);
$xpath = new DOMXPath($doc);

$searchTerm = "3";

$expression = sprintf("//device[contains(., %s)]", xpath_string($searchTerm));
$devices    = $xpath->query($expression);

printf("Search term: %s; found %d device(s):\n", $searchTerm, $devices->length);

foreach ($devices as $index => $device) {
    $comment = $xpath->query('following::comment()', $device);
    if (!$comment || !$comment->length) {
        throw new UnexpectedValueException('Unable to find comment.');
    }
    $comment  = $comment->item(0);
    $line     = $comment->getLineNo();
    $comment  = $comment->textContent;
    $position = $xpath->evaluate("count(preceding::device) + 1", $device);
    $number   = $device->textContent;

    echo "--------------------------------\n";
    echo "Device #" . (1 + $index) . ":\n" .
        " Line number: " . $line . "\n" .
        " Device position: " . $position . "\n" .
        " Device number: " . $number . "\n" .
        " Commented number: " . $comment . "\n";
}
echo "--------------------------------\n";