Perl XML::LibXML использование

У меня возникли проблемы с использованием XML::LibXML, и я хотел бы знать, есть ли способ сделать то, что я хочу, или мой XML должен быть изменен.

на данный момент мой XML выглядит так:

<fftg>
    <actions>

            <rename>
                    <mandatory>0</mandatory>
                    <other_things_here />
            </rename>

            <transfer>
                    <mandatory>0</mandatory>
                    <protocol>SFTP</protocol>
                    <other_things_here />
            </transfer>

            <transfer>
                    <mandatory>1</mandatory>
                    <protocol>FTP</protocol>
                    <other_things_here />
            </transfer>

            <archive>
                    <mandatory>1</mandatory>
                    <other_things_here />
            </archive>

            <rename>
                    <mandatory>1</mandatory>
                    <other_things_here />
            </rename>

    </actions>

</fftg>

как вы можете видеть, под "действиями" могут быть различные типы действий (1 или несколько действий каждого вида, с разными вещами под каждым действием)

Я хочу просмотреть каждое действие и выполнить определенные вещи в зависимости от действия.

Моя проблема: поскольку существует несколько действий одного и того же типа, скрипт не работает должным образом и перезаписывает предыдущее действие того же рода, или цикл для определенных действий повторяет все действия одного и того же вида

пример 1:

foreach my $transfer ($doc->findnodes('/fftg')) {

    # Browse Actions
    foreach my $action ($doc->findnodes('/fftg/actions/*')) {

            my $action_name = $action->nodeName();
            my $actiontype = ucfirst($action->nodeName());
            print "executing action $action_name ..\n";

            # Browse action details
            foreach my $action_details ($doc->findnodes('/fftg/actions/'.$action_name)) {
                    for my $detail ($action_details->findnodes('./*')) {
                            print $detail->nodeName(), ": ", $detail->textContent(), "\n";
                    }
            }
            print "-------------\n";
    }
}

результат 1:

executing action rename ..
mandatory: 0
other_things_here:
mandatory: 1
other_things_here:
-------------
executing action transfer ..
mandatory: 0
protocol: SFTP
other_things_here:
mandatory: 1
protocol: FTP
other_things_here:
-------------
executing action transfer ..
mandatory: 0
protocol: SFTP
other_things_here:
mandatory: 1
protocol: FTP
other_things_here:
-------------
executing action archive ..
mandatory: 1
other_things_here:
-------------
executing action rename ..
mandatory: 0
other_things_here:
mandatory: 1
other_things_here:
-------------

как видите, этот результат неверен, так как в каждом "типе действия" (который хорошо обнаружен) он повторяется для каждого действия того же рода.

это связано с:

foreach my $action_details ($doc->findnodes('/fftg/actions/'.$action_name)) {

как я могу сделать то, чего я хочу достичь? (если возможно без изменения XML)

что я хочу это:

executing action rename ..
mandatory: 0
other_things_here:
-------------
executing action transfer ..
mandatory: 0
protocol: SFTP
other_things_here:
-------------
executing action transfer ..
mandatory: 1
protocol: FTP
other_things_here:
-------------
executing action archive ..
mandatory: 1
other_things_here:
-------------
executing action rename ..
mandatory: 1
other_things_here:
-------------

благодарю вас

В дополнение к этому, я полагаю, что я мог бы изменить свой XML и использовать что-то подобное вместо этого, чтобы легко это исправить (но я не знаю, какой из них является лучшим вариантом из трех), но я хотел бы избежать этих двух ниже потому что XSD будет сложно производить впоследствии (я хочу конкретные параметры для каждого типа действия):

<fftg>
<actions>
        <action>
                <type>rename</type>
                <mandatory>0</mandatory>
                <other_things_here />
        </action>

        <action>
                <type>transfer</type>
                <mandatory>0</mandatory>
                <protocol>SFTP</protocol>
                <other_things_here />
        <action>

        <action>
                <type>transfer</type>
                <mandatory>0</mandatory>
                <protocol>FTP</protocol>
                <other_things_here />
        <action>

        <action>
                <type>archive</type>
                <mandatory>0</mandatory>
                <other_things_here />
        <action>

        <action>
                <type>rename</type>
                <mandatory>0</mandatory>
                <other_things_here />
        <action>



</actions>

</fftg>

или же

<fftg>
<actions>
        <action type="rename">
                <mandatory>0</mandatory>
                <other_things_here />
        </action>

        <action type="transfer">
                <mandatory>0</mandatory>
                <protocol>SFTP</protocol>
                <other_things_here />
        <action>

        <action type="transfer">
                <mandatory>0</mandatory>
                <protocol>FTP</protocol>
                <other_things_here />
        <action>

        <action type="archive">
                <mandatory>0</mandatory>
                <other_things_here />
        <action>

        <action type="rename">
                <mandatory>0</mandatory>
                <other_things_here />
        <action>



</actions>

</fftg>

еще раз спасибо с уважением,

1 ответ

Решение

Сначала вы вызываете findnodes на $doc, что означает, что ваше выражение XPath оценивается по отношению к корневому узлу документа. Во-вторых, выражение XPath, которое вы используете, начинается с '/', так что снова укореняется в узле верхнего уровня. Так что при звонке

$doc->findnodes('/fftg/actions/rename')

это значит "дай мне каждый <rename/> элемент, который является потомком <actions/> элемент, который является потомком <fftg/> элемент, который является корневым узлом документа ", и, в результате, вы получаете каждый <rename/> узел, который находится в документе.

Более того, петель слишком много. Первый является избыточным, так как может быть только один корневой элемент. Два цикла должны работать (один на <action/> дети, и еще один на своих детей)

for my $action ($doc->findnodes('/fftg/actions/*')) {
    print "executing ", $action->nodeName(), "\n";
    for my $detail ($action->findnodes('./*')) {
        print $detail->nodeName(), ": ", $detail->textContent(), "\n";
    }
}
Другие вопросы по тегам