3

I am implementing the OGC Web Feature Service and part of that is creating a feature schema that will inherit from the OGC schema. My service marshals the XML fine, but the client is unable to unmarshal the XML. I wrote a tester that illustrates the problem:

...
ObjectFactory wfsfactory = new ObjectFactory();
net.opengis.gml.v_3_1_1.ObjectFactory gmlfactory = new net.opengis.gml.v_3_1_1.ObjectFactory();
com.example.ObjectFactory exampleFactory = new com.example.ObjectFactory();
OgcJaxbManager manager = OgcJaxbManager.getInstance();
FeatureCollectionType featureCollection = wfsfactory
        .createFeatureCollectionType();
FeaturePropertyType prop = gmlfactory.createFeaturePropertyType();
prop.setFeature(exampleFactory.createFoo(exampleFactory.createFoo()));
featureCollection.setFeatureMember(Arrays.asList(prop));
//marshal to XML
String xml = manager.marshal(wfsfactory
        .createFeatureCollection(featureCollection));
log.info(xml);
//unmarshal back to object
FeatureCollectionType afterMarshal = (FeatureCollectionType) manager
        .unmarshal(xml);
JAXBElement<? extends AbstractFeatureType> feature = afterMarshal
        .getFeatureMember().get(0).getFeature();
if (feature == null) {
    log.info("null");
} else {
    log.info("not null");
}
...

Output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:FeatureCollection xmlns:ns2="http://www.w3.org/1999/xlink" xmlns:ns1="http://www.opengis.net/gml" xmlns:ns4="http://www.opengis.net/wfs" xmlns:ns3="http://www.w3.org/2001/SMIL20/" xmlns:ns9="http://www.opengis.net/wms" xmlns:ns5="http://www.opengis.net/ows/1.1" xmlns:ns6="http://www.opengis.net/ogc" xmlns:ns10="http://example.com" xmlns:ns7="http://www.opengis.net/ows" xmlns:ns11="http://www.w3.org/2001/SMIL20/Language" xmlns:ns8="http://www.opengis.net/wcs/1.1.1">
    <ns1:featureMember>
        <ns10:foo>
            <ns10:bar>0</ns10:bar>
        </ns10:foo>
    </ns1:featureMember>
</ns4:FeatureCollection>

null

Here is the OGC schema I am extending:

...
    <element name="FeatureCollection" type="gml:FeatureCollectionType" substitutionGroup="gml:_Feature"/>
    <!-- =========================================================== -->
    <complexType name="FeatureCollectionType">
        <annotation>
            <documentation>Concrete generic feature collection.</documentation>
        </annotation>
        <complexContent>
            <extension base="gml:AbstractFeatureCollectionType"/>
        </complexContent>
    </complexType>
    <!-- ===========================================================   -->
    <complexType name="AbstractFeatureCollectionType" abstract="true">
        <annotation>
            <documentation>A feature collection contains zero or more features.</documentation>
        </annotation>
        <complexContent>
            <extension base="gml:AbstractFeatureType">
                <sequence>
                    <element ref="gml:featureMember" minOccurs="0" maxOccurs="unbounded"/>
                    <element ref="gml:featureMembers" minOccurs="0"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>
    <!-- ===== property for feature association ==== -->
    <element name="featureMember" type="gml:FeaturePropertyType"/>
    <!-- ============================================================== -->
    <complexType name="FeaturePropertyType">
        <annotation>
            <documentation>Container for a feature - follow gml:AssociationType pattern.</documentation>
        </annotation>
        <sequence minOccurs="0">
            <element ref="gml:_Feature"/>
        </sequence>
        <attributeGroup ref="gml:AssociationAttributeGroup"/>
    </complexType>
    <!-- ============================================================== -->
    <element name="_Feature" type="gml:AbstractFeatureType" abstract="true" substitutionGroup="gml:_GML"/>
    <!-- =========================================================== -->
    <complexType name="AbstractFeatureType" abstract="true">
        <annotation>
            <documentation>An abstract feature provides a set of common properties, including id, metaDataProperty, name and description inherited from AbstractGMLType, plus boundedBy.    A concrete feature type must derive from this type and specify additional  properties in an application schema. A feature must possess an identifying attribute ('id' - 'fid' has been deprecated).</documentation>
        </annotation>
        <complexContent>
            <extension base="gml:AbstractGMLType">
                <sequence>
                    <element ref="gml:boundedBy" minOccurs="0"/>
                    <element ref="gml:location" minOccurs="0">
                        <annotation>
                            <appinfo>deprecated</appinfo>
                            <documentation>deprecated in GML version 3.1</documentation>
                        </annotation>
                    </element>
                    <!-- additional properties must be specified in an application schema -->
                </sequence>
            </extension>
        </complexContent>
    </complexType>
...

Here is my schema:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsd:schema version="1.0" targetNamespace="http://example.com"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:example="http://example.com"
    xmlns:gml="http://www.opengis.net/gml"
    elementFormDefault="qualified">

    <xsd:import namespace="http://www.opengis.net/gml"
        schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/gml.xsd"/>

    <xsd:element name="foo" type="example:foo"
        substitutionGroup="gml:_Feature"/>

    <xsd:complexType name="foo">
        <xsd:complexContent>
            <xsd:extension base="gml:AbstractFeatureType">
                <xsd:sequence>
                    <xsd:element name="bar" type="xsd:string" minOccurs="0"/>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>

</xsd:schema>

Here is the POJO that xjc produces:

package com.example;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "foo", propOrder = { "bar" })
public class Foo extends AbstractFeatureType {

    protected String bar;

    ...

Any help would be greatly appreciated.

Brian Clements
  • 3,787
  • 1
  • 25
  • 26
  • And what happens when you try? – skaffman Dec 14 '10 at 22:05
  • The feature property is null after the XML is unmarshaled. It should contain the Foo feature that was put in before it was marshaled. – Brian Clements Dec 14 '10 at 22:07
  • By the way, are you aware of http://confluence.highsource.org/display/OGCS/Home ? – lexicore Dec 15 '10 at 14:29
  • Yes. We are currently using the org.jvnet.ogc schema jars. Unfortunately, this is the part of the WFS spec that requires you to do schema inheritance to return the list of features defined by the local feature types (my schema above). I'm using the binding files from the schema jars and `xjc` seems to work just find and the marshaller doesn't complain. It's just when the context reads the XML back in that there is a problem. – Brian Clements Dec 15 '10 at 21:27

1 Answers1

4

I made this work, with two caveats.

You didn't post the ObjectContext for com.example, so I used a hand-coded ObjectContext. It is critical to include the

substitutionHeadNamespace="http://www.opengis.net/gml" and substitutionHeadName="_Feature"

values in the @XmlElementDecl for the factory method, otherwise I see the same symptoms, i.e. marshalling OK, unmarshalling is empty, but no exceptions.

com.example.ObjectContext looks like this:

package com.example;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;

@XmlRegistry
public class ObjectFactory {

    public ObjectFactory() { }

    public Foo createFoo() { return new Foo(); }

    @XmlElementDecl(namespace="http://example.com",
            name="foo",
            substitutionHeadNamespace="http://www.opengis.net/gml",
            substitutionHeadName="_Feature")
    public JAXBElement<Foo> createFoo(Foo foo) {
        return new JAXBElement<Foo>(new QName("http://example.com", "foo"), Foo.class, foo);   
    }
}

com.example.Foo looks like this, including main:

package com.example;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlAccessType;

import net.opengis.gml.v_3_1_1.AbstractFeatureType;
import net.opengis.gml.v_3_1_1.FeaturePropertyType;
import net.opengis.wfs.v_1_1_0.FeatureCollectionType;
import net.opengis.wfs.v_1_1_0.ObjectFactory;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "foo", propOrder = { "bar" })
public class Foo extends AbstractFeatureType {

    @XmlElement
    protected String bar = "0";

    @Override
    public Object createNewInstance() {
        return new Foo();
    }

    public static void main(String[] args) throws JAXBException {
        ObjectFactory wfsfactory = new ObjectFactory();
        net.opengis.gml.v_3_1_1.ObjectFactory gmlfactory = new net.opengis.gml.v_3_1_1.ObjectFactory();
        com.example.ObjectFactory exampleFactory = new com.example.ObjectFactory();
        FeatureCollectionType featureCollection = wfsfactory
                .createFeatureCollectionType();
        FeaturePropertyType prop = gmlfactory.createFeaturePropertyType();
        prop.setFeature(exampleFactory.createFoo(exampleFactory.createFoo()));
        featureCollection.setFeatureMember(Arrays.asList(prop));
        //marshal to XML
        JAXBContext ctx = JAXBContext.newInstance(ObjectFactory.class, net.opengis.gml.v_3_1_1.ObjectFactory.class, com.example.ObjectFactory.class);

        StringWriter sw =new StringWriter();
        Marshaller marshaller = ctx.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(wfsfactory.createFeatureCollection(featureCollection), sw);
        System.out.println(sw.toString());

        //unmarshal back to object
        JAXBElement<FeatureCollectionType> afterMarshal = (JAXBElement<FeatureCollectionType>)
            ctx.createUnmarshaller().unmarshal(new StringReader(sw.toString()));

        JAXBElement<? extends AbstractFeatureType> feature = afterMarshal
                .getValue().getFeatureMember().get(0).getFeature();
        if (feature == null) {
            System.out.println("null");
        } else {
            System.out.println("not null");
        }
    }
}

And this is the output I get:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:FeatureCollection xmlns:ns2="http://www.w3.org/1999/xlink" xmlns:ns1="http://www.opengis.net/gml" xmlns:ns4="http://example.com" xmlns:ns3="http://www.opengis.net/wfs" xmlns:ns5="http://www.w3.org/2001/SMIL20/" xmlns:ns6="http://www.opengis.net/ogc" xmlns:ns7="http://www.opengis.net/ows" xmlns:ns8="http://www.w3.org/2001/SMIL20/Language">
    <ns1:featureMember>
        <ns4:foo>
            <ns4:bar>0</ns4:bar>
        </ns4:foo>
    </ns1:featureMember>
</ns3:FeatureCollection>

not null

Good luck going forward!

JesperSM
  • 1,418
  • 1
  • 11
  • 15