users@jaxb.java.net

Re: Mapping of SAX events to corresponding JAXB classes

From: Aleksei Valikov <valikov_at_gmx.net>
Date: Sat, 29 Sep 2007 01:20:01 +0200

Hi.

> Hello everyone -
>
> I have generated JAXB classes from a given XSD schema. Since I have to deal
> with potentially very large XML instance documents, I am using a SAX parser
> to read those documents. I have successfully implemented a partial
> unmarshalling according to the sample provided by the JAXB RI 2.1 package.
> That's working fine so far.
>
> For an XML start element the SAX parser calls the following method (well,
> this is nothing new to you, of course):
>
> public void startElement(String uri, String localName, String qName,
> Attributes atts) throws SAXException {
> // things to do
> }
>
> Now my question: Is it possible - just based on these values provided by the
> SAX parser - to get to know which JAXB class would be instantiated? So, is
> there a way to say: "Ok, this start element would be mapped to this JAXB
> class", without actually unmarshalling the whole XML chunk?

This is an interesting idea.

Correct me if I go wrong somewhere. What you want to do is the
following: having a QName you want to determine onto which
schema-derived class it maps.
This information is contained in the schema-derived classes and the
object factory class, you just have to read the annotations allright.

First of all, you can simply read the annotations from the
ObjectFactory class you have generated along with your schema-derived
classes. You'll need to spot all the methods like:

    @XmlElementDecl(namespace = "", name = "purchaseOrder")
    public JAXBElement<PurchaseOrderType>
createPurchaseOrder(PurchaseOrderType value) {
        return new
JAXBElement<PurchaseOrderType>(_PurchaseOrder_QNAME,
PurchaseOrderType.class, null, value);
    }

I mean, methods annotated with @XmlElementDecl. From the annotation
and the generic return type you could derive the QName/schema-derived
class mapping.

A second approach is to use the jaxb reflection.
Consider the following code:

public static TypeInfoSet<Type, Class, Field, Method> create(
                        Class... classes) throws IllegalAnnotationsException {

                ModelBuilder<Type, Class, Field, Method> builder = new
ModelBuilder<Type, Class, Field, Method>(
                                new RuntimeInlineAnnotationReader(), Navigator.REFLECTION,
                                Collections.<Class, Class> emptyMap(), null);
                IllegalAnnotationsException.Builder errorHandler = new
IllegalAnnotationsException.Builder();
                builder.setErrorHandler(errorHandler);
                for (Class c : classes)
                        builder.getTypeInfo(new Ref<Type, Class>(Navigator.REFLECTION
                                        .use(c)));
                errorHandler.check();

                return builder.link();
        }

        public void testReflection() throws Exception {

                TypeInfoSet<Type, Class, Field, Method> set = create(ObjectFactory.class);

                for (ElementInfo<Type, Class> elementInfo : set.getAllElements()) {
                        final QName elementName = elementInfo.getElementName();
                        final Type type = elementInfo.getContentInMemoryType();
                        logger.debug("Element [" + elementName + "] is mapped onto ["
                                        + type + "].");
                }

        }

For the purchase order schema this prints out:

<Element [purchaseOrder] is mapped onto [class
org.jvnet.hyperjaxb3.ejb.tests.po.PurchaseOrderType].>
<Element [comment] is mapped onto [class java.lang.String].>

Is this the information you're looking for?

If you want to construct the TypeInfoSet from the context path
("com.acme.foo:com.acme.bar"), I guess you'll first have to parse this
string to get the list of ObjectFactory classes for the context
packages.

Bye.
/lexi