我对在PHP中使用Xpath有基本的了解,但我在一个特定的案例中遇到了一些问题,我认为问题出在标准上。
这是XML的片段,它基于OTA标准:
<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>
我想要什么:
- 基于元素
<RoomID>
获取特定的<RoomRate>
标记 - 获取全局RoomRate
<Total>
标记。我不想要<Rate>
标签中的<Total>
标签。这就是我使用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,所以,我不知道我做错了什么,我正在考虑在不同地方使用两个相同标签的标准中的问题,尽管这没有多大意义。
还有其他一些表达方式需要我尝试吗?
您正在从文档中获取命名空间。
$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()
,它将第一个找到的节点强制转换为字符串并返回
您需要使用一个前缀(您注册的),如果您想相对于上下文节点进行搜索,我认为您希望以.//
而不是//
开始您的路径,所以请尝试".//a:RoomRate[a:RoomID=$roomID]/a:Total"