Итерируя через XML с SimpleXML и XPath, результат находится в неправильном порядке

Мои переменные.

$xmlFile = 'xml.xml';
$xmlFileLoad = simplexml_load_file($xmlFile);
$xmlFileLoad->registerXPathNamespace("oi", "http://www.openimmo.de");
$immobilie = $xmlFileLoad->xpath('//oi:immobilie');

$zustand_angaben_letztemodernisierung = $xmlFileLoad->xpath('/oi:openimmo/oi:immobilie/oi:zustand_angaben/oi:letztemodernisierung');
$zustand_angaben_zustand = $xmlFileLoad->xpath('/oi:openimmo/oi:immobilie/oi:zustand_angaben/oi:zustand/@zustand_art');
$zustand_angaben_alter = $xmlFileLoad->xpath('/oi:openimmo/oi:immobilie/oi:zustand_angaben/oi:alter/@alter_attr');
$zustand_angaben_erschliessung = $xmlFileLoad->xpath('/oi:openimmo/oi:immobilie/oi:zustand_angaben/oi:erschliessung/@erschl_attr');
$zustand_angaben_altlasten = $xmlFileLoad->xpath('/oi:openimmo/oi:immobilie/oi:zustand_angaben/oi:altlasten');
$zustand_angaben_baujahr = $xmlFileLoad->xpath('/oi:openimmo/oi:immobilie/oi:zustand_angaben/oi:baujahr');

XML-файл (укороченная версия)

<?xml version="1.0" encoding="utf-8"?>
<openimmo xmlns="http://www.openimmo.de"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.openimmo.de openimmo.xsd">
    <immobilie>
        <zustand_angaben>
            <altlasten>keine</altlasten>
        </zustand_angaben>
    </immobilie>
    <immobilie>
        <zustand_angaben>
            <altlasten>keine</altlasten>
        </zustand_angaben>
    </immobilie>
    <immobilie>
        <zustand_angaben>
            <altlasten>keine</altlasten>
        </zustand_angaben>
    </immobilie>
    <immobilie>
        <zustand_angaben>
            <baujahr>ca. 1900</baujahr>
            <altlasten>nein</altlasten>
        </zustand_angaben>
    </immobilie>
    <immobilie>
        <zustand_angaben>
            <baujahr>ca. 1989</baujahr>
            <zustand zustand_art="ERSTBEZUG" />
            <alter alter_attr="ALTBAU" />
            <altlasten>keine</altlasten>
        </zustand_angaben>
    </immobilie>
</openimmo>

мой show.php

<?php
    include("variables.php");

    $i = 0;
    foreach ($immobilie as $immo) {

        echo "Immobilie $i<br />";
        if(isset($zustand_angaben_zustand[$i])) {
            echo $zustand_angaben_zustand[$i] . "</br>";
        }

        if(isset($zustand_angaben_alter[$i])) {
            echo $zustand_angaben_alter[$i] . "</br>";
        }

        if(isset($zustand_angaben_erschliessung[$i])) {
            echo $zustand_angaben_erschliessung[$i] . "</br>";
        }

        if(isset($zustand_angaben_altlasten[$i])) {
            echo $zustand_angaben_altlasten[$i] . "</br>";
        }

        if(isset($zustand_angaben_baujahr[$i])) {
            echo $zustand_angaben_baujahr[$i] . "</br>";
        }   
        echo "<br /><br />";
        $i++;
    }
?>

когда я выполняю код, результат будет следующим

Immobilie 0
ERSTBEZUG
ALTBAU
keine
5
A
5
A++
ca. 1900

Immobilie 1
keine

ca. 1989

Immobilie 2
keine

Immobilie 3
nein

Immobilie 4
keine

но я хочу его в том же порядке, что и в xml, поэтому в Immobilie 0 есть только keine, Immobilie 1 и 2, третий 1900 и nein и, наконец, Immobilie 4 со всеми свойствами в xml...

Что я делаю неправильно?

2 ответа

Решение

Используйте выражения XPath, относящиеся к текущему immobilie элемент вместо:

$i = 0;
foreach ($immobilie as $immo) {
    $immo->registerXPathNamespace("oi", "http://www.openimmo.de");
    $zustand_angaben_letztemodernisierung = $immo->xpath('./oi:zustand_angaben/oi:letztemodernisierung');
    $zustand_angaben_zustand = $immo->xpath('./oi:zustand_angaben/oi:zustand/@zustand_art');
    $zustand_angaben_alter = $immo->xpath('./oi:zustand_angaben/oi:alter/@alter_attr');
    $zustand_angaben_erschliessung = $immo->xpath('./oi:zustand_angaben/oi:erschliessung/@erschl_attr');
    $zustand_angaben_altlasten = $immo->xpath('./oi:zustand_angaben/oi:altlasten');
    $zustand_angaben_baujahr = $immo->xpath('./oi:zustand_angaben/oi:baujahr');

    echo "Immobilie $i<br />";
    if(isset($zustand_angaben_zustand[0])) {
        echo $zustand_angaben_zustand[0] . "</br>";
    }

    if(isset($zustand_angaben_alter[0])) {
        echo $zustand_angaben_alter[0] . "</br>";
    }

    if(isset($zustand_angaben_erschliessung[0])) {
        echo $zustand_angaben_erschliessung[0] . "</br>";
    }

    if(isset($zustand_angaben_altlasten[0])) {
        echo $zustand_angaben_altlasten[0] . "</br>";
    }

    if(isset($zustand_angaben_baujahr[0])) {
        echo $zustand_angaben_baujahr[0] . "</br>";
    }   
    echo "<br /><br />";
    $i++;
}

eval.in demo

Когда вы выполняете запросы xpath параллельно, все они располагаются рядом друг с другом.

Для вашего конкретного примера вам еще не нужен xpath, документ выглядит как идеальная работа для SimpleXMLElement, вы можете извлекать данные непосредственно из него.

Если вы просматриваете интересующие вас объекты (здесь, скорее всего, каждый элемент immobilie), а затем извлекаете данные (отфильтровываете неустановленные элементы), это может работать аналогично следующему примеру:

$xml = simplexml_load_string($string);

foreach ($xml->immobilie as $immobilie) {
    $angaben = $immobilie->zustand_angaben;

    $data = [
        'letzte_modernisierung' => $angaben->letztemodernisierung,
        'zustand_art'           => $angaben->zustand['zustand_art'],
        'alter'                 => $angaben->alter['alter_attr'],
        'erschliessung'         => $angaben->erschliessung['erschl_attr'],
        'altlasten'             => $angaben->altlasten,
        'baujahr'               => $angaben->baujahr,
    ];

    echo " * Immobilie:\n";
    foreach(array_filter($data) as $label => $wert) {
        echo "   - $label: $wert\n";
    }
}

Выход (также: онлайн-демо):

 * Immobilie:
   - altlasten: keine
 * Immobilie:
   - altlasten: keine
 * Immobilie:
   - altlasten: keine
 * Immobilie:
   - altlasten: nein
   - baujahr: ca. 1900
 * Immobilie:
   - zustand_art: ERSTBEZUG
   - alter: ALTBAU
   - altlasten: keine
   - baujahr: ca. 1989

Xpath совсем не плох, для такого сценария, как у вас, я бы порекомендовал вам использовать DOMDocument вместо SimpleXMLElement, поскольку он более гибок с Xpath благодаря методу оценки, который есть в DOMXpath.

В любом случае вы можете продолжить чтение этого вопроса, в котором показан один DOMDocument и одно решение на основе SimpleXMLElement для извлечения данных на основе Xpath из документов XML:

Другие вопросы по тегам