简体   繁体   中英

can't figure out SimpleXML syntax

I'm trying to parse some items from a log of soap request XML and can't seem to figure out how to get to the innards of a SimpleXMLElement. Here's an example of the XML I'm using:

<?xml version="1.0" encoding="UTF-8"?>
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://fedex.com/ws/rate/v9">
     <SOAP-ENV:Body>
      <ns1:RateRequest>
       <ns1:WebAuthenticationDetail>
        <ns1:UserCredential>
         <ns1:Key>aaaaaaaaaaa</ns1:Key>
         <ns1:Password>aaaaaaaaaaaaaaaaaaa</ns1:Password>
        </ns1:UserCredential>
       </ns1:WebAuthenticationDetail>
       <ns1:ClientDetail>
        <ns1:AccountNumber>11111111</ns1:AccountNumber>
        <ns1:MeterNumber>88888888</ns1:MeterNumber>
       </ns1:ClientDetail>
       <ns1:TransactionDetail>
        <ns1:CustomerTransactionId>1</ns1:CustomerTransactionId>
       </ns1:TransactionDetail>
       <ns1:Version>
        <ns1:ServiceId>crs</ns1:ServiceId>
        <ns1:Major>9</ns1:Major>
        <ns1:Intermediate>0</ns1:Intermediate>
        <ns1:Minor>0</ns1:Minor>
       </ns1:Version>
       <ns1:ReturnTransitAndCommit>true</ns1:ReturnTransitAndCommit>
       <ns1:RequestedShipment>
        <ns1:ShipTimestamp>2013-08-06T12:39:26-04:00</ns1:ShipTimestamp>
        <ns1:DropoffType>REGULAR_PICKUP</ns1:DropoffType>
        <ns1:Shipper>
         <ns1:AccountNumber>11111111</ns1:AccountNumber>
         <ns1:Address>
          <ns1:StreetLines>24 Seaview Blvd</ns1:StreetLines>
          <ns1:City>Port Washington</ns1:City>
          <ns1:StateOrProvinceCode>NY</ns1:StateOrProvinceCode>
          <ns1:PostalCode>11050</ns1:PostalCode>
          <ns1:CountryCode>US</ns1:CountryCode>
         </ns1:Address>
        </ns1:Shipper>
        <ns1:Recipient>
         <ns1:Address>
          <ns1:StreetLines>1234 Fifth Street</ns1:StreetLines>
          <ns1:City>Sixton</ns1:City>
          <ns1:StateOrProvinceCode>AR</ns1:StateOrProvinceCode>
          <ns1:PostalCode>72712</ns1:PostalCode>
          <ns1:CountryCode>US</ns1:CountryCode>
         </ns1:Address>
        </ns1:Recipient>
        <ns1:ShippingChargesPayment>
         <ns1:Payor>
          <ns1:AccountNumber>11111111</ns1:AccountNumber>
          <ns1:CountryCode>US</ns1:CountryCode>
         </ns1:Payor>
        </ns1:ShippingChargesPayment>
        <ns1:RateRequestTypes>ACCOUNT</ns1:RateRequestTypes>
        <ns1:PackageCount>1</ns1:PackageCount>
        <ns1:PackageDetail>INDIVIDUAL_PACKAGES</ns1:PackageDetail>
        <ns1:RequestedPackageLineItems>
         <ns1:Weight>
          <ns1:Units>LB</ns1:Units>
          <ns1:Value>14</ns1:Value>
         </ns1:Weight>
         <ns1:Dimensions>
          <ns1:Length>20</ns1:Length>
          <ns1:Width>20</ns1:Width>
          <ns1:Height>9</ns1:Height>
          <ns1:Units>IN</ns1:Units>
         </ns1:Dimensions>
        </ns1:RequestedPackageLineItems>
       </ns1:RequestedShipment>
      </ns1:RateRequest>
     </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

I'm trying to get out values like the CountryCode, PostalCode Weight->Value and all of the Dimensions but the namespaces are confusing me. Any time I parse it, and look in the debugger all I see for any given variable is {SimpleXMLElement} [0] yet some will show output with ->asXML() but most of my attempts to access data either error out, return false, or return another {SimpleXMLElement} [0]. I just want the string/value for these nodes!

The following for example, outputs absolutely nothing:

    $fReq = simplexml_load_string($xmlRequest);
    $fReq->registerXPathNamespace('ns1', 'http://fedex.com/ws/rate/v9');
    $ns = $fReq->getNameSpaces(true);
    $rr = $fReq->children($ns['ns1']);
    echo $rr->asXML()."\n";

I prefer using XMLReader: http://us3.php.net/manual/en/class.xmlreader.php

This is a example form PHP with small modification for match your requirements:

function xmlParse($content,$wrapperName,$callback,$limit=NULL){
    $xml = new XMLReader();
    $xml->xml($content);

    $n=0;
    $x=0;
    while($xml->read()){
        if($xml->nodeType==XMLReader::ELEMENT && $xml->name == $wrapperName){
            while($xml->read() && $xml->name != $wrapperName){
                if($xml->nodeType==XMLReader::ELEMENT){
                    $name = $xml->name;
                    $xml->read();
                    $value = $xml->value;
                    if(preg_match("/[^\s]/",$value)){
                        $subarray[$name] = $value;
                    }
                }
            }
            if($limit==NULL || $x<$limit){
                if($callback($subarray)){
                    $x++;
                }
                unset($subarray);
            }
            $n++;
        }
    }
    $xml->close();
}

xmlParse($test,'SOAP-ENV:Envelope','callback');

function callback($array){
    var_dump($array);

}

Just execute this with you XML and will return

Array
(
    [ns1:Key] => aaaaaaaaaaa
    [ns1:Password] => aaaaaaaaaaaaaaaaaaa
    [ns1:AccountNumber] => 11111111
    [ns1:MeterNumber] => 88888888
    [ns1:CustomerTransactionId] => 1
    [ns1:ServiceId] => crs
    [ns1:Major] => 9
    [ns1:Intermediate] => 0
    [ns1:Minor] => 0
    [ns1:ReturnTransitAndCommit] => true
    [ns1:ShipTimestamp] => 2013-08-06T12:39:26-04:00
    [ns1:DropoffType] => REGULAR_PICKUP
    [ns1:StreetLines] => 1234 Fifth Street
    [ns1:City] => Sixton
    [ns1:StateOrProvinceCode] => AR
    [ns1:PostalCode] => 72712
    [ns1:CountryCode] => US
    [ns1:RateRequestTypes] => ACCOUNT
    [ns1:PackageCount] => 1
    [ns1:PackageDetail] => INDIVIDUAL_PACKAGES
    [ns1:Units] => IN
    [ns1:Value] => 14
    [ns1:Length] => 20
    [ns1:Width] => 20
    [ns1:Height] => 9
)

Regards

You don't need to call registerXPathNamespace unless you're using XPath. You do, however, need to traverse through the SOAP Body element, which means you need to think about two namespaces, not just one.

It's also a good idea not to rely on namespace prefixes like ns1 staying the same in the future; the part that's guaranteed is the actual URI in the xmlns attribute. One way to make this more readable is to define constants for the XML namespaces used in your application, and pass those to the ->children() and ->attributes() methods when necessary.

Note also that ->children() "selects" a namespace until further notice, so once you've selected the Fedex namespace and traversed to the RateRequest node, you can just access elements as though namespaces weren't an issue (since everything below that in this document is in that same namespace).

Here's a completed example showing how this might look ( see a live demo here ).

// Give your own aliases to namespaces, don't rely on the XML always having the same ones
define('NS_SOAP', 'http://schemas.xmlsoap.org/soap/envelope/');
define('NS_FEDEX_RATE', 'http://fedex.com/ws/rate/v9');

// Parse the XML
$sx = simplexml_load_string($xml_string);
// $sx now represents the <SOAP-ENV:Envelope> element

// Traverse through the SOAP Body
$sx_soap_body = $sx->children(NS_SOAP)->Body;

// Now "switch" to the Fedex namespace, and start with the RateRequest node
$sx_rate_request = $sx_soap_body->children(NS_FEDEX_RATE)->RateRequest;

// Now traverse as normal, e.g. to the Recipient's CountryCode
// (the string cast isn't necessary for echo, but is a good habit to get into)
echo (string)$sx_rate_request->RequestedShipment->Recipient->Address->CountryCode;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM