=pod
=head1 NAME
Getopt::XML - Provide the user input arguments to Getopt::Long as an XML document
=head1 SYNOPSIS
Read in a list of XML elements and process them as the input arguments to the
Getopt::Long module
use XML::TreePP;
use Getopt::Long;
use Getopt::XML qw(GetXMLOptions);
use Data::Dump qw(dump);
#
# Set the XML Data
my $xmldata=<<"EOF_XMLDATA";
<apple>
<color>red</color>
<type>red delicious</type>
<isAvailable/>
<cityGrown>Macomb</cityGrown>
<cityGrown>Peoria</cityGrown>
<cityGrown>Galesburg</cityGrown>
</apple>
EOF_XMLDATA
#
# Parse the XML data using XML::TreePP module
my $tpp = XML::TreePP->new();
my $tree = $tpp->parse( $xmldata );
#
# Read the XML data in as arguments to Getopt::Long
my %options;
GetXMLOptions (
xmldoc => $tree,
xmlpath => '/apple',
Getopt_Long =>
[
\%options,
'isAvailable',
'color=s',
'type=s',
'cityGrown=s@'
]
);
dump(\%options);
This is the output:
{
cityGrown => ["Macomb", "Peoria", "Galesburg"],
color => "red",
isAvailable => 1,
type => "red delicious",
}
Alternately, the XML data can be in a file.
The above code would be rewritten as this:
use Getopt::Long;
use Getopt::XML qw(GetXMLOptions);
use Data::Dump qw(dump);
#
my %options;
GetXMLOptions (
xmlfile => '/path/to/xml_file.xml',
xmlpath => '/apple',
Getopt_Long =>
[
\%options,
'isAvailable',
'color=s',
'type=s',
'cityGrown=s@'
]
);
dump(\%options);
=head1 DESCRIPTION
This is simply another way to pass in user defined arguments to an application
using Getop::Long. The module provides a way to pass in user arguments from an
XML file into Getopt::Long.
In this way, the user can provide input to an application via an XML file. And
this can be useful if the application is ran at a regular interval. Or it may
be useful if the input to the application can change between multiple
executions, but the provided content is consistant over time based on the
context of the execution.
This input method may be desired for an application which has default values
for options the author wishes to exist, but the author wants those default
values to be changed by the user without having to edit the application code.
Or perhaps the application will reside in a multi-user environment, and this
module would be used to store the input options as part of the user preferences.
And finally, perhaps the options to be passed into the application resides
somewhere else in XML or another storage format that can be transformed into
XML as input to an application.
=head1 REQUIREMENTS
The following perl modules are depended on by this module:
( I<Note: Dependency on Params::Validate was removed in version 0.52> )
=over 4
=item * XML::TreePP
=item * XML::TreePP::XMLPath
=item * Getopt::Long # Getopt::Long 2.37 or greater is required
=back
Both XML::TreePP and XML::TreePP::XMLPath required modules are Pure PERL
implementations.
Getopt::Long version 2.37 introduces the GetOptionsFromArray() method. Versions
of Getopt::Long previous to 2.37 do not contain the GetOptionsFromArray() method.
The process of transforming XML data into options that can be passed into
Getopt::Long is performed with the GetOptionsFromArray() method.
=head1 IMPORTABLE METHODS
When the calling application invokes this module in a use clause, the following
methods can be imported into its space.
=over 4
=item * C<GetXMLOptions>
=item * C<GetXMLOptionsFromFile>
=back
Example:
use Getopt::XML qw( GetXMLOptions GetXMLOptionsFromFile );
=head1 METHODS
=cut
package Getopt::XML;
use 5.008001;
use strict;
use warnings;
use Exporter;
use Carp;
use Getopt::Long qw(GetOptionsFromArray); # requires Getopt::Long 2.37 or greater
use XML::TreePP;
use XML::TreePP::XMLPath qw(filterXMLDoc);
BEGIN {
use vars qw(@ISA @EXPORT @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw();
@EXPORT_OK = qw(&GetXMLOptions &GetXMLOptionsFromFile);
use vars qw($REF_NAME);
$REF_NAME = "Getopt::XML"; # package name
use vars qw( $VERSION );
$VERSION = '0.53';
}
=pod
=head2 new
Create a new object instances of this module.
=over 4
=item * I<returns>
An object instance of this module.
=back
my $glx = new Getopt::XML();
=cut
# new
#
# It is not necessary to create an object of this module.
# However, if you choose to do so any way, here is how you do it.
#
# my $obj = new Getopt::XML;
#
# You will have problems with the methods if you call them in an object
# oriented mannor. So you are better off not creating an object instance of
# this module, unless you are sure you know what you are doing.
#
sub new {
my $pkg = shift;
my $class = ref($pkg) || $pkg;
my $self = bless {}, $class;
return $self;
}
=pod
=head2 XMLToGetoptArgsArray
This method formats a subtree of an XML document into an array that is
acceptable to be passed in to the Getopt::Long::GetOptionsFromArray() method.
=over 4
=item * B<XMLTree>
The hash reference of a parsed XML file, which has been parsed
by XML::TreePP::parse()
=item * I<returns>
A reference to an array which an acceptable array input to the
Getopt::Long::GetOptionsFromArray() method.
=back
my $ha_list = XMLToGetoptArgsArray ( $XMLTree )
=cut
# XMLToGetoptArgsArray
# @param xmltree the XML::TreePP XML Tree
# @return [options] an array reference of options suitable for GetOpt::Long::GetoptionsFromArray()
sub XMLToGetoptArgsArray ($);
sub XMLToGetoptArgsArray ($) {
my $self = shift if ref($_[0]) eq $REF_NAME || undef;
if (@_ != 1) { carp 'method XMLToGetoptArgsArray($) requires one argument.'; return undef; }
my $xmltree = shift;
my @options;
if (ref $xmltree eq "ARRAY") {
# If the XMLPath filters to more than one node, we accept all results
foreach my $xml_e (@{$xmltree}) {
my $sopt = XMLToGetoptArgsArray($xml_e);
next if !defined $sopt;
push (@options,@{$sopt});
}
return \@options;
} elsif (ref $xmltree ne "HASH") {
return undef;
}
while (my ($key,$val) = each(%{$xmltree})) {
# We include attribute in the <tag>
if ($key =~ /^\-/) {
$key =~ s/^\-//;
}
if (! defined $val) {
push(@options,('--'.$key));
next;
} elsif (ref $val eq "ARRAY") {
foreach my $a (@{$val}) {
push(@options,('--'.$key));
push(@options,$a);
}
} elsif (ref $val eq "HASH") {
push(@options,('--'.$key));
foreach (my ($skey,$sval) = each %{$val}) {
push(@options,($skey.'='.$sval));
}
} elsif (ref $val eq "SCALAR") {
push(@options,('--'.$key));
push(@options,${$val});
} else {
push(@options,('--'.$key));
push(@options,$val);
}
}
return \@options;
}
=pod
=head2 GetXMLOptions
Read a XML::TreePP parsed XML document, retrieve the XML data located at the
specified XML subtree, and transform it into an acceptable argument array that
can be passed into to the Getopt::Long::GetOptionsFromArray() method.
=over 4
=item * B<xmldoc>
The hash reference of a parsed XML file, which has been parsed
by XML::TreePP::parse()
=item * B<xmlpath> - I<optional>
The XML Path as recognized by the XML::TreePP::XMLPath module.
Note that XMLPath is NOT an XPath, although it has similarities.
=item * B<Getopt_Long>
The hash array of a Getopt::Long configuration which is a definition
of the expected Getopt::Long::GetOptions() input options.
=back
GetXMLOptions ( xmldoc => $XMLTree, xmlpath => $XMLPath, Getopt_Long => \@options )
=cut
# GetXMLOptions
# @param xmldoc the XML::TreePP parsed XML file as a HASH
# @param xmlpath an XPath-like string used to retrieve a XML child element for the parsing, instead of the XML root
# @param Getopt_Long input for the Getopt::Long::GetOptions(@) method used in this method for input
# @return the result of Getopt::Long() using the 'xmldoc' as input (as opposed to @ARGV as input)
#
# This method does not look at nor touch @ARGV, but instead parses the XML
# document 'xmldoc', starting at 'path', and uses that in place of @ARGV.
# Thus, the returning data is from the result of
# Getopt::Long::GetOptionsFromArray(<parsed-xml-file>,@{'Getopt_Long'})
#
sub GetXMLOptions (@) {
my $self = shift if ref($_[0]) eq $REF_NAME || undef;
if (! @_ >= 1) { carp 'method GetXMLOptions(@) requires at least one argument.'; return undef; }
my %args = @_; # xmldoc, dtddoc, xmlpath, Getopt_Long,
my $tree = $args{'xmldoc'};
# not yet supported # @param dtddoc (optional) the DTD doc to validate the XML doc
#my %args = validate ( @_, { xmldoc => { optional => 0 },
# dtddoc => { optional => 1 },
# xmlpath => { type => SCALAR, optional => 1 },
# Getopt_Long => { type => ARRAYREF, optional => 1 }
# }
# );
my $subtree;
if (defined $args{'xmlpath'}) {
$subtree = filterXMLDoc($tree,$args{'xmlpath'});
} else {
$subtree = $tree;
}
if (! defined $subtree) {
return undef;
}
my $xmlargs = XMLToGetoptArgsArray($subtree);
return GetOptionsFromArray($xmlargs,@{$args{'Getopt_Long'}});
}
=pod
=head2 GetXMLOptionsFromFile
This method is a wrapper around the Getopt::XML::GetXMLOptions() method.
Parse a XML file with the XML::TreePP::parsefile() method, then call the
GetXMLOptions() method with the resulting XML::TreePP parsed XML document and
other parameters passed in from the caller.
=over 4
=item * B<xmlfile>
The XML file which will be parsed by XML::TreePP::parse() into a hash reference.
=item * B<xmlpath> - I<optional>
The XML Path as recognized by the XML::TreePP::XMLPath module.
Note that XMLPath is NOT an XPath, although it has similarities.
=item * B<Getopt_Long>
The hash array of a Getopt::Long configuration which is a definition
of the expected Getopt::Log::GetOptions() input options.
=back
GetXMLOptionsFromFile ( xmlfile => $XMLFile, xmlpath => $XMLPath, Getopt_Long => \@options )
=cut
# GetXMLOptionsFromFile
# @param xmlfile the XML file
# @param xmlpath an XPath-like string used to retrieve a XML child element for the parsing, instead of the XML root
# @param Getopt_Long input for the Getopt::Long::GetOptions(@) method used in this method for input
# @return the result of Getopt::Long() using the 'xmlfile' as input (as opposed to @ARGV as input)
#
# See GetXMLOptions method.
#
sub GetXMLOptionsFromFile (@) {
my $self = shift if ref($_[0]) eq $REF_NAME || undef;
if (! @_ >= 1) { carp 'method GetXMLOptionsFromFile(@) requires at least one argument.'; return undef; }
my %args = @_; # xmlfile, dtdfile, xmlpath, Getopt_Long,
# not yet supported # @param dtdfile (optional) the DTD file to validate the XML file
#my %args = validate ( @_, { xmlfile => { optional => 0 },
# dtdfile => { optional => 1 },
# xmlpath => { type => SCALAR, optional => 1 },
# Getopt_Long => { type => ARRAYREF, optional => 0 }
# }
# );
my $tpp = XML::TreePP->new();
my $tree = $tpp->parsefile( $args{'xmlfile'} );
return GetXMLOptions(
xmldoc => $tree,
xmlpath => $args{'xmlpath'},
Getopt_Long => $args{'Getopt_Long'}
);
}
1;
__END__
=pod
=head1 EXAMPLES
=head2 Method: new
It is not necessary to create an object of this module.
However, if you choose to do so any way, here is how you do it.
my $obj = new Getopt::XML;
This module supports being called by two methods.
=over 4
=item 1. By importing the functions you wish to use, as in:
use Getopt::XML qw( function1 function2 );
function1( args )
See IMPORTABLE METHODS section for methods available for import
=item 2. Or by calling the functions in an object oriented mannor, as in:
my $glx = new Getopt::XML;
$glx->function1( args )
=back
Using either method works the same and returns the same output.
=head2 Method: XMLToGetoptArgsArray
use Getopt::Long qw(GetOptionsFromArray); # requires Getopt::Long 2.37 or greater
use Getopt::XML qw(XMLToGetoptArgsArray);
use XML::TreePP;
my $tpp = XML::TreePP->new();
my $tree = $tpp->parsefile( '/path/to/xml_file.xml' );
my $xmlargs = XMLToGetoptArgsArray($tree);
my %options;
my @Getopt_Long = (
\%options,
'opt1',
'opt2=s',
'opt3=s@'
);
GetOptionsFromArray($xmlargs,@Getopt_Long);
# data from XML is now in %options
Where the XML file looks like this:
<opt1/> <!-- true or false -->
<opt2>argA</opt2> <!-- single value option -->
<opt3>argB</opt3> <!-- multi-value option -->
<opt3>argC</opt3>
<opt3>argD</opt3>
etc...
=head2 Method: GetXMLOptions
Parse an XML file with XML::TreePP::parsefile() and pass in the resulting
parsed XML::TreePP document which will be used to create the arguments that
would be passed in to the Getopt::Long::GetOptionsFromArray() method.
use Getopt::Long;
use Getopt::XML qw(GetXMLOptions);
use XML::TreePP;
# Parse the XML file into an XML::TreePP document
my $tpp = XML::TreePP->new();
my $tree = $tpp->parsefile( '/path/to/xml_file.xml' );
# Define %options, and populate it with options found in the XML document
my %options;
GetXMLOptions (
xmldoc => $tree,
xmlpath => '/xmlconfig/options',
Getopt_Long =>
[
\%options,
'verbose',
'single_option=s',
'multiple_options=s@'
]
);
Where the file '/path/to/xml_file.xml' contains the following content:
<xmlconfig>
<options>
<verbose/>
<single_option>argument1A</single_option>
<multiple_options>argument2A</multiple_options>
<multiple_options>argument2B</multiple_options>
<multiple_options>argument2C</multiple_options>
</options>
</xmlconfig>
=head2 Method: GetXMLOptionsFromFile
Parse an XML file to create the arguments that would be passed in to
the Getopt::Long::GetOptionsFromArray() method.
With the Getopt::XML module, you can now put the would be user
input into an XML file like the example below.
use Getopt::Long;
use Getopt::XML qw(GetXMLOptionsFromFile);
# Define %options, and populate it with options found in the XML file
my %options;
GetXMLOptionsFromFile (
xmlfile => '/path/to/xml_file.xml',
xmlpath => '/xmlconfig/options',
Getopt_Long =>
[
\%options,
'verbose',
'single_option=s',
'multiple_options=s@'
]
);
Where the file '/path/to/xml_file.xml' contains the following content:
<xmlconfig>
<options>
<verbose/>
<single_option>argument1A</single_option>
<multiple_options>argument2A</multiple_options>
<multiple_options>argument2B</multiple_options>
<multiple_options>argument2C</multiple_options>
</options>
</xmlconfig>
=head2 General
The sample XML file '/path/to/xml_file.xml' contains the following content:
<xmlconfig>
<options>
<verbose/>
<single_option>argument1A</single_option>
<multiple_options>argument2A</multiple_options>
<multiple_options>argument2B</multiple_options>
<multiple_options>argument2C</multiple_options>
</options>
</xmlconfig>
B<What you are used to doing with the Getopt::Long module>
With the Getopt::Long module alone, you performed this to retrieve user input.
This prints out what the user passed in for the --single_option option on the
command line.
use Getopt::Long;
my %options;
GetOptions ( %options,
'verbose',
'single_option=s',
'multiple_options=s@'
);
print $options{'single_option'},"\n";
B<What you can do using XML>
With the Getopt::XML module, you can now put the would be user input into
an XML file like the sample XML file above.
This prints out the value of the <single_option></single_option> element as
found in the XML file.
use Getopt::Long;
use Getopt::XML qw(GetXMLOptionsFromFile);
my %options;
GetXMLOptionsFromFile (
xmlfile => '/path/to/xml_file.xml',
xmlpath => '/xmlconfig/options',
Getopt_Long =>
[
\%options,
'verbose',
'single_option=s',
'multiple_options=s@'
]
);
print $options{'single_option'},"\n";
B<What you can do using both methods>
Now try doing both. You can provide the XML file to give all the defaults for
the input to your application, then you can overwrite the defaults with user
provided input.
This prints out what the user passed in for the --single_option option on the
command line. However, if the user did not provide such input, then the value
for this option as found in the XML file is what is printed out.
use Getopt::Long;
use Getopt::XML qw(GetXMLOptionsFromFile);
# Read in the default arguments for your options from the XML file:
my %options;
GetXMLOptionsFromFile (
xmlfile => '/path/to/xml_file.xml',
xmlpath => '/xmlconfig/options',
Getopt_Long =>
[
\%options,
'verbose',
'single_option=s',
'multiple_options=s@'
]
);
# Then overwrite those defaults with user provided input
GetOptions ( %options,
'verbose',
'single_option=s',
'multiple_options=s@'
);
print $options{'single_option'},"\n";
B<Passing in XML:TreePP parsed XML, instead of a file>
Optionally, if you are using the XML::TreePP module for your XML files, you can
pass in the parsed XML::TreePP XML document instead of the actual file.
use Getopt::Long;
use Getopt::XML qw(GetXMLOptions);
use XML::TreePP;
# Parse the XML file using XML::TreePP module
my $tpp = XML::TreePP->new();
my $tree = $tpp->parsefile( '/path/to/xml_file.xml' );
my %options;
# Read the XML data in as arguments to Getopt::Long
GetXMLOptions (
xmldoc => $tree,
xmlpath => '/xmlconfig/options',
Getopt_Long =>
[
\%options,
'verbose',
'single_option=s',
'multiple_options=s@'
]
);
Leveraging the capabilites of XML::TreePP, you can also fetch the XML file
from the internet via a HTTP GET request
use Getopt::Long;
use Getopt::XML qw(GetXMLOptions);
use XML::TreePP;
# Parse the XML file using XML::TreePP module
my $tpp = XML::TreePP->new();
my $tree = $tpp->parsehttp( GET => "http://config.mydomain.com/myapp/default_settings.xml" );
my %options;
# Read the XML data in as arguments to Getopt::Long
GetXMLOptions (
xmldoc => $tree,
xmlpath => '/xmlconfig/options',
Getopt_Long =>
[
\%options,
'verbose',
'single_option=s',
'multiple_options=s@'
]
);
=head1 AUTHOR
Russell E Glaue, http://russ.glaue.org
=head1 SEE ALSO
C<Getopt::Long>
C<XML::TreePP>
C<XML::TreePP::XMLPath>
Getopt::XML on Codepin: http://www.codepin.org/project/perlmod/Getopt-XML
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2008-2009 Center for the Application of Information Technologies.
All rights reserved.
This program is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
=cut