【问题标题】:using xpath on non-hierarchical input在非分层输入上使用 xpath
【发布时间】:2021-07-26 05:32:30
【问题描述】:

我在 PHP 中使用 SimpleXML 和 xpath 来读取 xml 文件。问题是文件(不是我自己设计的)并不是真正的层次结构。例如:

<country>USA</country>
...
...
<city>Los Angeles</city>
<city>San Diego</city>
...
...
<country>Canada</country>
...
...
<city>Toronto</city>
<city>Banff</city>
...
...
<country>Scotland</country>
...
...
<city>Glasgow</city>
<city>Edinburgh</city>
<city>Banff</city>

... 代表任意数量的其他字段。 我可以使用 $xml = simplexml_load_file( 'filename' ); 读取文件 我的问题是找到每个$xml-&gt;xpath("//city") 对应的&lt;country&gt;

也就是说,我想知道如何在每个城市的 xml 数据中找到最后出现的国家

【问题讨论】:

  • 在处理此类数据之前,值得进行 XSLT 转换以将其转换为结构更好的 XML 格式。

标签: php xml xpath


【解决方案1】:

这里有两种观点来解决这个问题。一种是迭代所有city 元素并找到它之前的country 兄弟。

$data = new SimpleXMLElement($xml);

$cities = [];
foreach ($data->xpath('//city') as $city) {
    $country = (string)($city->xpath('preceding-sibling::country')[0] ?? '');
    $cities[] = $city.', '.$country; 
}
var_dump($cities);

另一种方法是同时迭代所有 countrycity 元素并存储当前的 country

$data = new SimpleXMLElement($xml);
$country = '';
$cities = [];
foreach ($data->xpath('//*[self::country or self::city]') as $node) {
    if ($node->getName() == 'country') {
        $country = (string)$node; 
    } else {
        $cities[] = $node.', '.$country; 
    }
}
var_dump($cities);

这将避免为每个 city 元素执行第二个 Xpath 表达式。

对于 DOM 用户来说,这看起来并没有太大的不同。首先使用二级表达式:

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);

$cities = [];
foreach ($xpath->evaluate('//city') as $city) {
    $country = $xpath->evaluate('string(preceding-sibling::country)', $city);
    $cities[] = $city->textContent.', '.$country; 
}
var_dump($cities);

存储当前国家:

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);

$country = '';
$cities = [];
foreach ($xpath->evaluate('//*[self::country or self::city]') as $node) {
    if ($node->localName == 'country') {
        $country = $node->textContent; 
    } else {
       $cities[] = $node->textContent.', '.$country; 
    }
}
var_dump($cities);

【讨论】:

    【解决方案2】:

    不幸的是,simplexml 不支持 xpath 2.0,因此要实施@Birei 的建议,您必须走很长的路:

    $cities = $xml->xpath("//city");
    foreach ($cities as $city) {
       $country =  $city[0]->xpath('./preceding-sibling::country[1]');
       echo trim($city) . ' ' . trim($country[0]);
       echo "\r\n";
    }
    

    你应该得到相同的输出。

    【讨论】:

      【解决方案3】:

      我不知道php,但有一个xpath 2.0 表达式可以找到它们:

      假设一个固定的输入文件,比如:

      <root>
      <country>USA</country>
      <city>Los Angeles</city>
      <city>San Diego</city>
      <country>Canada</country>
      <city>Toronto</city>
      <city>Banff</city>
      <country>Scotland</country>
      <city>Glasgow</city>
      <city>Edinburgh</city>
      <city>Banff</city>
      </root>
      

      xpath 2.0 表达式:

      for $x in //city return concat($x, ', ', $x/preceding-sibling::country[1])
      

      产量:

      'Los Angeles, USA'
      'San Diego, USA'
      'Toronto, Canada'
      'Banff, Canada'
      'Glasgow, Scotland'
      'Edinburgh, Scotland'
      'Banff, Scotland'
      

      【讨论】:

      • 太棒了,谢谢,我一定会试一试的。
      猜你喜欢
      • 2017-11-21
      • 2020-12-29
      • 1970-01-01
      • 2021-05-20
      • 2014-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-28
      相关资源
      最近更新 更多