[Xml-compile] Reading (hooks) according to xsi:type without xsi:type hierarchy

Roman Daniel roman.daniel at davosro.cz
Thu Nov 26 11:29:27 GMT 2015


Hi Mark,

thanks for the answer. It works. I do not understand how could I overlook
compileType. Notice it earlier could have saved me from tens of XPaths.

There is only one minor problem. The element with xsi:Type can be defined
in the schema either as xs:anyType or as a base type. So I decided to use
replace hook and for each element to check xsi:type first. If there is a
xsi:type I will create new reader, otherwise I use the reader passed to the
hook. But if I create a new reader and call it from the hook, the hook is
called again, again, and again. So I need to come up with a cumbersome
solution how to skip the invocation of reader based on xsi:type when I am
already in the reader based on xsi:type.

Is there a cleaner way how to deal with such infinite recursion?

Also if I use reader instead of compile, compileType I will end up with
error message: cannot find element or attribute `{urn:testHook}extType' at
me:extType

Both problems are better illustrated with example below:

Thanks

Roman Daniel

use strict;
use warnings;

use XML::Compile::Schema;
use XML::Compile::Util qw(pack_type);
use Data::Dump qw(pp);

sub get_xsi_type {
    my ($elem) = @_;

    my $type
        = $elem->getAttributeNS( "http://www.w3.org/2001/XMLSchema-instance
",
        'type' )
        or return;

    my ( $prefix, $localname )
        = $type =~ /^(.*?):(.*)/ ? ( $1, $2 ) : ( '', $type );
    my $ns = $elem->lookupNamespaceURI($prefix);
    return pack_type( $ns, $localname );
}

my $TEST_NS    = 'urn:testHook';
my $schema_txt = <<"END_SCHEMA";
<schema targetNamespace="$TEST_NS"
        xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:me="$TEST_NS"
elementFormDefault="qualified">

<element name="env">
  <complexType>
    <sequence>
       <element name="alfa" type="me:baseType" />
       <element name="beta" type="anyType"  />
    </sequence>
  </complexType>
</element>

<complexType name="baseType">
    <sequence>
        <element name="num_one" type="int" />
    </sequence>
</complexType>

<complexType name="extType">
  <complexContent>
      <extension base="me:baseType">
      <sequence>
        <element name="num_two" type="int" />
      </sequence>
      </extension>
  </complexContent>
</complexType>

</schema>
END_SCHEMA

my $xc = XML::Compile::Schema->new( $schema_txt, allow_undeclared => 1 );


my $skip_elem;
my $hook_ok = sub {
    my ( $elem, $args, $path, $type, $r ) = @_;

    if ( not($skip_elem) and my $xsi_type = get_xsi_type($elem) ) {
        my $type_reader = $xc->compileType(
            READER  => $xsi_type,
            element => pack_type( $elem->namespaceURI, $elem->localname ),
        );
        $skip_elem = 1;
        return $type_reader->($elem);
    }
    undef $skip_elem;
    return $r->($elem);
};
my $hook_infinite = sub {
    my ( $elem, $args, $path, $type, $r ) = @_;

    if ( my $xsi_type = get_xsi_type($elem) ) {
        my $type_reader = $xc->compileType(
            READER  => $xsi_type,
            element => pack_type( $elem->namespaceURI, $elem->localname ),
        );
        return $type_reader->($elem);
    }
    return $r->($elem);
};
$xc->addHook(
    action  => 'READER',
    replace => $hook_ok,
);

my $reader = $xc->compile( READER => pack_type( $TEST_NS, 'env' ) );
my $doc = XML::LibXML->new->parse_string(<<"END_XML");
<me:env xmlns:me="$TEST_NS" xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance">
    <me:alfa xsi:type="me:extType">
<me:num_one>12</me:num_one>
<me:num_two>13</me:num_two>
    </me:alfa>
    <me:beta xsi:type="me:extType">
<me:num_one>14</me:num_one>
<me:num_two>15</me:num_two>
    </me:beta>
</me:env>
END_XML

my $struct = $reader->( $doc->documentElement );
pp($struct);

















2015-11-23 9:55 GMT+01:00 Mark Overmeer <mark at overmeer.net>:

>
> Hi Roman,
>
> * Roman Daniel (roman.daniel at davosro.cz) [151122 19:08]:
> > I have a schema (VMWare webservices, with urn:vim25 namespace) which have
> > some elements defined as xsd:anyType
>
> >          <element name="val" type="xsd:anyType" />
> >          <val xsi:type="ManagedEntityStatus">green</val>
>
> The ugliness of anyType combined with the ugliness of xsi:type!  Argggg!
> Haven't seen that before.
>
> > When I read XML elements based on such schema, those elements (val
> above),
> > are left untranslated and appears in the resulting structure
> > as original XML::LibXML elements. I understand this and do not expect
> > XML::Compile to behave differently.
>
> That's the default behavior of the anyType handler, yes.
>
> > What I want is to translate those "xsd:anyType" elements manually, either
> > via hook or by traversing the result structure and compiling those
> > untranslated elements with another call of a new reader.
>
> There exists logic to decode <any> elements, but not for anyType elements.
> Yes, a hook will probably work.
>
> > Since all of these elements always come with a type I thought that would
> be
> > nice to create a new reader for the type,
> > say "{urn:vim25}ManagedEntityStatus".
> >
> > my $reader = $xml_schema->compile(READER =>
> > "{urn:vim25}ManagedEntityStatus");
>
> Why are you not using
>     my $r = $schema->reader("{urn:vim25}ManagedEntityStatus");
>
> or  $schema->addPrefixes(vm => 'urn:vim25');
>     my $r = $schema->reader("vm:ManagedEntityStatus");
>
> It will cash the compiled handlers for you.
>
> > with the meaning read the element, translate its content and attributes
> > according to XML type, regardless of element name.
> > But to create such $reader is not possible.
>
> Of course it is possible... whether it is supported is a different
> question ;-)
>
> > So the only other way I can imagine is to:
> >
> > 1) add a new fake XML schema to my $xml_schema
> >
> > <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
> >     xmlns:tns="urn:myNonsenseSchema"
> >     xmlns:orig="urn:vim25"
> >     targetNamespace="urn:myNonsenseSchema"
> >     elementFormDefault="qualified">
> > <xsd:element name="ManagedEntityStatus" type="orig:ManagedEntityStatus"/>
> > </xsd:element>
> >
> > creating an element for the type.
>
> This is the trick used by $schema->compileType
> Don't hessitate to ask for more details.
> --
> Regards,
>
>                MarkOv
>
> ------------------------------------------------------------------------
>        Mark Overmeer MSc                                MARKOV Solutions
>        Mark at Overmeer.net                          solutions at overmeer.net
> http://Mark.Overmeer.net                   http://solutions.overmeer.net
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.scsys.co.uk/pipermail/xml-compile/attachments/20151126/4434253a/attachment.htm>


More information about the Xml-compile mailing list