Рекурсивная функция php для анализа вложенных тегов xml

Я пытаюсь разобрать эту XML-структуру, но не могу найти способ разобрать «n» -глубокие вложенные теги с помощью рекурсии. Структура xml:

<plist version="1.0">
    <key>1.1.1</key>
    <dict>
        <key>nag</key>
        <integer>1</integer>
    </dict>

    <key>2.2.2</key>
    <dict>
        <key>nag</key>
        <integer>1</integer>
    </dict>

    <key>3.3.3</key>
    <dict>
        <key>show_upgrade_button</key>
        <integer>0</integer>

        <key>nag_startup</key>
        <dict>
            <key>nag_gameover</key>
            <integer>3</integer>
        </dict>

        <key>my_stuff</key>
        <string>1=cb 2=rm 3=cb+rm =leave banner ads off</string>

    </dict>

    <key>4.4.4</key>
    <dict>
        <key>nag</key>
        <integer>1</integer>
    </dict>
</plist>

Узлы сопоставляются key - dict, являясь ключевым узлом, номер версии для данных внутри узла dict, но структура xml имеет произвольную dict вложенность, как вы можете видеть в приведенном выше коде. У меня есть рекурсивная функция, которая пока принимает узел dict, но я не вижу света.

<? php
function recursiveNodes($nodes, $arr){
        $count=0;
        if($nodes->hasChildNodes() === true  ){ 

            foreach($nodes->childNodes as $node){

                $temp = array();
                if($node->nodeName === 'key'){

                    $temp['key_name'] = $node->nodeValue;
                    if($node->nextSibling->nodeName !== 'dict'){
                        $sibling = $node->nextSibling;                      
                        $temp['type_name'] = $sibling ->nodeName;
                        $temp['value_name'] = $sibling ->nodeValue;
                    }
                    if($sibling->nodeName === 'dict'){
                    return recursiveNodes($sibling, $arr[$count++][]=$temp);
                }   
                }

            }

        }
            return $arr;
    }
    ?>

person Diego Romero    schedule 05.07.2013    source источник
comment
Что вы пытаетесь сделать с этим XML? Непонятно, что вы на самом деле пытаетесь найти.   -  person jcsanyi    schedule 05.07.2013
comment
Вы пытаетесь превратить эту структуру в массив? Вы пытаетесь представить это в рекурсивной структуре данных массива?   -  person nickb    schedule 05.07.2013
comment
Извините, если неясно, я пытаюсь получить key nodeValue и следующие key данные родного брата в виде массива, который мне нужно вставить в базу данных. Скажем, первая пара ‹key› ‹dict›, <key>1.1.1</key> <dict> <key>nag</key> <integer>1</integer> </dict> из узла ‹key›, который я получил 1.1.1, из узла ‹dict› - массив со следующими данными его дочерних узлов key_name = nag, type_name = integer и type_value = 1.   -  person Diego Romero    schedule 05.07.2013
comment
@nickb да, проблема в том, что я не знаю, сколько вложенных dicts у него будет. И это может быть пара key-dict внутри тега dict, в котором будет больше вложенных тегов, или пара key-anytag, у которой нет дополнительных дочерних узлов.   -  person Diego Romero    schedule 05.07.2013
comment
Если рекурсия вызывает у вас проблемы с XML-документом, посмотрите xpath, который обычно уже может вернуть вам, например, список. Например. вы можете запросить все <dict> элементы, у которых есть <key> в качестве родственных.   -  person hakre    schedule 05.07.2013


Ответы (1)


Рекурсия в вашей функции нарушена. Возможно, было бы проще превратить его в объект, а не в отдельную функцию.

Это также позволило бы более легко расширять его по мере необходимости.

См. Следующий пример использования:

$parser = new PlistXMLParser();
$parser->loadXML($xml);
print_r($parser->parse());

С вашим примерным вводом он дает следующее:

Array
(
    [1.1.1] => Array
        (
            [nag] => 1
        )

    [2.2.2] => Array
        (
            [nag] => 1
        )

    [3.3.3] => Array
        (
            [show_upgrade_button] => 0
            [nag_startup] => Array
                (
                    [nag_gameover] => 3
                )

            [my_stuff] => 1=cb 2=rm 3=cb+rm =leave banner ads off
        )

    [4.4.4] => Array
        (
            [nag] => 1
        )

)

Внутренне это работает в основном так же, как и вы, см. Здесь отрывок из источников:

...
switch ($type) {
    case self::NODE_INTEGER:
        $result[$keyString] = sprintf('%0.0f', trim($value));
        break;

    case self::NODE_STRING:
        $result[$keyString] = (string)$value;
        break;

    case self::NODE_DICT:
        $parser = new self();
        $parser->loadSimpleXMLElement($value);
        $result[$keyString] = $parser->parse();
        break;

    default:
        throw new UnexpectedValueException(sprintf('Unexpected type "%s" for key "%s"', $type, $key));
}
...

Синтаксический анализатор использует конструкцию switch, чтобы лучше работать с отдельными маркерами типа, которые появляются в структуре XML. Исключение выделяет те функции, которые вы еще не реализовали, и рекурсия запускается для NODE_DICT, который просто создает новый синтаксический анализатор и позволяет ему выполнять свою работу. Очень простая форма рекурсии.

person hakre    schedule 05.07.2013
comment
Большое спасибо, похоже, он должен работать, при попытке запустить код он дает мне ошибку операнда list($value) = $key->xpath('following-sibling::*[1]') + [NULL]; здесь, строка 33, также это будет работать с xml с обычным заголовком <?xml version="1.0" encoding="UTF-8" .... еще раз спасибо! простой и мощный код - person Diego Romero; 05.07.2013
comment
Код написан на самой старой текущей версии PHP, срок эксплуатации которой не истек: 5.4. Пожалуйста, обновите PHP до версии 5.4 или выполните резервное копирование кода ([NULL] - ›array(NULL)). И да, все XML, поддерживаемое SimpleXMLElement, поддерживается им. В этом случае это сравнимо с DOMDocument. - person hakre; 05.07.2013