perlvroom has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to get an attribute from a certain part of an XML based on another attribute in the element above.
<?xml version="1.0" encoding="utf-8"?> <Month> <Week> <Day day-of-week="Monday"> <Times> <Time start-time="01:00"/> </Times> </Day> <Day day-of-week="Tuesday"> <Times> <Time start-time="02:00"/> </Times> </Day> <Day day-of-week="Wednesday"> <Times> <Time start-time="03:00"/> </Times> </Day> <Day day-of-week="Thursday"> <Times> <Time start-time="04:00"/> </Times> </Day> <Day day-of-week="Friday"> <Times> <Time start-time="05:00"/> </Times> </Day> <Day day-of-week="Sunday"> <Times> <Time start-time="06:00"/> </Times> </Day> </Week> </Month>
Here is the code i have so far:
use XML::Twig; use strict; use warnings; my $twig=XML::Twig->new(); $twig->parsefile( 'CC.xml'); my $data = $twig->first_elt( 'Day' ); my $times = $twig->first_elt( 'Time' ); my $day = $data->att( 'day-of-week' ); my $time = $times->att('start-time'); if ($day='Tuesday') { print $time; }
I basically want to be able to get the "start-time" attribute for whichever day i specify.

Replies are listed 'Best First'.
Re: Extracting XML data from a tree
by toolic (Bishop) on Jun 04, 2013 at 13:58 UTC
    You could use a handler:
    use XML::Twig; use strict; use warnings; my $twig = XML::Twig->new(twig_handlers => { Day => \&Day }); $twig->parsefile('CC.xml'); exit; sub Day { my ( $twig, $day ) = @_; if ($day->att('day-of-week') eq 'Tuesday') { print $day->first_child('Times')->first_child('Time')->att('st +art-time'), "\n"; } } __END__ 02:00
      Thanks! This did exactly what i needed.
Re: Extracting XML data from a tree
by Neighbour (Friar) on Jun 04, 2013 at 14:22 UTC
    The handler toolic describes is capable of working with large files.
    Alternatively, you could use xpath:
    #!/usr/bin/perl use strict; use warnings; use XML::Twig; use v5.10; my $twig = XML::Twig->new(); $twig->parsefile( 'CC.xml'); my $looking_for_day = 'Tuesday'; foreach my $node ($twig->get_xpath('//Day[@day_of_week="' . $looking_f +or_day . '"]/Times/Time')) { say $node->att('start-time'); }
      One small correction: change day_of_week to day-of-week:
      foreach my $node ($twig->get_xpath('//Day[@day-of-week="' . $looking_f +or_day . '"]/Times/Time')) {
        Whoops, yes...I had a bit of trial and error getting it to work, and changed the -'s to _'s, but forgot to change them back when posting the code.
        Thanks for the correction.
Re: Extracting XML data from a tree
by hdb (Monsignor) on Jun 04, 2013 at 14:43 UTC

    Not Twig but Simple:

    use strict; use warnings; use XML::Simple; my $xml = XMLin( 'CC.xml' ); for (@{ $xml->{Week}{Day} }) { print $_->{Times}{Time}{'start-time'},"\n" if $_->{'day-of-week'} eq + "Tuesday"; }

      See Simpler than XML::Simple.

      Your code would break if there were multiple <Time> tag in one <Times>. Here's one of the ways to do this with XML::Rules. It handles the multiple <Time> tags and stops processing the XML once it finds the requested day.

      use strict; use XML::Rules; use Data::Dumper; my $parser = XML::Rules->new( strispaces => 7, rules => { Time => 'as array', Times => 'pass', 'Day' => sub { my ($tag_name, $attrs, $context, $parent_data, $parser) = +@_; if ($attrs->{'day-of-week'} eq $parser->{parameters}) { $parser->return_this(join(', ', grep( defined($_), map + $_->{'start-time'}, @{$attrs->{Time}}))); } }, Month => sub{} } ); my $time = $parser->parse(\*DATA, 'MondayX'); print "Time: $time\n"; __DATA__ <?xml version="1.0" encoding="utf-8"?> <Month> <Week>

      Jenda
      Enoch was right!
      Enjoy the last years of Rome.

        Agreed. The story does not look as favorable for XML::Simple anymore if the structure changes. If one makes it more robust using ForceArray => 1, then at least one more loop is required:

        my $xml = XMLin( 'CC.xml', ( ForceArray => 1 ) ); for my $day (@{ $xml->{Week}[0]{Day} }) { next unless $day->{'day-of-week'} eq "Tuesday"; for my $time ( @{ $day->{Times}[0]{Time} } ) { print $time->{'start-time'},"\n" if exists $time->{'start-time'}; } }

        Not really so "simple" anymore...

Re: Extracting XML data from a tree
by choroba (Cardinal) on Jun 05, 2013 at 08:34 UTC
    Using XML::XSH2, a wrapper around XML::LibXML:
    open CC.xml ; echo //Day[@day-of-week="Tuesday"]/Times/Time/@start-time ;
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ