()
Format C<-PnDTnHnMnS>, where optional starting C<-> means negative.
The C is obligatory, and the C indicates start of a time part.
All other C are optional.
=item B()
Format C<-PnYnMnDTnHnMnS>, where optional starting C<-> means negative.
The C is obligatory, and the C indicates start of a time part.
All other C are optional.
=item B()
Format C<-PnYnMn>, where optional starting C<-> means negative.
The C is obligatory, the C are optional.
=back
=head3 Strings
=over 4
=item B(, IDREF, IDREFS)
A label, reference to a label, or set of references.
PARTIAL IMPLEMENTATION: the validity of used characters is not checked.
=item B(, ENTITY, ENTITIES)
A name which contains no colons (a non-colonized name).
=item B()
=item B()
An RFC3066 language indicator.
=item B()
String where all sequence of white-spaces (including new-lines) are
interpreted as one blank. Blanks at beginning and the end of the
string are ignored.
=item B()
(Usually utf8) string.
=item B(, NMTOKEN, NMTOKENS)
=back
=head3 URI
=over 4
=item B()
NOT IMPLEMENTED, so treated as string.
=item B()
A qualified type name: a type name with optional prefix. The prefix notation
C will be translated into the C<{$ns}type> notation.
For writers, this translation can only happen when the C<$ns> is also
in use on some other place in the message: the name-space declaration
can not be added at run-time. In other cases, you will get a run-time
error. Play with L,
predefining evenything what may be used, setting the C count to C<1>.
=item B()
You may pass a string or, for instance, an URI object which will be
stringified into an URI. When read, the data will not automatically
be translated into an URI object: it may not be used that way.
=back
=head3 only in 1999 and 2000/10 schemas
=over 4
=item B()
Perl strings can contain any byte, also nul-strings, so can
contain any sequence of bits. Limited to byte length.
=item B()
'Old' name for L.
=item B()
Probably the same rules as L.
=back
=head1 SEE ALSO
This module is part of XML-Compile distribution version 1.58,
built on June 27, 2017. Website: F
Please post questions or ideas to the mailinglist at
F .
For live contact with other developers, visit the C<#xml-compile> channel
on C.
=head1 LICENSE
Copyrights 2006-2017 by [Mark Overmeer]. For other contributors see ChangeLog.
This program is free software; you can redistribute it and/or modify it
under the Artistic license.
See F
XML-Compile-1.58/lib/XML/Compile/Schema/Instance.pm 0000644 0001750 0000144 00000016065 13124470276 022331 0 ustar 00markov users 0000000 0000000 # Copyrights 2006-2017 by [Mark Overmeer].
# For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
use warnings;
use strict;
package XML::Compile::Schema::Instance;
use vars '$VERSION';
$VERSION = '1.58';
use Log::Report 'xml-compile', syntax => 'SHORT';
use XML::Compile::Schema::Specs;
use XML::Compile::Util qw/pack_type unpack_type/;
use Scalar::Util qw/weaken/;
my @defkinds = qw/element attribute simpleType complexType
attributeGroup group/;
my %defkinds = map +($_ => 1), @defkinds;
sub new($@)
{ my $class = shift;
(bless {}, $class)->init( {top => @_} );
}
sub init($)
{ my ($self, $args) = @_;
my $top = $args->{top};
defined $top && $top->isa('XML::LibXML::Node')
or panic "instance is based on XML node";
$self->{filename} = $args->{filename};
$self->{source} = $args->{source};
$self->{$_} = {} for @defkinds, 'sgs', 'import';
$self->{include} = [];
$self->_collectTypes($top, $args);
$self;
}
sub targetNamespace { shift->{tns} }
sub schemaNamespace { shift->{xsd} }
sub schemaInstance { shift->{xsi} }
sub source { shift->{source} }
sub filename { shift->{filename} }
sub schema { shift->{schema} }
sub tnses() {keys %{shift->{tnses}}}
sub sgs() { shift->{sgs} }
sub type($) { $_[0]->{types}{$_[1]} }
sub element($) { $_[0]->{element}{$_[1]} }
sub elements() { keys %{shift->{element}} }
sub attributes() { keys %{shift->{attributes}} }
sub attributeGroups() { keys %{shift->{attributeGroup}} }
sub groups() { keys %{shift->{group}} }
sub simpleTypes() { keys %{shift->{simpleType}} }
sub complexTypes() { keys %{shift->{complexType}} }
sub types() { ($_[0]->simpleTypes, $_[0]->complexTypes) }
my %skip_toplevel = map +($_ => 1), qw/annotation notation redefine/;
sub _collectTypes($$)
{ my ($self, $schema, $args) = @_;
$schema->localName eq 'schema'
or panic "requires schema element";
my $xsd = $self->{xsd} = $schema->namespaceURI || '';
if(length $xsd)
{ my $def = $self->{def}
= XML::Compile::Schema::Specs->predefinedSchema($xsd)
or error __x"schema namespace `{namespace}' not (yet) supported"
, namespace => $xsd;
$self->{xsi} = $def->{uri_xsi};
}
my $tns;
if($tns = $args->{target_namespace})
{ $schema->removeAttribute('targetNamespace');
$schema->setAttribute(targetNamespace => $tns);
}
else
{ $tns = $schema->getAttribute('targetNamespace') || '';
}
$self->{tns} = $tns;
$self->{efd} = $args->{element_form_default}
|| $schema->getAttribute('elementFormDefault')
|| 'unqualified';
$self->{afd} = $args->{attribute_form_default}
|| $schema->getAttribute('attributeFormDefault')
|| 'unqualified';
$self->{tnses} = {}; # added when used
$self->{types} = {};
$self->{schema} = $schema;
weaken($self->{schema});
NODE:
foreach my $node ($schema->childNodes)
{ next unless $node->isa('XML::LibXML::Element');
my $local = $node->localName;
my $myns = $node->namespaceURI || '';
$myns eq $xsd
or error __x"schema element `{name}' not in schema namespace {ns} but {other}"
, name => $local, ns => $xsd, other => ($myns || '');
next
if $skip_toplevel{$local};
if($local eq 'import')
{ my $namespace = $node->getAttribute('namespace') || $tns;
my $location = $node->getAttribute('schemaLocation') || '';
push @{$self->{import}{$namespace}}, $location;
next NODE;
}
if($local eq 'include')
{ my $location = $node->getAttribute('schemaLocation')
or error __x"include requires schemaLocation attribute at line {linenr}"
, linenr => $node->line_number;
push @{$self->{include}}, $location;
next NODE;
}
unless($defkinds{$local})
{ mistake __x"ignoring unknown definition class {class}"
, class => $local;
next;
}
my $name = $node->getAttribute('name')
or error __x"schema component {local} without name at line {linenr}"
, local => $local, linenr => $node->line_number;
my $tns = $node->getAttribute('targetNamespace') || $tns;
my $type = pack_type $tns, $name;
$self->{tnses}{$tns}++;
$self->{$local}{$type} = $node;
if(my $sg = $node->getAttribute('substitutionGroup'))
{ my ($prefix, $l) = $sg =~ m/:/ ? split(/:/, $sg, 2) : ('',$sg);
my $base = pack_type $node->lookupNamespaceURI($prefix), $l;
push @{$self->{sgs}{$base}}, $type;
}
}
$self;
}
sub includeLocations() { @{shift->{include}} }
sub imports() { keys %{shift->{import}} }
sub importLocations($)
{ my $locs = $_[0]->{import}{$_[1]};
$locs ? @$locs : ();
}
sub printIndex(;$)
{ my $self = shift;
my $fh = @_ % 2 ? shift : select;
my %args = @_;
$fh->print("namespace: ", $self->targetNamespace, "\n");
if(defined(my $filename = $self->filename))
{ $fh->print(" filename: $filename\n");
}
elsif(defined(my $source = $self->source))
{ $fh->print(" source: $source\n");
}
my @kinds
= ! defined $args{kinds} ? @defkinds
: ref $args{kinds} eq 'ARRAY' ? @{$args{kinds}}
: $args{kinds};
my $list_abstract
= exists $args{list_abstract} ? $args{list_abstract} : 1;
foreach my $kind (@kinds)
{ my $table = $self->{$kind};
keys %$table or next;
$fh->print(" definitions of ${kind}s:\n") if @kinds > 1;
foreach (sort keys %$table)
{ my $info = $self->find($kind, $_);
my ($ns, $name) = unpack_type $_;
next if $info->{abstract} && ! $list_abstract;
my $abstract = $info->{abstract} ? ' [abstract]' : '';
my $final = $info->{final} ? ' [final]' : '';
$fh->print(" $name$abstract$final\n");
}
}
}
sub find($$)
{ my ($self, $kind, $full) = @_;
my $node = $self->{$kind}{$full}
or return;
return $node # translation of XML node into info is cached
if ref $node eq 'HASH';
my %info = (type => $kind, node => $node, full => $full);
@info{'ns', 'name'} = unpack_type $full;
$self->{$kind}{$full} = \%info;
my $abstract = $node->getAttribute('abstract') || '';
$info{abstract} = $abstract eq 'true' || $abstract eq '1';
my $final = $node->getAttribute('final') || '';
$info{final} = $final eq 'true' || $final eq '1';
my $local = $node->localName;
if($local eq 'element') { $info{efd} = $node->getAttribute('form') }
elsif($local eq 'attribute'){ $info{afd} = $node->getAttribute('form') }
$info{efd} ||= $self->{efd}; # both needed for nsContext
$info{afd} ||= $self->{afd};
\%info;
}
1;
XML-Compile-1.58/lib/XML/Compile/Schema/Specs.pm 0000644 0001750 0000144 00000007215 13124470276 021637 0 ustar 00markov users 0000000 0000000 # Copyrights 2006-2017 by [Mark Overmeer].
# For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
use warnings;
use strict;
package XML::Compile::Schema::Specs;
use vars '$VERSION';
$VERSION = '1.58';
use Log::Report 'xml-compile', syntax => 'SHORT';
use XML::Compile::Schema::BuiltInTypes qw/%builtin_types/;
use XML::Compile::Util qw/SCHEMA1999 SCHEMA2000 SCHEMA2001 unpack_type/;
### Who will extend this?
# everything which is not caught by a special will need to pass through
# the official meta-scheme: the scheme of the scheme. These lists are
# used to restrict the namespace to the specified, hiding all helper
# types.
my @builtin_common = qw/
boolean
byte
date
decimal
double
duration
ENTITIES
ENTITY
float
ID
IDREF
IDREFS
int
integer
language
long
Name
NCName
negativeInteger
NMTOKEN
NMTOKENS
nonNegativeInteger
nonPositiveInteger
NOTATION
pattern
positiveInteger
QName
short
string
time
token
unsignedByte
unsignedInt
unsignedLong
unsignedShort
yearMonthDuration
/;
my @builtin_extra_1999 = qw/
binary
recurringDate
recurringDay
recurringDuration
timeDuration
timeInstant
timePeriod
uriReference
year
/;
my @builtin_extra_2000 = (@builtin_extra_1999, qw/
anyType
CDATA
/ );
my @builtin_extra_2001 = qw/
anySimpleType
anyType
anyURI
base64Binary
dateTime
dayTimeDuration
error
gDay
gMonth
gMonthDay
gYear
gYearMonth
hexBinary
normalizedString
precisionDecimal
/;
my %builtin_public_1999 = map { ($_ => $_) }
@builtin_common, @builtin_extra_1999;
my %builtin_public_2000 = map { ($_ => $_) }
@builtin_common, @builtin_extra_2000;
my %builtin_public_2001 = map { ($_ => $_) }
@builtin_common, @builtin_extra_2001;
my %sloppy_int_version =
( integer => 'int'
, long => 'int'
, nonNegativeInteger => 'unsigned_int'
, nonPositiveInteger => 'non_pos_int'
, positiveInteger => 'positive_int'
, negativeInteger => 'negative_int'
, unsignedLong => 'unsigned_int'
, unsignedInt => 'unsigned_int'
);
my %sloppy_float_version = map +($_ => 'sloppy_float'),
qw/decimal precisionDecimal float double/;
my %schema_1999 =
( uri_xsd => SCHEMA1999
, uri_xsi => SCHEMA1999.'-instance'
, builtin_public => \%builtin_public_1999
);
my %schema_2000 =
( uri_xsd => SCHEMA2000
, uri_xsi => SCHEMA2000.'-instance'
, builtin_public => \%builtin_public_2000
);
my %schema_2001 =
( uri_xsd => SCHEMA2001
, uri_xsi => SCHEMA2001 .'-instance'
, builtin_public => \%builtin_public_2001
);
my %schemas = map { ($_->{uri_xsd} => $_) }
\%schema_1999, \%schema_2000, \%schema_2001;
sub predefinedSchemas() { keys %schemas }
sub predefinedSchema($) { defined $_[1] ? $schemas{$_[1]} : () }
sub builtInType($$;$@)
{ my ($class, $node, $ns) = (shift, shift, shift);
my $name = @_ % 1 ? shift : undef;
($ns, $name) = unpack_type $ns
unless defined $name;
my $schema = $schemas{$ns}
or return ();
my %args = @_;
return $builtin_types{boolean_with_Types_Serialiser}
if $args{json_friendly} && $name eq 'boolean';
return $builtin_types{$sloppy_int_version{$name}}
if $args{sloppy_integers} && exists $sloppy_int_version{$name};
if($args{sloppy_floats} && (my $maptype = $sloppy_float_version{$name}))
{ return $builtin_types{sloppy_float_force_NV}
if $args{json_friendly} && $maptype eq 'sloppy_float';
return $builtin_types{$maptype};
}
# only official names are exported this way
my $public = $schema->{builtin_public}{$name};
defined $public ? $builtin_types{$public} : ();
}
1;
XML-Compile-1.58/lib/XML/Compile/Schema.pod 0000644 0001750 0000144 00000220266 13124470313 020723 0 ustar 00markov users 0000000 0000000 =encoding utf8
=head1 NAME
XML::Compile::Schema - Compile a schema into CODE
=head1 INHERITANCE
XML::Compile::Schema
is a XML::Compile
XML::Compile::Schema is extended by
XML::Compile::Cache
=head1 SYNOPSIS
# compile tree yourself
my $parser = XML::LibXML->new;
my $tree = $parser->parse...(...);
my $schema = XML::Compile::Schema->new($tree);
# get schema from string
my $schema = XML::Compile::Schema->new($xml_string);
# get schema from file (most used)
my $schema = XML::Compile::Schema->new($filename);
my $schema = XML::Compile::Schema->new([glob "*.xsd"]);
# the "::Cache" extension has more power
my $schema = XML::Compile::Cache->new(\@xsdfiles);
# adding more schemas, from parsed XML
$schema->addSchemas($tree);
# adding more schemas from files
# three times the same: well-known url, filename in schemadir, url
# Just as example: usually not needed.
$schema->importDefinitions('http://www.w3.org/2001/XMLSchema');
$schema->importDefinitions('2001-XMLSchema.xsd');
$schema->importDefinitions(SCHEMA2001); # from ::Util
# alternatively
my @specs = ('one.xsd', 'two.xsd', $schema_as_string);
my $schema = XML::Compile::Schema->new(\@specs); # ARRAY!
# see what types are defined
$schema->printIndex;
# create and use a reader
use XML::Compile::Util qw/pack_type/;
my $elem = pack_type 'my-namespace', 'my-local-name';
# $elem eq "{my-namespace}my-local-name"
my $read = $schema->compile(READER => $elem);
my $data = $read->($xmlnode);
my $data = $read->("filename.xml");
# when you do not know the element type beforehand
use XML::Compile::Util qw/type_of_node/;
my $elem = type_of_node $xml->documentElement;
my $reader = $reader_cache{$type} # either exists
||= $schema->compile(READER => $elem); # or create
my $data = $reader->($xmlmsg);
# create and use a writer
my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
my $write = $schema->compile(WRITER => '{myns}mytype');
my $xml = $write->($doc, $hash);
$doc->setDocumentElement($xml);
# show result
print $doc->toString(1);
# to create the type nicely
use XML::Compile::Util qw/pack_type/;
my $type = pack_type 'myns', 'mytype';
print $type; # shows {myns}mytype
# using a compiled routines cache
use XML::Compile::Cache; # separate distribution
my $schema = XML::Compile::Cache->new(...);
# Show which data-structure is expected
print $schema->template(PERL => $type);
# Error handling tricks with Log::Report
use Log::Report mode => 'DEBUG'; # enable debugging
dispatcher SYSLOG => 'syslog'; # errors to syslog as well
try { $reader->($data) }; # catch errors in $@
=head1 DESCRIPTION
This module collects knowledge about one or more schemas. The most
important method provided is L, which can create XML file
readers and writers based on the schema information and some selected
element or attribute type.
Various implementations use the translator, and more can be added
later:
=over 4
=item C<< $schema->compile('READER'...) >> translates XML to HASH
The XML reader produces a HASH from a XML::LibXML::Node tree or an
XML string. Those represent the input data. The values are checked.
An error produced when a value or the data-structure is not according
to the specs.
The CODE reference which is returned can be called with anything
accepted by L.
Example: create an XML reader
my $msgin = $rules->compile(READER => '{myns}mytype');
# or ... = $rules->compile(READER => pack_type('myns', 'mytype'));
my $xml = $parser->parse("some-xml.xml");
my $hash = $msgin->($xml);
or
my $hash = $msgin->('some-xml.xml');
my $hash = $msgin->($xml_string);
my $hash = $msgin->($xml_node);
with L as schema object:
$rules->addPrefix(m => 'myns');
my $hash = $rules->reader('m:mytype')->($xml);
=item C<< $schema->compile('WRITER', ...) >> translates HASH to XML
The writer produces schema compliant XML, based on a Perl HASH. To get
the data encoding correctly, you are required to pass a document object
in which the XML nodes may get a place later.
Create an XML writer
my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
my $write = $schema->compile(WRITER => '{myns}mytype');
my $xml = $write->($doc, $hash);
print $xml->toString;
alternative
my $write = $schema->compile(WRITER => 'myns#myid');
with L as schema object:
$rules->addPrefix(m => 'myns');
my $xml = $rules->writer('m:mytype')->($doc, $hash);
=item C<< $schema->template('XML', ...) >> creates an XML example
Based on the schema, this produces an XML message as example. Schemas
are usually so complex that people loose overview. This example may
put you back on track, and used as starting point for many creating the
XML version of the message.
=item C<< $schema->template('PERL', ...) >> creates an Perl example
Based on the schema, this produces an Perl HASH structure (a bit
like the output by Data::Dumper), which can be used as template
for creating messages. The output contains documentation, and is
usually much clearer than the schema itself.
=item C<< $schema->template('TREE', ...) >> creates a parse tree
To be able to produce Perl-text and XML examples, the templater
generates an abstract tree from the schema. That tree is returned
here. Be warned that the structure is not fixed over releases:
add regression tests for this to your project.
=back
Be warned that the B; you can develop schemas
which do work well with this module, but are not valid according to W3C.
In many cases, however, the translater will refuse to accept mistakes:
mainly because it cannot produce valid code.
Extends L<"DESCRIPTION" in XML::Compile|XML::Compile/"DESCRIPTION">.
=head1 METHODS
Extends L<"METHODS" in XML::Compile|XML::Compile/"METHODS">.
=head2 Constructors
Extends L<"Constructors" in XML::Compile|XML::Compile/"Constructors">.
=over 4
=item XML::Compile::Schema-EB( [$xmldata], %options )
Details about many name-spaces can be organized with only a single
schema object (actually, the data is administered in an internal
L object)
The initial information is extracted from the $xmldata source. The $xmldata
can be anything what is acceptable by L, which
is everything accepted by L or an ARRAY of those things.
You may also add any OPTION accepted by L to guide the
understanding of the schema. When no $xmldata is provided, you can add
it later with L
You can specify the hooks before you define the schemas the hooks
work on: all schema information and all hooks are only used when
the readers and writers get compiled.
-Option --Defined in --Default
block_namespace []
hook undef
hooks []
ignore_unused_tags
key_rewrite []
parser_options XML::Compile
schema_dirs XML::Compile undef
typemap {}
=over 2
=item block_namespace => NAMESPACE|TYPE|HASH|CODE|ARRAY
See L
=item hook => HOOK|ARRAY
See L. Adds one HOOK (HASH) or more at once.
=item hooks => ARRAY
Add one or more hooks. See L.
=item ignore_unused_tags => BOOLEAN|REGEXP
(WRITER) Usually, a C warning is produced when a user provides
a data structure which contains more data than is needed for the XML
message which is created; this will show structural problems. However,
in some cases, you may want to play tricks with the data-structure and
therefore disable this precausion.
With a REGEXP, you can have more control. Only keys which do match
the expression will be ignored silently. Other keys (usually typos
and other mistakes) will get reported. See L
=item key_rewrite => HASH|CODE|ARRAY
Translate XML element local-names into different Perl keys.
See L.
=item parser_options => HASH|ARRAY
=item schema_dirs => DIRECTORY|ARRAY-OF-DIRECTORIES
=item typemap => HASH
HASH of Schema type to Perl object or Perl class. See L, the
serialization of objects.
=back
=back
=head2 Accessors
Extends L<"Accessors" in XML::Compile|XML::Compile/"Accessors">.
=over 4
=item $obj-EB($hook|LIST|undef)
A $hook is specified as HASH or a LIST of PAIRS. When C, this call
is ignored. See L and L below.
=item $obj-EB( $hook, [$hook, ...] )
Add multiple hooks at once. These must all be HASHes. See L