Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

getting ancestors of element

by jccunning (Acolyte)
on Aug 30, 2012 at 18:50 UTC ( #990820=perlquestion: print w/ replies, xml ) Need Help??
jccunning has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to get list of ancestors for an element using xml-twig. I have array containing a string in each element like shown in string variable as follows.
#!/usr/bin/perl use strict; use warnings; use XML::Twig; my $string = "\n <files name=\"Common.h\">\n <enums>\n <membe +rs kind=\"enum\" name=\"PermissionLevel\" protection=\"public\" stati +c=\"no\" virtualness=\"non_virtual\">\n <values initializer=\" + 99\" name=\"PERMISSION_PUBLIC\"></values>\n <values initializ +er=\" 98\" name=\"PERMISSION_NDA\"></values>\n </members>\n + <members kind=\"enum\" name=\"RegisterType\" protection=\"public\" s +tatic=\"no\" virtualness=\"non_virtual\">\n <values initialize +r=\" 4\" name=\"REGISTER_PCI\"></values>\n </members>\n </enu +ms>\n <functions>\n <members const=\"no\" kind=\"function\" n +ame=\"Compare\" protection=\"public\" static=\"no\" type=\"bool\" vir +tualness=\"non_virtual\" volatile=\"no\">\n <parameters declar +ation_name=\"first\" type=\"T\"/>\n <parameters declaration_na +me=\"second\" type=\"T\"/>\n </members>\n </functions>\n </f +iles>"; print $string; my $twig = XML::Twig->new; $twig->parse( $string ); my @names = $twig->ancestors('values'); print @names;
For example, for values element I would like to get list that tells me values element is in members element which is in enums element, etc. I get error cannot locate object method ancestors.

Comment on getting ancestors of element
Download Code
Re: getting ancestors of element
by philiprbrenan (Monk) on Aug 30, 2012 at 19:23 UTC

    The following calls a user supplied routine with a list of ancestors of a node when it encounters a node with the right name in the XML tree.

    use feature ":5.14"; use warnings FATAL => qw(all); use strict; use Data::Dump qw(dump pp); use XML::Simple; my $x = XMLin(<<'END'); <files name="Common.h"> <enums> <members kind="enum" name="PermissionLevel" protection="public" +static="no" virtualness="non_virtual"> <values initializer=" 99" name="PERMISSION_PUBLIC"></values> <values initializer=" 98" name="PERMISSION_NDA"></values> </members> <members kind="enum" name="RegisterType" protection="public" sta +tic="no" virtualness="non_virtual"> <values initializer=" 4" name="REGISTER_PCI"></values> </members> </enums> <functions> <members const="no" kind="function" name="Compare" protection="p +ublic" static="no" type="bool" virtualness="non_virtual" volatile="no +"> <parameters declaration_name="first" type="T"/> <parameters declaration_name="second" type="T"/> </members> </functions> </files> END pp($x); sub r($$$;$); sub r($$$;$) {my ($r, $l, $e, $a) = @_; $a = [] unless $a; return unless $l and ref($l); if (ref($l) =~ /HASH/) {for(sort keys %$l) {unless (/$e/) {push @$a, $_; r($r, $l->{$_}, $e, $a); pop @$a; } else {&$r(@$a); } } } elsif (ref($l) =~ /ARRAY/) {for(1..@$l) {unless ($l->[$_-1] =~ /$e/) {push @$a, $_; r($r, $l->[$_-1], $e, $a); pop @$a; } else {&$r(@$a); } } } } r sub {say "@_"}, $x, "values";

    Produces

    enums members PermissionLevel
    enums members RegisterType
    
      Thanks for reply. Maybe you can tell me what arguments are implied in sub r($$$;$) Not familiar with that string of special variables. Thanks

        See perlsub. $ means a scalar value, and

        A semicolon (;) separates mandatory arguments from optional arguments.

        So, sub r($$$;$) means: r is a subroutine prototyped to take at least 3, and at most 4, scalar arguments.

        Hope that helps,

        Athanasius <°(((><contra mundum

      Does not appear to work on different string, for example if $x is equal to following and search for "parameters":
      <classes name="Panoply::AttributeDesc"> <all_members name="AttributeDesc" protection="public" scope="Panop +ly::AttributeDesc" virtualness="non_virtual"/> <all_members name="AttributeDesc" protection="public" scope="Panop +ly::AttributeDesc" virtualness="non_virtual"/> <all_members name="description" protection="public" scope="Panoply +::AttributeDesc" virtualness="non_virtual"/> <all_members name="name" protection="public" scope="Panoply::Attri +buteDesc" virtualness="non_virtual"/> <all_members name="value" protection="private" scope="Panoply::Att +ributeDesc" virtualness="non_virtual"/> <public_members> <members kind="variable" name="name" protection="public" static= +"no" type="std::string" virtualness="non_virtual"></members> <members kind="variable" name="value" protection="public" static +="no" type="std::string" virtualness="non_virtual"></members> <members kind="variable" name="description" protection="public" +static="no" type="std::string" virtualness="non_virtual"></members> </public_members> <public_methods> <members const="no" kind="function" name="AttributeDesc" protect +ion="public" static="no" virtualness="non_virtual" volatile="no"> <parameters declaration_name="name" type="const std::string &a +mp;"/> <parameters declaration_name="value" type="const std::string & +amp;"/> <parameters declaration_name="desc" default_value="&quot;&quot +;" type="const std::string &amp;"/> </members> <members const="no" kind="function" name="AttributeDesc" protect +ion="public" static="no" virtualness="non_virtual" volatile="no"></me +mbers> </public_methods> </classes>

        XML::Simple folds (by default) on the name= attribute. At lines -3, -8 from the end you have two <members> lines with the same name="AttributeDesc". This fooled XML::Simple into treating the two blocks as the same, allowing the second instance to overwrite the first. By adding KeyAttr=>[] as an option to XMLin, this default behaviour is suppressed and the expected result is obtained.

        use feature ":5.14"; use warnings FATAL => qw(all); use strict; use Data::Dump qw(dump pp); use XML::Simple; my $x = XMLin(<<'END', KeyAttr=>[]); <classes name="Panoply::AttributeDesc"> <all_members name="AttributeDesc" protection="public" scope="Panop +ly::AttributeDesc" virtualness="non_virtual"/> <all_members name="AttributeDesc" protection="public" scope="Panop +ly::AttributeDesc" virtualness="non_virtual"/> <all_members name="description" protection="public" scope="Panoply +::AttributeDesc" virtualness="non_virtual"/> <all_members name="name" protection="public" scope="Panoply::Attri +buteDesc" virtualness="non_virtual"/> <all_members name="value" protection="private" scope="Panoply::Att +ributeDesc" virtualness="non_virtual"/> <public_members> <members kind="variable" name="name" protection="public" static= +"no" type="std::string" virtualness="non_virtual"></members> <members kind="variable" name="value" protection="public" static +="no" type="std::string" virtualness="non_virtual"></members> <members kind="variable" name="description" protection="public" +static="no" type="std::string" virtualness="non_virtual"></members> </public_members> <public_methods> <members const="no" kind="function" name="AttributeDesc" protect +ion="public" static="no" virtualness="non_virtual" volatile="no"> <parameters declaration_name="name" type="const std::string &a +mp;"/> <parameters declaration_name="value" type="const std::string & +amp;"/> <parameters declaration_name="desc" default_value="&quot;&quot +;" type="const std::string &amp;"/> </members> <members const="no" kind="function" name="AttributeDesc" protect +ion="public" static="no" virtualness="non_virtual" volatile="no"></me +mbers> </public_methods> </classes> END #pp($x); sub r($$$;$); sub r($$$;$) {my ($r, $l, $e, $a) = @_; $a = [] unless $a; return unless $l and ref($l); if (ref($l) =~ /HASH/) {for(sort keys %$l) {unless (/$e/) {push @$a, $_; r($r, $l->{$_}, $e, $a); pop @$a; } else {&$r(@$a); } } } elsif (ref($l) =~ /ARRAY/) {for(1..@$l) {unless ($l->[$_-1] =~ /$e/) {push @$a, $_; r($r, $l->[$_-1], $e, $a); pop @$a; } else {&$r(@$a); } } } } r sub {say "@_"}, $x, "parameters";

        Produces

        public_methods members 1
        
Re: getting ancestors of element
by Anonymous Monk on Aug 30, 2012 at 19:46 UTC

    I get error cannot locate object method ancestors.

    When you read XML::Twig where is ancestors documented, which package does it belong to?

    Try $twig->root->ancestors

Re: getting ancestors of element
by mertserger (Curate) on Aug 31, 2012 at 09:12 UTC

    I would recommend mirod's site http://xmltwig.org/xmltwig/ as he is the author of XML::Twig. I find this section http://xmltwig.org/xmltwig/twig_stable.html particularly useful.

    I think your code my @names = $twig->ancestors('values'); has two problems. Firstly it is actually looking for ancestors of the string itself (and there are none). You need to find every tag within the string and find theirancestors. Secondly you have an optional condition on the ancestors: the ('values') bit. This means you would only get ancestors called 'values'. If you want every ancestor, no condition is needed.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://990820]
Front-paged by tye
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (14)
As of 2014-07-24 11:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (160 votes), past polls