iterating SimpleXml xpath result

两盒软妹~` 提交于 2019-12-10 11:25:16

问题


I'm using SimpleXMLElement to read xml returned from a remote server. The results are then parsed with xpath like this:

$result = <<<XML
<DataImport2Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.xxx.xxx/Services/DataImport2"> 
    <Number /> 
    <Blocks> 
        <Block>
            <Code>Fbf</Code> 
            <Fields> 
                <Field> 
                    <Code>FinnsIFbf</Code> 
                    <Value>1</Value> 
                </Field>
            </Fields>
        </Block>
    </Blocks> 
</DataImport2Result>
XML;

$xml = new SimpleXMLElement($result);
$xml->registerXPathNamespace("data", array_pop($xml->getNamespaces()));

foreach($xml->xpath("//data:Code[.='Fbf']/..") as $block) {
    foreach($block[0]->Fields->Field as $field) {
        echo "Code: ". $field->Code ."\n"; // SHould return FinnsIFbf
    }
}

The $result is an Array with SimpleXMLElement objects. The actual error occurs when trying to use $block[0]->Fields(SimpleXMLElement object) as an array. So the results are there. It's iterating that is the problem.
This works just fine in PHP 5.3.2, but on the server which is running 5.1.6 it fails with:
Fatal error: Objects used as arrays in post/pre increment/decrement must return values by reference

What's the simplest way to fix this without upgrading the server version (server admin thinks its "unstable" with a newer version)?

One solution could be if I could make the xpath return the Field in the first $result, so I wont have to iterate the $block[0]->Fields->Field but I failed to make such an xpath expression.


回答1:


As was mentionned in Sjoerd's answer, $block is not an array. SimpleXMLElement::xpath() returns an array of objects, each of those objects representing one single element. So basically, you have to replace $block[0] with $block since it already represents the block you're looking for.

Also, I have rewritten your XPath expression. Since you're looking for a <data:Block/> element, that's what you should target. The thing about <data:Code/> is a predicate, so it should go within brackets. Of course in your case the result is the same, but it's good practice to have semantically correct expressions, helps having a clearer idea of what's going on later on when you re-read that code (or if someone else has to maintain it.)

foreach ($xml->xpath('//data:Block[data:Code="Fbf"]') as $block) {
    foreach ($block->Fields->Field as $field) {
        echo "Code: ". $field->Code ."\n"; // SHould return FinnsIFbf
    }
}

Update

I didn't notice that you said that all you were interested in was the <Field/> element. In that case you can get it directly through XPath: (remember that they're all in the data namespace)

foreach ($xml->xpath('//data:Block[data:Code="Fbf"]/data:Fields/data:Field') as $field) {
    echo "Code: ". $field->Code ."\n"; // SHould return FinnsIFbf
}



回答2:


It is probably because $block is not an array. You should use var_dump or print_r to confirm that, and then use is_array in your code to make sure it is an array.

If SimpleXML finds multiple elements with the same name, it returns an array of objects. If it finds a single element, it returns just that element, not an array with a single element. So when you expect multiple elements, you always have to code an exception case for when there is only one element.



来源:https://stackoverflow.com/questions/3108721/iterating-simplexml-xpath-result

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!