使用xpath获取特定的节


Using xpath to grab a particular section

我有以下xml,我正在尝试获取以下元素,但不确定它是如何完成的,接近底部:

Cashless catering Primary School

有人能建议如何使用xpath通过使用refinement="Pupil"标记/属性来获取它吗?

<?xml version="1.0" encoding="utf-16" standalone="no"?>
<IntegrationExport xmlns="urn:NSCP-Integration-Export-v1">
<Data>
<Citizen messageId="331013" id="43018" authorisingId="1" messageTypeId="1" smartcardId="12345680201327582" serviceId="57" issuer="Primary School">
  <Services>
    <Service application="ISO File Handler" refinement="ISO File Handler" />
    <Service application="CCDA" refinement="CCDA">
      <Item name="SMARTCARDID">12345680201327582</Item>
      <Item name="IIN" />
      <Item name="CARDNO" />
      <Item name="ISSUE">7</Item>
      <Item name="TITLE" />
      <Item name="FORENAME">Jon</Item>
      <Item name="INITIALS" />
      <Item name="SURNAME">Doe</Item>
      <Item name="NAME">Jon Doe</Item>
      <Item name="DOB">2004-11-04 00:00:00</Item>
      <Item name="GENDER">1</Item>
      <Item name="Ethnic Origin">White Other</Item>
      <Item name="Faith" />
      <Item name="SEN / Disability" />
      <Item name="Language" />
      <Item name="DOBVERIFIED">1</Item>
      <Item name="FLAT" />
      <Item name="HOUSE NUMBER/NAME" />
      <Item name="Street" />
      <Item name="Locality" />
      <Item name="Postal Town" />
      <Item name="County" />
      <Item name="POSTCODE" />
      <Item name="LOCAL AUTHORITY" />
      <Item name="RESIDENT">R</Item>
      <Item name="UPRN" />
      <Item name="HOME TEL" />
      <Item name="WORK TEL" />
      <Item name="MOBILE" />
      <Item name="EMAIL" />
      <Item name="Password" />
      <Item name="EXPIRY DATE">2017-09-01 00:00:00</Item>
      <Item name="Reward points">90</Item>
      <Item name="UPN">E301207408111</Item>
      <Item name="ParentPay ID">4292111</Item>
      <Item name="PayPoint Account No" />
      <Item name="YEARGROUP">3</Item>
      <Item name="FORMNAME">RED</Item>
      <Item name="Acknowledgement" />
      <Item name="USERID" />
      <Item name="REWARDS DATE" />
      <Item name="BARCODE">00100048123</Item>
      <Item name="MEMBER ID" />
      <Item name="LEISURECODE" />
      <Item name="LEISUREDATE" />
    </Service>
    <Service application="Special Needs" refinement="Special Needs">
      <Item name="CUSTOM MESSAGE">Placeholder message for special needs application.</Item>
      <Item name="SCREEN COLOUR">00</Item>
      <Item name="FONT">00</Item>
      <Item name="CHARACTER SIZE">00</Item>
      <Item name="SPEECH OUTPUT">00</Item>
    </Service>
    <Service application="Cashless catering Primary School" refinement="Pupil" />
    <Service application="Splash" refinement="Splash">
      <Item name="USERNAME" />
      <Item name="INITIAL PASSWORD" />
    </Service>
  </Services>
</Citizen>

代码

$endpoint = "http://111.222.11.200/someUrl.asmx?WSDL";
$client = new SoapClient($endpoint, array('trace' => 1));
$xml = $client->GetCitizenData($arrValues);
$xml = (string)$xml->GetCitizenDataResult;
$xml = preg_replace('/(<'?xml[^?]+?)utf-16/i', '$1utf-8', $xml);
$xml_element = simplexml_load_string($xml);
$nodes = $xml_element->xpath('/Citizen/Services/Service[@refinement="Pupil"]/@application');

当我转储节点阵列时,我得到以下信息:

SimpleXMLElement Object ( [@attributes] => Array ( [application] => Cashless catering Primary School ) ) 

因此,我们最终得出1,就XPath而言,您已经得到了正确的结果;路径表达式检索正确的节点。

唯一的困难是评估路径表达式会返回数组。当整个数组被转储时,您自然会得到比所需字符串本身更多的返回。使用

var_dump($nodes[application]);

将只转储此数组中的第一个项目:

string(32) "Cashless catering Primary School"

使用reset()可以做类似的事情:

<?php
$array = array( application => 'Cashless catering Primary School');
echo reset($array);
?>

CCD_ 2也是如此。


但是请注意,在许多情况下都不鼓励使用PHP和Java的SimpleXML库,因为它们的行为可能会令人惊讶。在您的特定情况下,可以原谅预期像这样的XPath表达式

//Citizen

不会返回任何内容,因为这些元素位于默认命名空间中。通常,默认名称空间需要在PHP代码中重新声明,并提供给XPath引擎。但是SimpleXML忽略了默认的名称空间——事实上,这"并不那么简单"。


1这应该会给你上一堂写好问题的课。对于您未来的问题,请确保立即包含所有相关信息。

假设您已经为命名空间urn:NSCP-Integration-Export-v1注册了别名x,只获取属性"application":

//x:Citizen/x:Services/x:Service[@refinement='Pupil']/@application

编辑

如果不能使用命名空间,可以使用与命名空间无关的'local-name()'。注意,我假设在同一个树中只有一种类型的CitizenService,这似乎是合理的,因为您的示例文档引用了一个名称空间。

//*[local-name()='Citizen']//*[local-name()='Service' and @refinement='Pupil']/@application