Combining two Xpaths into one loop?

2020-04-16 17:57发布

I'm using xpath to grab information from a document, the only issue is I havn't been able to combine them into 1 for loop so the information displays correctly on the page. My code is:

<?php

$doc = new DOMDocument;
$doc->preserveWhiteSpace = FALSE;
$doc->load('http://mdoerrdev.com/xml/updates-mits.xml');

$xpath = new DOMXPath($doc);
$xpath->registerNamespace('MITS', "http://www.mitsproject.org/namespace");

$unitName  = $xpath->evaluate("//ILS_Unit[@FloorplanID='550584']/Unit/MITS:MarketingName");
$unitPrice = $xpath->evaluate("//ILS_Unit[@FloorplanID='550584']/Unit/MITS:Information/MITS:MarketRent");

?>

    <div class="unit-name">
        <?php
        foreach ($unitName as $un) {
            echo $un->nodeValue . "\n";
        }
        ?>
    </div>

    <div class="floor-plan-box fpb-one-bedroom cleafix"> <?php
        foreach ($unitPrice as $up) {
            echo $up->nodeValue . "\n";

            ?><img src='<?php bloginfo('stylesheet_directory'); ?>/img/floorplans/<?php echo "550584" ?>.jpg' /> <?php
        };
        ?>
    </div>

(from: http://pastie.org/8360106)

I need to combine the MarketRent and MarketingName information together as opposed to having them display separately as they do in the current code.

3条回答
beautiful°
2楼-- · 2020-04-16 18:16

This depends a bit on what you're trying to achieve. You can combine two xpath expressions by using the Union operator (pipe |):

$unitName = $xpath->evaluate("
(
    //ILS_Unit[@FloorplanID='550584']/Unit/MITS:MarketingName
    |//ILS_Unit[@FloorplanID='550584']/Unit/MITS:Information/MITS:MarketRent
)
");

Which then would return all evaluated nodes in document order:

<div class="unit-name">
        1265.0000
C-304
1265.0000
C-308
1255.0000
C-204
1255.0000
C-208
1230.0000
C-102
1230.0000
C-103
    </div>

Which is probably not what you're looking for. Instead you want to either evaluate over both results the same time, which can be achieved with a MultipleIterator:

$unitName  = $xpath->evaluate("//ILS_Unit[@FloorplanID='550584']/Unit/MITS:MarketingName");
$unitPrice = $xpath->evaluate("//ILS_Unit[@FloorplanID='550584']/Unit/MITS:Information/MITS:MarketRent");

$it = new MultipleIterator();
$it->attachIterator(new IteratorIterator($unitName));
$it->attachIterator(new IteratorIterator($unitPrice));

foreach($it as $value) {
    list($name, $price) = $value;
    echo " * ", $name->nodeValue, " ", $price->nodeValue, "\n";
}

Output:

 * C-304 1265.0000
 * C-308 1265.0000
 * C-204 1255.0000
 * C-208 1255.0000
 * C-102 1230.0000
 * C-103 1230.0000

This is probably more what you're looking for. Also for what you do, SimpleXMLElement might be more easy to use because it already allows to output nodes in string context.

Additionally there is another concept that directly maps shallow objects to an underlying XML document and it has been outlined here:

It is quite an interesting concept. IIRC I wrote something similar like applying one xpath query onto all nodes of another one, but I don't find it right now.

查看更多
一纸荒年 Trace。
3楼-- · 2020-04-16 18:21

Use the context node argument of $xpath->evaluate()...

Your code with these adjustments:

<?php

$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->load('http://mdoerrdev.com/xml/updates-mits.xml');

$xpath = new DOMXPath($doc);
$xpath->registerNamespace('MITS', "http://www.mitsproject.org/namespace");

$units = $xpath->evaluate("//ILS_Unit[@FloorplanID='550584']/Unit");
echo "<table>";
echo "<tr><th>Marketing Name</th><th>Market Rent</th></tr>";
foreach($units as $unit)  {
  $marketingName = $xpath->evaluate("string(MITS:MarketingName)", $unit);
  $marketRent = $xpath->evaluate("string(MITS:Information/MITS:MarketRent)", $unit);
  echo "<tr>";
  echo "  <td>" . $marketingName . "</td>";
  echo "  <td>" . $marketRent . "</td>";
  echo "</tr>";
}
echo "</table>";
?>

Yields this output with MarketingName and MarketRent displayed together rather than separately per your request:

enter image description here

查看更多
仙女界的扛把子
4楼-- · 2020-04-16 18:34

I couldn't resist to give this another try to apply a model on some XML source. I opted for SimpleXMLElement for this answer. It is merely transparent and has mostly only implications on the xpath expressions. It should be possible to translate the same interface to DOMDocument.

$unitPrototype = new XPrototype(
    "//ILS_Unit[@FloorplanID='550584']/Unit",
    [
        'name'  => 'MITS:MarketingName',
        'price' => 'MITS:Information/MITS:MarketRent'
    ]
);

$units = new XList('http://mdoerrdev.com/xml/updates-mits.xml', $unitPrototype);

foreach ($units as $unit) {
    echo
    $unit->name, " ",
    $unit->price, "\n";
}

The source of the used types like XPrototype and XList is on Github: XObjects.php.

查看更多
登录 后发表回答