The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Apache::AxKit::Language::XPathScript - Simple XPath web scripting

SYNOPSIS

  PerlTypeHandler Apache::AxKit::XMLFinder
  PerlHandler Apache::AxKit::StyleFinder
  PerlSetVar StylesheetMap "application/x-xpathscript => \
                Apache::AxKit::Language::XPathScript"

DESCRIPTION

This module provides the user with simple XPath crossed with ASP-style scripting in a template. The system picks the template from the <?xml-stylesheet?> processing instruction using Apache::AxKit::StyleFinder and then this module combines the xml source file, and the stylesheet by setting the xml file up as the document for XPath expressions to be used.

SYNTAX

The syntax follows the basic ASP stuff. <% introduces perl code, and %> closes that section of perl code. <%= ... %> can be used to output a perl expression.

The interesting stuff comes when you start to use XPath. The following methods are available for your use:

findnodes($path, [$context])
findvalue($path, [$context])
findnodes_as_string($path, [$context])
apply_templates( $path, [$context])
apply_templates( @nodes )

The find* functions are identical to the XML::XPath methods of the same name, so see XML::XPath for more information. They allow you to create dynamic templates extremely simply:

  <%
  foreach my $n (findnodes('//fred')) {
    print "Found a fred\n";
    foreach my $m (findnode('..', $n)) {
      print "fred's parent was: ", $m->[node_name], "\n";
    }
  }
  %>

This, combined with the simplicity of both ASP and XPath, make a pretty powerful combination.

Even more powerful though is the ability to do XSLT-like apply-templates on nodes. The apply_templates function looks at the information in the $t hash reference. If there is a key with the same name as the current tag, the values in that key are used for processing. Here's a guide to some of the possibilities:

        <%
        foreach my $node (findnodes('xpath/here')) {
                print apply_templates($node);
        }
        %>

That prints all nodes found by the path.

        <%
        print findvalue('xpath/here', $context);
        %>

That prints the value of whatever was found by the XPath search in context $context. (if the search returns a NodeSet it prints the string-value of the nodeset. See the Xpath spec for what that means).

        <%
        print findnodes_as_string('xpath/here');
        %>

That prints the nodes as they were found. e.g. if the xml was:

        <foo>
                <bar><foobar>Hello!</foobar></bar>
        </foo>

and the search was '/foo/bar/foobar' it prints '<foobar>Hello!</foobar>'.

        <%
        $t->{'a'}{pre} = '<i>';
        $t->{'a'}{post} = '</i>';
        $t->{'a'}{showtag} = 1;
        %>

When using apply_templates(@nodes) (recommended), this prints all <a> tags with <i>...</i> around them.

        <%
        $t->{'a'}{pre} = '<i>';
        $t->{'a'}{post} = '</i>';
        %>

When using apply_templates(@nodes), on <a> tags, prints <i>...</i> instead of <a>...</a>.

        <%
        $t->{'a'}{testcode} = sub {...};
        %>

This sets up a sub to determine what to do with the node when apply_templates is used. The sub recieves the node as the first (and only) parameter. Return 0 to stop processing at that node and return, return -1 to process that node but not its children, and return 1 to process this node normally (i.e. process this node and its children). An example of where this might be useful is to test the context of this node:

        <%
        $t->{'a'}{testcode} = 
        sub {
                my $node = shift;
                if (findvalue('ancestor::foo = true()', $node)) {
                    return 0;
                }
                return 1;
        };
        %>

This only process 'a' nodes that aren't descendants of a 'foo' element.

You can also use testcode to setup custom values for pre and post, depending on context, for example:

  $t->{'title'}{testcode} =
        sub {
                my $node = shift;
                if (findvalue('parent::section = true()', $node)) {
                        $t->{'title'}{pre} = '<h1>';
                        $t->{'title'}{post} = '</h1>';
                }
                elsif (findvalue('parent::subsection = true()', $node)) {
                       $t->{'title'}{pre} = '<h2>';
                        $t->{'title'}{post} = '</h2>';
                }
                                else {
                                        $t->{'title'}{pre} = '<title>';
                                        $t->{'title'}{post} = '</title>';
                                }
                return 1;
        };

Which sets titles to appear as h1's in a section context, or as h2's in a subsection context, or just ordinary titles in other contexts.

A COMPLETE EXAMPLE

This is the code I use to process some web pages:

  <%
  $t->{'a'}{pre} = '<i>';
  $t->{'a'}{post} = '</i>';
  $t->{'a'}{showtag} = 1;
  
  $t->{'title'}{testcode} =
    sub {
        my $node = shift;
        if (findvalue('parent::section = true()', $node)) {
            $t->{'title'} =
                 { pre => '<h2>', post => '</h2>' };
        }
        elsif (findvalue('parent::subsection = true()', $node)) {
            $t->{'title'} =
                 { pre => '<h3>', post => '</h3>' };
        }
        return 1;
    };
  
  $t->{'section'}{post} = '<p>';
  $t->{'subsection'}{post} = '<br>';
  
  %>
  <html>
  <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <%= apply_templates('/page/head/title') %>
  </head>
  <body bgcolor="white">
        <h1><%= findvalue('/page/head/title/text()') %></h1>
        
  <%= apply_templates('/page/body/section') %>

  <br>
  <small>This page is copyright Fastnet Software Ltd, 2000.
  Contact <a href="mailto:matt@sergeant.org">Matt Sergeant</a>
  for details and availability.</small>
  </body>
  </html>

AUTHOR

Matt Sergeant, matt@sergeant.org

SEE ALSO

XML::XPath.

LICENSE

This module is free software, and is distributed under the same terms as Perl.