Class::XPath?

samtregar on 2003-02-27T06:03:14

I really like my XPath for object trees idea. I like it so much that I want other people to use it too. The question is, what is the most effective way to make that happen?

I've been thinking I could make it into a module. It could be used like:

  package Some::Node;
  use Class::XPath
     get_parent => 'parent',
     get_root   => 
        sub { local $_ = shift;
              while($_->parent) { 
                $_ = $_->parent
              }
              return $_; 
            },
     get_name   => 'name',
     get_kids   => 'children',
     get_attr   => 'attr';

Then clients of Some::Node can do:

  @nodes = Some::Node->match('/foo/bar[1]/*');

The idea being that you give Class::XPath some instructions on how to perform various operations - either in the form of method names or as subroutines. Then Class::XPath creates customized versions of xpath() and match() that work with your class.

The value-add here is the XPath parser and executor. At start it would be a straight port of the simple parser I've written already. Later it could grow to include support for more of XPath and learn to optimize match() requests using caches and such. Then others can enjoy the benefits of having full XPath support for their trees without much work at all.

Feedback would be appreciated. Would you use such a module?

-sam


Tree Matching?

chromatic on 2003-02-27T07:11:47

I just wrote B::SAX yesterday (yes, it fires SAX events based on traversing a compiled Perl optree). Is it possible to run XPath over SAX events?

Otherwise, I'll have to write a little state machine to find the subtree I'm seeking. That doesn't sound too awful, but the less work, the better.

Re:Tree Matching?

Matts on 2003-02-27T07:26:07

Yes, see Barrie Slaymaker's XML::Filter::Dispatcher.

Either that or build a DOM (using something like XML::LibXML::SAX::Builder) and go from there.

yup

wickline on 2003-02-27T12:12:11

> Would you use such a module?

I'd love to :)

I can also imagine that it would also be handy to have the module work without having Some::Node's author build in support. This could allow your code to work with modules which were written before your release, and modules that didn't feel like adding yet another feature.
#!/usr/bin/perl
use HTML::TreeBuilder;
use HTML::Element;
use Class::XPath (
     install_in => 'HTML::Element',
     %the_get_foo_mappings,
);
or maybe
#!/usr/bin/perl
use HTML::TreeBuilder;
use Class::XPath;
my $tree = HTML::TreeBuilder->new();
my $xp_tree = Class::XPath (
     use_object => $tree,
     %the_get_foo_mappings,
);
If get_attr is supposed to return a list of attributes, then you'll probably need a get_attr_value or something too.

On the other hand, if get_attr is expected to take an attribute name and return a value (or undefined if the attribute does not exist) then there may be a problem in situations where the underlying object may allow for undef to be a reasonable value for an attribute. For example, if someone wanted to use Class::XPath to traverse a messy perl data structure, some of the values might well be undef. ...or maybe get_attr could work like CGI::param in the sense of returning a list of attributes when called without any args, and returning the value for a given attribute when passed that attribute.

I'm curious: how does XPATH handle meta-character escapes? I'm imagining the case of applying it to some representation of a CSS document. A selector's value may well contain a whole host of XPATH-looking syntax. You may want to expose an escape method, or alternate versions of each method (one which escapes the path/attr, one which does not).

In any case, I think the module is a great idea. It seems like a very sensible abstraction that lets XPATH work happen in one place and allows everyone the option of bolting XPATH support onto their project.

-matt

Re:yup

dlc on 2003-03-21T19:45:47

How about maybe:

  package Foo;
  use base qw(Class::XPath);

Class::XPath can be designed to be mixed-in to any arbitrary class, and simply provide a match method. Seems much simpler.

Re:yup

samtregar on 2003-03-21T19:53:23

How does Class::XPath magically know how to operate on the nodes of your tree unless you tell it? Sure, inheritence is an option but I doubt the end result would require any less configuration.

-sam

Re:yup

dlc on 2003-03-21T20:04:46

I wasn't disgreeing with the need for configuration, just the need usage examples. I was suggesting something more like:

  use base qw(Class::XPath);

  sub new {
      my $class = shift;
      my $self = bless { } => $class;

      $self->create_matcher(%xpath_stuff);

      return $self;
  }

Though I would suggest that a good default match method would be one that could automatically walk a data structure (i.e., didn't use methods, but did stuff based on the underlying data). This default matcher would probably be limited to non-attribute XPath operations, e.g.:

/root/foo[2]

which would find:

   $var->{'root'}->{'foo'}->[1]

(XPath is 1-based, right?)