Alex van den Hoogen
Alex van den Hoogen

Reputation: 754

JAXB Unmarshalling and inheritence

Situation
For a project we have to deal with quite a large number of XSDs. A lot of these schema's are GML or related to GML in some way or the other. We use JAXB2 to provide us with Java classes and to map XML's to Java objects. This has been frustrating but with projects like the OGC Bindings project, we have come a long way.

Problem
When unmarshalling the data we are currently, seemingly having a problem with inheritence in JAXB2. When we are unmarshalling a collection, this collection is not being filled. So I used a ValidationEventCollector as an Handler to inspect the code. Although there are no Exceptions thrown, the ValidationEventCollector will still give me the error: unexpected element ...

The relevant code
We are using GML 3.1.1 with the recommended bindings and CityGML with the following binding: bindings.xjb

<jaxb:bindings version="1.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns:inheritance="http://jaxb2-commons.dev.java.net/basic/inheritance"
    xmlns:annox="http://annox.dev.java.net" jaxb:extensionBindingPrefixes="xjc inheritance">

    <jaxb:globalBindings
        fixedAttributeAsConstantProperty="false" typesafeEnumBase="xs:string"
        typesafeEnumMemberName="generateName" generateIsSetMethod="true">
        <xjc:noValidator />
        <xjc:noValidatingUnmarshaller />
    </jaxb:globalBindings>

    <jaxb:bindings schemaLocation="citygml-2.0/2.0/cityGMLBase.xsd"
        node="/xs:schema">
        <jaxb:schemaBindings>
            <jaxb:package name="net.opengis.citygml.v_2_0" />
        </jaxb:schemaBindings>
        <jaxb:bindings node="xs:complexType[@name='CityModelType']">
            <annox:annotate>
                <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement"
                    name="CityModel"
                    namespace="http://www.opengis.net/citygml/2.0" />
            </annox:annotate>
        </jaxb:bindings>
    </jaxb:bindings>

</jaxb:bindings>

We also have tried <xjc:simple /> but that didn't make a difference. Besides the bindings we have a catalog that works (regarding other schema's) and duplicate ObjectFactory classes are deleted before compilation.

The following (part of) XML is used for unmarshalling

<?xml version="1.0" encoding="UTF-8"?>
<cit:CityModel xmlns:gml="http://www.opengis.net/gml" xmlns:cit="http://www.opengis.net/citygml/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" gml:id="example">
    <gml:boundedBy>
        <gml:Envelope srsName="http://www.opengis.net/def/crs/EPSG/0/28992">
            <gml:lowerCorner>144280.193 414155.258</gml:lowerCorner>
            <gml:upperCorner>147300.873 416928.884</gml:upperCorner>
        </gml:Envelope>
    </gml:boundedBy>
    <cit:cityObjectMember>
        ...
    </cit:cityObjectMember>
    <cit:cityObjectMember>
        ...
    </cit:cityObjectMember>
    <cit:cityObjectMember>
        ...
    </cit:cityObjectMember>
</cit:CityModel>

The cityObjectMember (where the issues are) code in the ObjectFactory class in net.opengis.citygml.v_2_0

@XmlElementDecl(namespace = "http://www.opengis.net/citygml/2.0", name = "cityObjectMember", substitutionHeadNamespace = "http://www.opengis.net/gml", substitutionHeadName = "featureMember")
public JAXBElement<FeaturePropertyType> createCityObjectMember(FeaturePropertyType value) {
    return new JAXBElement<FeaturePropertyType>(_CityObjectMember_QNAME, FeaturePropertyType.class, null, value);
}

With the according code for unmarshalling

Unmarshaller um = JAXBContext.newInstance("net.opengis.citygml.v_2_0:net.opengis.gml").createUnmarshaller();

JAXB2ValidationEventCollector vec = new JAXB2ValidationEventCollector();
um.setEventHandler(vec);

Object unmarshalled = um.unmarshal(this.getFile());

// Check for errors, when there are (validation) errors, throw them to System.err.
if (vec.hasEvents()) {
    for (ValidationEvent ve : vec.getEvents()) {
        System.err.println(String.format("[Line: %d Column: %d] %s", ve.getLocator().getLineNumber(), 
                ve.getLocator().getColumnNumber(), ve.getMessage()));
    }
}

and as the ValidationEventCollector:

import javax.xml.bind.ValidationEvent;
import javax.xml.bind.util.ValidationEventCollector;

class JAXB2ValidationEventCollector extends ValidationEventCollector {

    @Override
    public boolean handleEvent(ValidationEvent event) {
        super.handleEvent(event);
        return true;
    }
}

The issues!
First of all, like I already mentioned, the cityObjectMember's are not getting parsed. Which is the root of the problem. That's why I added the eventHandler to the unmarshaller. This resulted in the following error:

[Line: 56 Column: 27] unexpected element (uri:"http://www.opengis.net/citygml/2.0", local:"cityObjectMember"). Expected elements are <{http://www.opengis.net/gml}ellipsoidName>,<{http://www.opengis.net/gml}meridianName>,<{http://www.opengis.net/gml}featureMember>,<{http://www.opengis.net/citygml/2.0}_GenericApplicationPropertyOfCityModel>,<{http://www.opengis.net/gml}parameterName>,<{http://www.opengis.net/gml}groupName>,<{http://www.opengis.net/gml}srsName>,<{http://www.opengis.net/gml}metaDataProperty>,<{http://www.opengis.net/gml}priorityLocation>,<{http://www.opengis.net/gml}location>,<{http://www.opengis.net/gml}coordinateOperationName>,<{http://www.opengis.net/gml}datumName>,<{http://www.opengis.net/gml}featureMembers>,<{http://www.opengis.net/gml}methodName>,<{http://www.opengis.net/gml}boundedBy>,<{http://www.opengis.net/gml}csName>,<{http://www.opengis.net/gml}description>,<{http://www.opengis.net/gml}name>

and to be honest... I'm quite stuck now. I don't how to proceed any further. So if anyone knows the answer or the root of the problem, that would be really nice. Thanks :).

Upvotes: 1

Views: 603

Answers (1)

lexicore
lexicore

Reputation: 43651

I'm the author of the "OGC Schemas and Tools Project". Your bindings seem correct, object factory looks fine, it should work. The core issue you're experiencing is that gml:featureMember is not substituted by cit:cityObjectMember for some reason. It is hard to say why. I'd attack the problem by trying to analyse the runtime model that JAXB creates for your classes. Somehow your @XmlElementDecl on createCityObjectMember gets ignored.

My approach for this kind of things is usually to take something which does not work and something which does and then try to reduce the gap thus identifying the cutting edge which is essentially a problem.

In this case I would take the code and build a minimal example where substitution works. Then I'd try to reduce yor not working example, stripping it of irrelevant properties and so on. Finally you'll get the edge which will give you a clue of what's wrong.

My guess, without going deeper would be scopes. Maybe the substitution of gml:featureMemeber is somehow limited and your substitution does not match the "limit". Hard to say without debugging.

Feel free to contact me (valikov at gmx net), provide your mappings (or the whole project), I'll try to help you out.

Upvotes: 2

Related Questions