hale vs. App-Schema

This section will go into more detail on points where the behavior of App-Schema differs from hale's.

Feature Type mappings

The core of App-Schema's mapping file is the definition of one or more Feature Type Mappings. Each Feature Type Mapping specifies the name of an element in the target application schema, which defines the target feature type name, and a set of Attribute Mappings, controlling how the properties of the target type will be populated from the source.

Roughly speaking, the plug-in translates type relations to Feature Type Mappings and property relations to Attribute Mappings. The translation logic depends on the actual relation used.

In particular:

Note that a Join is handled differently in hale and App-Schema:

It is also worth mentioning that, although in hale multiple join conditions can be specified for each source type, App-Schema only supports single-condition joins; thus, in order to be translateable to a GeoServer App-Schema configuration, an alignment should not contain more than one join condition for the same source type.
See the section on Feature Chaining for more details.

Another limitation worth noting is that all target types must belong to the namespace of the target schema. For example, if the imported target schema is the INSPIRE application schema for Land Cover Vector, the target element of all generated Feature Type mappings must belong to the namespace http://inspire.ec.europa.eu/schemas/lcv/3.0.
This restriction stems from the fact that currently the alignment is translated to a single App-Schema mapping configuration, driving a single App-Schema data store, which in GeoServer must belong to a single namespace. This in practice means that all feature types served by the store must be in its namespace. This does not mean that attribute mappings cannot refer to types belonging to different namespaces: the restriction only applies to the feature types the store will serve.
The limitation may be removed in a future release.

Instance identity and merging

App-Schema determines the identity of a feature by looking at a special Attribute Mapping containing the <idExpression> directive, which defines a CQL expression that will be used to set the gml:id attribute of the feature (more details on this can be found in the official GeoServer documentation).

Note that source instances having the same gml:id are automatically merged by App-Schema. The user can control the instance identity by defining a property relation that targets the gml:id attribute of the target feature type: the plug-in will translate it to the special <idExpression> directive, rather than treating it as a regular attribute mapping. If no <idExpression> mapping is available, App-Schema will automatically generate a unique gml:id for each source instance.

This is another important point where hale and App-Schema differ:

This is particularly relevant when translating an alignment that contains one or more Merge relations. To reproduce the same behavior in hale and App-Schema, the user needs to construct the gml:id from a combination of the source properties configured as the properties to merge on in hale (e.g. using a Formatted String transformation).

Mapping geometries

Special care should be taken when the target of a property relation is a geometry. For ordinary GML geometry properties (i.e. whose type inherits from gml:GeometryPropertyType), the mapping in both hale and App-Schema is as simple as defining a Rename relation between the source property (be it a geometry column in a database, or a geometric attribute in a shapefile) and the target property.

However, if the target geometry property is not a gml:GeometryPropertyType, in order to have the plug-in generate a working App-Schema mapping, the user must explicitly target the concrete geometry type contained in the geometry property. An example is the gmd:EX_BoundingPolygon_Type defined in the OGC Geographic MetaData (GMD) markup language, whose property gmd:polygon contains a geometry, but is not a gml:GeometryPropertyType. The correct target property to select here to encode e.g. a MultiPolygon would be gmd:polygon/gml:AbstractGeometry/gml:MultiSurface, and not simply gmd:polygon.

Another point that deserves attention is the generation of GML IDs for geometries. In GML 3.x, all geometries must have a gml:id attribute, even though often its actual value is of no interest to the user. hale satisfies this requirement by automatically generating GML IDs on data export. However, the App-Schema plug-in requires an explicit mapping for the geometry's gml:id attribute to generate a correct App-Schema configuration. This limitation will likely be removed in a future release.

Mapping nillable mandatory elements

There are cases when a complex type contains a lot of elements that are mandatory, but can be nil, i.e. have an attribute xsi:nil="true". A famous example is GeographicalNameType, from the INSPIRE application schema Geographical Names.

If no mapping is present in the alignment for a nillable mandatory element, upon data export hale automatically creates an empty element with an xsi:nil="true" attribute. On the contrary, App-Schema does not handle this situation automatically, but requires the user to explicitly map the xsi:nil attribute in the configuration file.

The App-Schema plug-in for hale strives to alleviate this nuisance by tuning the generated Attribute Mappings of nillable elements for which a mapping has been defined in the alignment. The tuning results in an xsi:nil="true" attribute being automatically added to the element when it has no value.

However, this only applies to mapped elements. For nillable elements that do not participate in the alignment, the user is required to provide a mapping, to make the generation of the necessary App-Schema configuration possible. hale does not allow to map the xsi:nil attribute directly, so the plug-in relies on the nilReason attribute: if a mapping exists for the nilReason attribute of a nillable element, the generated Attribute Mapping is altered so that an xsi:nil="true" attribute will appear on the element when a value exists for the nilReason attribute.