Extending the Information Model¶
Use Case¶
The sdmx-core libraries rely on the compatibility of the SDMX Information Model between versions. This enables a single Java interface to be created to define an SDMX Structure which can be read and written in different versions of the standard. An example is the CodelistBean, which can be read in SDMX v1.0 and written in SDMX v3.0. There are cases, however, where information in the information model exists in one version of the standard but not in a previous version; for example a CodeBean has a validity period in SDMX 3.2 but not in previous versions.
The second use case for extending the information model is where a capability is added to an SDMX Structure type, that is not a native part of the model. An example is the CodelistBean which can have a regular expression pattern defined for it, this pattern is used to restrict the IDs of Codes added to the Codelist.
Solution¶
Where these use cases exist, the solution is to add the property to the structure, for example a CodeBean has a SdmxDate for validity (SDMX 3.2 compliant), and a Codelist has a regular expression Pattern (not part of the SDMX specification).
These properties can then be used natively by any process that requires this information, as they are standard Java fields in the type that would be expected for the property. When serialising the information (writing to a file, for example), the FusionJson format can be used, as FusionJson is the only format which natively supports all sdmx-core structural metadata extensions designed with the flexibility to support these use cases without being coupled to a version of SDMX.
When serialising information in SDMX formats Annotations are used to hold the additional extensions when the underlying format does not natively understand the property.
The sdmx-core framework supports this use case by providing the following method on the AnnotatableBean
StructureWriterEngines are expected to call this method when writing annotations for structures, passing in the version of the information model that the writer is writing the output against. The structure is then able to convert any property into an annotation to encapsulate the information. The annotation type must start with 'FR' to indicate the annotation is created to transmit an extension to the model. The structure must also be able to convert an annotation back when reading the message back in, this is achieved when the structure is being constructed, as the AnnotableBeanImpl groups all annotations with a type that starts with 'FR' and asks for any subclasses to process these. If a subclass does not process these annotations, they will remain as annotations on the structure.
/**
* This map of annotations were used to serialise properties in an exchange format
* where the property was not supported. This method must be overridden by classes the override
* the AnnotationBean.getAnnotations(SDMX_IM) method, the generation of Annotations to encapsulate
* properties must be balanced by the ability to convert the annotation back to the property
* on read
* @param mutable
* @param annotation
* @see AnnotationBean.getAnnotations(SDMX_IM)
*/
protected void processPropertyAnnotations(AnnotableMutableBean mutable, Map<String, List<AnnotationMutableBean>> propertyAnnotation) {
//To be Overridden where required
}
An example of this behaviour can be seen on the CodelistBean where an annotation is used to transmit information telling the Codelist to use a regular expression for validation of the code IDs.
@Override
protected void processPropertyAnnotations(AnnotableMutableBean mutable, Map<String, List<AnnotationMutableBean>> propertyAnnotation) {
super.processPropertyAnnotations(mutable, propertyAnnotation);
List<AnnotationMutableBean> restrictPatterns = propertyAnnotation.remove("FR_RESTRICT_ITEMS");
if(restrictPatterns != null && !restrictPatterns.isEmpty()) {
if(restrictPatterns.size() > 1) {
throw new SdmxSemmanticException("reserved annotation 'FR_RESTRICT_ITEMS' may only appear once");
}
this.restrictionRegEx = restrictPatterns.get(0).getTitle();
}
}
@Override
public List<AnnotationBean> getAnnotations(SDMX_IM imVersion) {
List<AnnotationBean> annotations = super.getAnnotations(imVersion);
if(restrictionRegEx != null) {
AnnotationBean annotation = new AnnotationBeanImpl(new AnnotationMutableBeanImpl().setType("FR_RESTRICT_ITEMS").setTitle(restrictionRegEx), this);
annotations.add(annotation);
}
return annotations;
}