How to parse a SOAP XML response with Namespaces using PHP and SimpleXML
Asked Answered
P

2

1

I'm using CURL to send a SOAP request to Mondrian. This is the PHP code where CURL is being used:

$poststring =
    '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-        ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Header />
    <SOAP-ENV:Body>
    <Execute xmlns="urn:schemas-microsoft-com:xml-analysis">
    <Command>
    <Statement>
    select {[Measures].[Unit Sales]} on columns from Sales
    </Statement>
    </Command>
    <Properties>
    <PropertyList>
    <Catalog>FoodMart</Catalog>
    <DataSourceInfo>Provider=Mondrian;DataSource=MondrianFoodMart;</DataSourceInfo>
    <Format>Multidimensional</Format>
    <AxisFormat>TupleFormat</AxisFormat>
    </PropertyList>
    </Properties>
    </Execute>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://localhost:8080/mondrian/xmla');
curl_setopt($ch, CURLOPT_POSTFIELDS, $poststring);
curl_setopt_array($ch, $this->_curlOptions);        
$_rawResult = curl_exec($ch);

curl_close($ch);

The received SOAP response I get from Mondrian is:

<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP- ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" >
<SOAP-ENV:Header>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<cxmla:ExecuteResponse xmlns:cxmla="urn:schemas-microsoft-com:xml-analysis">
<cxmla:return>
<root xmlns="urn:schemas-microsoft-com:xml-analysis:mddataset"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:EX="urn:schemas-microsoft-com:xml-analysis:exception">
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:schemas-microsoft-com:xml-analysis:mddataset" xmlns="urn:schemas-microsoft-com:xml-analysis:mddataset" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sql="urn:schemas-microsoft-com:xml-sql" elementFormDefault="qualified">
<xsd:complexType name="MemberType">
<xsd:sequence>
<xsd:element name="UName" type="xsd:string"/>
<xsd:element name="Caption" type="xsd:string"/>
<xsd:element name="LName" type="xsd:string"/>
<xsd:element name="LNum" type="xsd:unsignedInt"/>
<xsd:element name="DisplayInfo" type="xsd:unsignedInt"/>
        <xsd:sequence maxOccurs="unbounded" minOccurs="0">
          <xsd:any processContents="lax" maxOccurs="unbounded"/>
        </xsd:sequence>
      </xsd:sequence>
      <xsd:attribute name="Hierarchy" type="xsd:string"/>
    </xsd:complexType>
    <xsd:complexType name="PropType">
      <xsd:attribute name="name" type="xsd:string"/>
    </xsd:complexType>
    <xsd:complexType name="TupleType">
      <xsd:sequence maxOccurs="unbounded">
        <xsd:element name="Member" type="MemberType"/>
      </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="MembersType">
      <xsd:sequence maxOccurs="unbounded">
        <xsd:element name="Member" type="MemberType"/>
      </xsd:sequence>
      <xsd:attribute name="Hierarchy" type="xsd:string"/>
    </xsd:complexType>
    <xsd:complexType name="TuplesType">
      <xsd:sequence maxOccurs="unbounded">
        <xsd:element name="Tuple" type="TupleType"/>
      </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="CrossProductType">
      <xsd:sequence>
        <xsd:choice minOccurs="0" maxOccurs="unbounded">
          <xsd:element name="Members" type="MembersType"/>
          <xsd:element name="Tuples" type="TuplesType"/>
        </xsd:choice>
      </xsd:sequence>
      <xsd:attribute name="Size" type="xsd:unsignedInt"/>
    </xsd:complexType>
    <xsd:complexType name="OlapInfo">
      <xsd:sequence>
        <xsd:element name="CubeInfo">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="Cube" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="CubeName" type="xsd:string"/>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="AxesInfo">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="AxisInfo" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="HierarchyInfo" minOccurs="0" maxOccurs="unbounded">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:sequence maxOccurs="unbounded">
                            <xsd:element name="UName" type="PropType"/>
                            <xsd:element name="Caption" type="PropType"/>
                            <xsd:element name="LName" type="PropType"/>
                            <xsd:element name="LNum" type="PropType"/>
                            <xsd:element name="DisplayInfo" type="PropType" minOccurs="0" maxOccurs="unbounded"/>
                          </xsd:sequence>
                          <xsd:sequence>
                            <xsd:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
                          </xsd:sequence>
                        </xsd:sequence>
                        <xsd:attribute name="name" type="xsd:string" use="required"/>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                  <xsd:attribute name="name" type="xsd:string"/>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="CellInfo">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:sequence minOccurs="0" maxOccurs="unbounded">
                <xsd:choice>
                  <xsd:element name="Value" type="PropType"/>
                  <xsd:element name="FmtValue" type="PropType"/>
                  <xsd:element name="BackColor" type="PropType"/>
                  <xsd:element name="ForeColor" type="PropType"/>
                  <xsd:element name="FontName" type="PropType"/>
                  <xsd:element name="FontSize" type="PropType"/>
                  <xsd:element name="FontFlags" type="PropType"/>
                  <xsd:element name="FormatString" type="PropType"/>
                  <xsd:element name="NonEmptyBehavior" type="PropType"/>
                  <xsd:element name="SolveOrder" type="PropType"/>
                  <xsd:element name="Updateable" type="PropType"/>
                  <xsd:element name="Visible" type="PropType"/>
                  <xsd:element name="Expression" type="PropType"/>
                </xsd:choice>
              </xsd:sequence>
              <xsd:sequence maxOccurs="unbounded" minOccurs="0">
                <xsd:any processContents="lax" maxOccurs="unbounded"/>
              </xsd:sequence>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="Axes">
      <xsd:sequence maxOccurs="unbounded">
        <xsd:element name="Axis">
          <xsd:complexType>
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
              <xsd:element name="CrossProduct" type="CrossProductType"/>
              <xsd:element name="Tuples" type="TuplesType"/>
              <xsd:element name="Members" type="MembersType"/>
            </xsd:choice>
            <xsd:attribute name="name" type="xsd:string"/>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="CellData">
      <xsd:sequence>
        <xsd:element name="Cell" minOccurs="0" maxOccurs="unbounded">
          <xsd:complexType>
            <xsd:sequence maxOccurs="unbounded">
              <xsd:choice>
                <xsd:element name="Value"/>
                <xsd:element name="FmtValue" type="xsd:string"/>
                <xsd:element name="BackColor" type="xsd:unsignedInt"/>
                <xsd:element name="ForeColor" type="xsd:unsignedInt"/>
                <xsd:element name="FontName" type="xsd:string"/>
                <xsd:element name="FontSize" type="xsd:unsignedShort"/>
                <xsd:element name="FontFlags" type="xsd:unsignedInt"/>
                <xsd:element name="FormatString" type="xsd:string"/>
                <xsd:element name="NonEmptyBehavior" type="xsd:unsignedShort"/>
                <xsd:element name="SolveOrder" type="xsd:unsignedInt"/>
                <xsd:element name="Updateable" type="xsd:unsignedInt"/>
                <xsd:element name="Visible" type="xsd:unsignedInt"/>
                <xsd:element name="Expression" type="xsd:string"/>
              </xsd:choice>
            </xsd:sequence>
            <xsd:attribute name="CellOrdinal" type="xsd:unsignedInt" use="required"/>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
    <xsd:element name="root">
      <xsd:complexType>
        <xsd:sequence maxOccurs="unbounded">
          <xsd:element name="OlapInfo" type="OlapInfo"/>
          <xsd:element name="Axes" type="Axes"/>
          <xsd:element name="CellData" type="CellData"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <OlapInfo>
    <CubeInfo>
      <Cube>
        <CubeName>Sales</CubeName>
      </Cube>
    </CubeInfo>
    <AxesInfo>
      <AxisInfo name="Axis0">
        <HierarchyInfo name="Measures">
          <UName name="[Measures].[MEMBER_UNIQUE_NAME]"/>
          <Caption name="[Measures].[MEMBER_CAPTION]"/>
          <LName name="[Measures].[LEVEL_UNIQUE_NAME]"/>
          <LNum name="[Measures].[LEVEL_NUMBER]"/>
          <DisplayInfo name="[Measures].[DISPLAY_INFO]"/>
        </HierarchyInfo>
      </AxisInfo>
    </AxesInfo>
    <CellInfo>
      <Value name="VALUE"/>
      <FmtValue name="FORMATTED_VALUE"/>
      <FormatString name="FORMAT_STRING"/>
    </CellInfo>
  </OlapInfo>
  <Axes>
    <Axis name="Axis0">
      <Tuples>
        <Tuple>
          <Member Hierarchy="Measures">
            <UName>[Measures].[Unit Sales]</UName>
            <Caption>Unit Sales</Caption>
            <LName>[Measures].[MeasuresLevel]</LName>
            <LNum>0</LNum>
            <DisplayInfo>0</DisplayInfo>
          </Member>
        </Tuple>
      </Tuples>
    </Axis>
  </Axes>
  <CellData>
    <Cell CellOrdinal="0">
      <Value xsi:type="xsd:double">266773</Value>
      <FmtValue>266,773</FmtValue>
      <FormatString>Standard</FormatString>
    </Cell>
  </CellData>
</root>
</cxmla:return>
</cxmla:ExecuteResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

My objective is to extract all the Cell elements that come inside the CellData element. Although this response comes with only one Cell, it is possible that further answers come with more than one. I was thinking of using XPath, but I may be missing some detail related to Namespace registering:

$xml = simplexml_load_string($_rawResponse);
$xml->registerXPathNamespace('xsi', 'http://www.w3.org/2001/XMLSchema-instance');
$xml->registerXPathNamespace('cxmla', 'urn:schemas-microsoft-com:xml-analysis');
$_res = $xml->xpath('//CellData/Cell');
var_dump($_res);

The result of var_dump is:

array(0) {
}

instead of something like:

array(1) {
  [0]=>
  object(SimpleXMLElement)#2 (4) {
    ["@attributes"]=>
    array(1) {
      ["CellOrdinal"]=>
      string(1) "0"
    }
    ["Value"]=>
    string(4) "266773"
    ["FmtValue"]=>
    string(5) "266,773"
    ["FormatString"]=>
    string(8) "Standard"
  }
}

What am I doing wrong? Could you point me in the right direction ? Thank you in advance.

Pickpocket answered 15/12, 2011 at 18:43 Comment(3)
If you dump the entire parsed XML structure, what do you get? Do you see the Cell elements in there? If so, it could be a problem with your xpath; if not, then it's a problem with parsing the data.Bibliolatry
Have you considered using a proper soap libary instead? SOAP XML was never intended to be created or parsed directly--an abstraction layer is supposed to provide you with something in a native datatype automatically. If there is a WSDL your job gets even easer....Interpleader
Hi, Francis. There is no WSDL and I've also tried using SoapClient in non-WSDL mode, but the problem would be the same, since the __doRequest method would return the same XML string.Pickpocket
I
2

This is a namespace issue.

The <root> element, which is the parent of the <CellData> element, has a default namespace definition xmlns="urn:schemas-microsoft-com:xml-analysis:mddataset" so <CellData> is in that namespace (like all its descendants also happen to be). Therefore you need to register this namespace and give it some prefix and then use that prefix in your XPath steps.

$xml->registerXPathNamespace('md', 'urn:schemas-microsoft-com:xml-analysis:mddataset');
$_res = $xml->xpath('//md:CellData/md:Cell');

XPath queries always search for an element that is not in any namespace if the given element name is not prefixed.

Irretentive answered 15/12, 2011 at 23:59 Comment(1)
Thank you, jasso, it was indeed a namespace issue.Pickpocket
R
-2

Using DOMDocument you can get all values in xml. Try with DOMDocument Documentation Link: http://php.net/manual/en/class.domdocument.php

Repatriate answered 15/12, 2011 at 18:52 Comment(1)
Thank you for your answer. Unfortunately, I'm not having much luck with that solution either: $domdoc = new DOMDocument(); $domdoc->loadXML($_res); $xpath = new DOMXPath($domdoc); $xpath->registerNamespace('xsi', 'w3.org/2001/XMLSchema-instance'); $xpath->registerNamespace('cxmla', 'urn:schemas-microsoft-com:xml-analysis'); $res = $xpath->query('//CellData/Cell'); printf($res->length . PHP_EOL); The result is 0, when it should be 1. I tried parsing a similar XML string without the SOAP and Namespaces stuff and worked fine.Pickpocket

© 2022 - 2024 — McMap. All rights reserved.