10 Replies Latest reply on Oct 18, 2006 1:37 PM by starksm64

    Using annotations to define schema mappings

      Since:

      1) obviously we can't define schema annotations on the javaee schemas
      2) writing handlers (objectmodelfactory, schemabindinginitialzer) by hand
      is laborious and error prone
      3) none of this information on the mapping is exposed to tools

      I've created an alternate way of mapping schemas to object models.
      This involves the use of annotations.

      In fact, it goes a bit beyond that. In many cases, the annotations
      are not required since it will make a "good guess" at the mapping
      using conventions and looking at the object model's class tree.

      Here's an example showing what I've got working so far
      which caters for about 80-90% of use cases:

      /*
      * JBoss, Home of Professional Open Source
      * Copyright 2006, JBoss Inc., and individual contributors as indicated
      * by the @authors tag. See the copyright.txt in the distribution for a
      * full listing of individual contributors.
      *
      * This is free software; you can redistribute it and/or modify it
      * under the terms of the GNU Lesser General Public License as
      * published by the Free Software Foundation; either version 2.1 of
      * the License, or (at your option) any later version.
      *
      * This software is distributed in the hope that it will be useful,
      * but WITHOUT ANY WARRANTY; without even the implied warranty of
      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      * Lesser General Public License for more details.
      *
      * You should have received a copy of the GNU Lesser General Public
      * License along with this software; if not, write to the Free
      * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
      * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
      */
      package org.jboss.test.xml.builder.support;
      
      import java.util.Collection;
      
      import org.jboss.xb.binding.annotations.Schema;
      import org.jboss.xb.binding.annotations.SchemaAttribute;
      import org.jboss.xb.binding.annotations.SchemaProperty;
      import org.jboss.xb.binding.annotations.SchemaType;
      import org.jboss.xb.binding.annotations.SchemaValue;
      
      /**
       * Example.
      
      Example of using annotations to map the schema
      JBossXBBuilder.build(schemaBinding, Example.class);
      
      This will look at Example.class and the classes
      it references, e.g. AnotherExample.class
      
      It will also look at parameterized classes,
      e.g. if there was a property of type
      Collection<AnotherExample>
      it will also look at AnotherExample.
      
       * @author <a href="adrian@jboss.com">Adrian Brock</a>
       * @version $Revision: 1.1 $
       */
      
      // This is only processed on the root type
      // passed to the builder
      @Schema(ignoreUnresolvedFieldOrClass=true)
      
      // The xsd name
      // <xsd:complex-type name="theTypeNameInTheXSD"/>
      @SchemaType(name="theTypeNameInTheXSD")
      
      // Alternatively an element and can take an namespace
      // No namespace means it will search for the name
      // in any namespace in the schema
      // @SchemaElement(name="foo", namespace="urn:jboss.com:bar")
      
      // The type/example is optional
      // currently it will search for an element or type
      // with name "example". (lowercase first letter)
      public class Example
      {
       private String someAttribute;
      
       private Collection<String> someCollection;
      
       private AnotherExample someProperty;
      
       private String someValue;
      
       public String getSomeAttribute()
       {
       return someAttribute;
       }
      
       // maps the xml attribute name <x id="blah"/>
       @SchemaAttribute(name="id")
       public void setSomeAttribute(String someAttribute)
       {
       this.someAttribute = someAttribute;
       }
      
       public Collection<String> getSomeCollection()
       {
       return someCollection;
       }
      
       // maps the xml child name for a collection
       // <parent>
       // <child/>
       // <child/>
       // </parent>
       @SchemaProperty(name="child")
       public void setSomeCollection(Collection<String> someCollection)
       {
       this.someCollection = someCollection;
       }
      
       public AnotherExample getSomeProperty()
       {
       return someProperty;
       }
      
       // maps the xml child name for a non-collection
       // <parent>
       // <child/>
       // </parent>
       @SchemaProperty(name="child")
       public void setSomeProperty(AnotherExample someProperty)
       {
       this.someProperty = someProperty;
       }
      
       public String getSomeValue()
       {
       return someValue;
       }
      
       // A tag to say this takes the character data
       // <x>this</x>
       @SchemaValue
       public void setSomeValue(String someValue)
       {
       this.someValue = someValue;
       }
      }
      


      This work is currently uncommitted to SVN.

        • 1. Re: Using annotations to define schema mappings
          aloubyansky

          That's great. But maybe we should mimic JAXB2 annotations or at least take them into account so that we can easily map them to our own.

          • 2. Re: Using annotations to define schema mappings
            starksm64

            Yes, and we also need to support the ability to pickup the schema annotations from an external file. The jaxb mechanism for this has an xpath syntax where the elements that should be annotated are matched based on the expression.

            An example from the jaxb2 spec, consider the following schema and external binding file.
            Source Schema: A.xsd:

            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
             xmlns:ens="http://example.com/ns"
             targetNamespace="http://example.com/ns">
             <xs:complexType name="aType">
             <xs:sequence>
             <xs:element name="foo" type="xs:int"/>
             </xs:sequence>
             <xs:attribute name="bar" type="xs:int"/>
             </xs:complexType>
             <xs:element name="root" type="ens:aType"/>
            </xs:schema>
            


            External binding declarations file:
            <jaxb:bindingsxmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
             xmlns:xs="http://www.w3.org/2001/XMLSchema"
             version="1.0">
             <jaxb:bindings schemaLocation='A.xs'>
             <jaxb:bindings node="//xs:complexType[@name=?aType?]?>
             <jaxb:class name="customNameType"/>
             <jaxb:bindings node=?.//xs:element[@name=?foo?]?>
             <jaxb:property name="customFoo"/>
             </jaxb:bindings>
             <jaxb:bindings node=?./xs:attribute[@name=?bar?]?>
             <jaxb:property name="customBar"/>
             </jaxb:bindings>
             </jaxb:bindings>
             </jaxb:bindings>
            </jaxb:bindings>
            


            Conceptually, the combination of the source schema and external binding file above are the equivalent of the following inline annotated schema.
            <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
             xmlns:ens="http://example.com/ns"
             targetNamespace="http://example.com/ns"
             xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
             jaxb:version="1.0">
             <xs:complexType name="aType">
             <xs:annotation>
             <xs:appinfo>
             <jaxb:class name="customNameType"/>
             </xs:appinfo>
             </xs:annotation>
             <xs:sequence>
             <xs:element name="foo" type="xs:int">
             <xs:annotation>
             <xs:appinfo>
             <jaxb:property name="customFoo"/>
             </xs:appinfo>
             </xs:annotation>
             </xs:element>
             </xs:sequence>
             <xs:attribute name="bar" type="xs:int">
             <xs:annotation>
             <xs:appinfo>
             <jaxb:property name="customBar"/>
             </xs:appinfo>
             </xs:annotation>
             </xs:attribute>
             </xs:complexType>
             <xs:element name="root" type="ens:aType"/>
            </xs:schema>
            



            • 3. Re: Using annotations to define schema mappings

              I think overridding annotations would be relatively trivial in XsdBinder.

              I don't know that it could use real XPath though since Xerces' XSModel
              is not a DOM model. It is more what the xml specs call an
              "information set", but don't know it is formal enough to apply this:
              http://www.w3.org/TR/xpath#infoset

              • 4. Re: Using annotations to define schema mappings
                jason.greene

                 

                "scott.stark@jboss.org" wrote:
                Yes, and we also need to support the ability to pickup the schema annotations from an external file. The jaxb mechanism for this has an xpath syntax where the elements that should be annotated are matched based on the expression.

                An example from the jaxb2 spec, consider the following schema and external binding file.
                Source Schema: A.xsd:



                FYI, JAXB only uses that biding file for generating java files with annotations, it does not use them for runtime processing. So the whole thing is designed around a static java to xml mapping.

                -Jason

                • 5. Re: Using annotations to define schema mappings
                  jason.greene

                   

                  "adrian@jboss.org" wrote:
                  I think overridding annotations would be relatively trivial in XsdBinder.

                  I don't know that it could use real XPath though since Xerces' XSModel
                  is not a DOM model. It is more what the xml specs call an
                  "information set", but don't know it is formal enough to apply this:
                  http://www.w3.org/TR/xpath#infoset


                  Well you could parse the xsd into dom first, then apply the xpath, then load the modified dom into an XSModel.

                  -Jason

                  • 6. Re: Using annotations to define schema mappings
                    starksm64

                    So should we look at taking the sun jaxb xsd metamodel and move away from the xerces specific code or is it trading one problem for another?

                    • 7. Re: Using annotations to define schema mappings
                      jason.greene

                      I checked and JAXB does the same thing to support the binding file. xjc loads it into a DOM tree, then applies the bindings using xpath, then parses the modified DOM.

                      To answer your question though, I think XSModel is flawed in a number of ways, and IMO we need to move away from it.

                      These include


                      1. not very extensible
                      2. incomplete / buggy
                      3. tied to xerces
                      4. language neutral API that is a bitch to work with in java
                      5. can't use it to write schema


                        The sun jaxb implementation has a seperate model for reading a writing. They externalized their reading implementation, its called XSOM:

                        https://xsom.dev.java.net/

                        See the design overview here:
                        https://xsom.dev.java.net/implementation.html

                        For writing they basically implement a light weight model that only writes what they generate.

                        I think we should look into switching to XSOM because it doesn't suffer from problems 1-4. It is quite efficient, uses SAX, and it supports customization using visitors.

                        Although, I don't think this solves the overall problem in the java world, which is the lack of a standard XML Schema api that supports both reading and writing. The only thing close to it I have seen is the eclipse schema model, but it is tied to tons of bloated dependencies.

                        -Jason


                      • 8. Re: Using annotations to define schema mappings
                        jason.greene

                         

                        "alex.loubyansky@jboss.com" wrote:
                        That's great. But maybe we should mimic JAXB2 annotations or at least take them into account so that we can easily map them to our own.


                        One of the nice things about JAXB's decesion to only process schema at compile time, is that it is way more efficient then to load and process 50 xml schema files. If the goal is to support JAXB behavior, and to be competitive, then there would need to be some way to do an annotation only binding mode.

                        -Jason

                        • 9. Re: Using annotations to define schema mappings
                          weston.price

                          Was their any conclusion to this thread? What was the decided approach?

                          • 10. Re: Using annotations to define schema mappings
                            starksm64

                            The conclusion is waiting for review of the initial checkin from Adrian. Whatever that is should support the jaxb2 annotations, and we do need the ability to define schema annotations external to the schema file.