Ein paar Notizen zur Verwendung von Perls XML:LibXML möchte ich hier mal veröffentlichen, damit sie mir nicht mehr verloren gehen.

Schon etwas älter, aber trotzdem nützlich ist die LibXML Quick Reference Card von Andrew Ford.

Und hier noch ein paar Code-Schnipsel mit kurzen Erklärungen dazu.

Ein kleiner Test vorweg

Zuerst ein Test, ob alles richtig eingerichtet ist:

use XML::LibXML;
my $parser = XML::LibXML->new();
my $xmlfile = "./test.xml";
my $dom = $parser->parse_file( $xmlfile );

print $dom->toString();

Das sollte wieder das eingelesene xml-File ausgeben. Damit lässt sich schnell testen, ob alles richtig geladen wird und richtig eingelesen ist.

Beispiel mit einem xml-File

Ein Ausschnitt aus dem xml-File hilft vielleicht zu verstehen, wonach ich hier suche.

<component name="Server"><insttype name="install"><command name="win32">
            $INSTPATH\install.exe
        </command><command name="linux">
            $INSTPATH/install
        </command></insttype><insttype name="update"><command name="linux">
            $INSTPATH/update
        </command><command name="win32">
            $INSTPATH\update.exe
        </command></insttype></component>

Für verschiedene Komponenten sind je nach Installationstyp Kommandos im xml-File hinterlegt. Jetzt möchte ich in meinem Perl-Skript mit Hilfe von LibXML das Ganze parsen:

use XML::LibXML;
my $parser = XML::LibXML->new();
my $xmlfile = "./components.xml";
my $dom = $parser->parse_file( $xmlfile );

@cmd_list = ();

my $xpath = '/scenarios/scenario[@name='."'".$SCENARIO."'".']/installation';
foreach my $scen_node ( $scen_dom->findnodes( $xpath ) ){
    my $cfg_xpath = '/Component[@name='."'".$scen_node->findvalue( './component' )."'".']/InstType[@name='."'".$scen_node->findvalue( './type' )."'".']/Command[@name='."'".$os."'".']';
    foreach my $cmd ( $cfg_dom->findnodes( $cfg_xpath ) ){
        push( @cmd_list, { 'type' => $scen_node->findvalue( './type' ), 'component' => $scen_node->findvalue( './component' ), 'cmd' => $cmd->textContent() } );
    }
}

Um das foreach bin ich nicht rumgekommen, selbst wenn ich weiß, dass ich immer nur einen Treffer habe. Man bekommt eben ein Ergebnis vom Typ Nodelist und nicht vom Typ Node.

Validieren gegen ein dtd-File

Ein xml-File gegen ein dtd-File validieren. Gefunden habe ich das hier, vielen Dank dafür.

sub validate_xml_file{
    my $xml_file = shift;
    my $dtd_file = ( $xml_file =~ /(.+)\.xml/ ) ? "$1.dtd" : undef;;
    my $val = 0;

    if ( -e $dtd_file ) {
        my $dtd = XML::LibXML::Dtd->new( "", $dtd_file );
        my $xml = XML::LibXML->new;
        $xml->keep_blanks(0);
        my $tree = $xml->parse_file( $xml_file );
        $val = $tree->validate( $dtd );
    }
    return( $val );
}

Noch ein paar Links zu Seiten, die ich mal kurz überflogen habe und die so aussahen, als sollte ich das mal genauer lesen:

Über Hilfe wie man das alles evtl. etwas eleganter machen kann und Links zu verständlichen Tutorials oder Beispielen würde ich mich freuen.