我具有有关在PHP中使用Xpath的基本知识,但在处理特定案例时遇到了一些麻烦,我认为问题出在标准中。

这是XML的代码段,它基于OTA standards

<SendHotelResResult xmlns:a="http://schemas/Models/OTA" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <a:RoomRates>
         <a:RoomRate>
             <a:EffectiveDate>2015-11-13T00:00:00</a:EffectiveDate>
             <a:ExpireDate>2015-11-15T00:00:00</a:ExpireDate>
             <a:RatePlanID>25</a:RatePlanID>
             <a:RatesType>
                 <a:Rates>
                     <a:Rate>
                         <a:AgeQualifyingCode i:nil="true"/>
                                 <a:EffectiveDate>2015-11-13T00:00:00</a:EffectiveDate>
                         <a:Total>
                             <a:AmountAfterTax>0</a:AmountAfterTax>
                             <a:AmountBeforeTax>260.00</a:AmountBeforeTax>
                             <a:CurrencyCode>EUR</a:CurrencyCode>
                          </a:Total>
                     </a:Rate>
                     <a:Rate>
                         <a:AgeQualifyingCode i:nil="true"/>
                             <a:EffectiveDate>2015-11-14T00:00:00</a:EffectiveDate>
                         <a:Total>
                             <a:AmountAfterTax>0</a:AmountAfterTax>
                             <a:AmountBeforeTax>260.00</a:AmountBeforeTax>
                             <a:CurrencyCode>EUR</a:CurrencyCode>
                         </a:Total>
                     </a:Rate>
                 </a:Rates>
             </a:RatesType>
             <a:RoomID>52</a:RoomID>
             <a:Total>
                 <a:AmountAfterTax>546.00</a:AmountAfterTax>
                 <a:AmountBeforeTax>520.00</a:AmountBeforeTax>
                 <a:CurrencyCode>EUR</a:CurrencyCode>
             </a:Total>
         </a:RoomRate>
     </a:RoomRates>
</SendHotelRes>


我想要的是:


根据元素<RoomRate>获取特定的<RoomID>标签。
获取全局的RoomRate <Total>标签。我不希望<Total>标记内的<Rate>标记。这就是为什么我使用xpath而不是简单的getElementsByTagName('Total')的原因。我不知道OTA标准是否有某种方法可以区分Total标签。


到目前为止的尝试:

$dom = new DOMDocument();
$response = $dom->load($xmlSendHotelRes);
$roomID = '52';
$roomRatesTag = $response->getElementsByTagName('RoomRates')->item(0);
$prefix = $roomRatesTag->prefix;
$namespace = $roomRatesTag->lookupNamespaceURI($prefix);
$xpath = new DOMXpath($dom);
$xpath->registerNamespace($prefix, $namespace);
$roomRateTotal = $xpath->query("//RoomRate[RoomID=$roomID]/Total", $roomRatesTag, true);


我已经尝试使用$roomRatesTag和不使用./RoomRate[RoomID=$roomID]/Total作为上下文以及其他类似表达式:
//RoomRate[RoomID=$roomID]/Total//RoomRate/[RoomID=$roomID]/Total//RoomRate[RoomID=$roomID]/Total//RoomRate/RoomID[text() = $roomID]/../Total$roomRate = $xpath->query("//RoomRate");,但它们中的任何一个都可以。

实际上,即使也会返回一个空的DOMNodeList,所以,我不知道我做错了什么,我正在考虑在不同位置具有2个相同标签的标准中的问题,尽管这没有多大意义。

我还需要尝试其他一些表达方式吗?

最佳答案

您正在从文档中获取名称空间。

$prefix = $roomRatesTag->prefix;
$namespace = $roomRatesTag->lookupNamespaceURI($prefix);


但这不是必需的还是一个好主意。您知道该文档使用OTA,所以您知道名称空间为http://schemas/Models/OTA

前缀只是实际名称空间值的别名,下面的3个XML示例全部解析为节点{http://schemas/Models/OTA}RoomRates


<a:RoomRates xmlns:a="http://schemas/Models/OTA"/>
<ota:RoomRates xmlns:ota="http://schemas/Models/OTA"/>
<RoomRates xmlns="http://schemas/Models/OTA"/>


您的Api必须在名称空间内寻找节点。

一种可能性是使用*NS(可识别命名空间)方法。

$response->getElementsByTagNameNS('http://schemas/Models/OTA', 'RoomRates')->item(0);


另一种方法是使用Xpath并为名称空间注册前缀。这可以是文档的前缀,也可以是其他前缀。

$document = new DOMDocument();
$document->load($xmlSendHotelRes);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('ota', 'http://schemas/Models/OTA');

var_dump(
  $xpath->evaluate(
    'string(//ota:RoomRates/ota:RoomRate[ota:RoomID=$roomID]/ota:Total)')
  )
);


对于位置路径,DOMXpath::evaluate()将返回DOMNodeList,但使用string()会将找到的第一个节点转换为字符串并返回。

08-18 12:35