XmlSnapshotService

Allows an XML document to be generated capturing the data of a root entity and specified related entities.

An XSD schema (to which the XML will be compatible) can also be generated.

This XML/XSD can be used for various purposes, such as mail merge/reporting, or adhoc auditing.

The service offers a basic API to create a snapshot of a single object, and a more flexible API that allows the size of the graph to be customized.

API

XmlSnapshotService.java
interface XmlSnapshotService {
  XmlSnapshotService.Snapshot snapshotFor(Object domainObject)     (1)
  XmlSnapshotService.Snapshot.Builder builderFor(Object domainObject)     (2)
  T getChildElementValue(Element el, String tagname, Class<T> expectedCls)     (3)
}
1 snapshotFor(Object)

Exports the state of a domain object into a Snapshot (which can then be converted into XML, for example).

2 builderFor(Object)

Creates a Snapshot.Builder that allows the contents of the snapshot to include other related state.

3 getChildElementValue(Element, String, Class)

Convenience method to extract value of an XML element, based on its type.

Members

snapshotFor(Object)

Exports the state of a domain object into a Snapshot (which can then be converted into XML, for example).

builderFor(Object)

Creates a Snapshot.Builder that allows the contents of the snapshot to include other related state.

getChildElementValue(Element, String, Class)

Convenience method to extract value of an XML element, based on its type.

Implementation

The framework provides a default implementation of this service, o.a.c.core.runtimeservices.xmlsnapshot.XmlSnapshotServiceDefault.

Usage

The most straight-forward usage of this service is simply:

XmlSnapshot snapshot = xmlsnapshotService.snapshotFor(customer);
Element customerAsXml = snapshot.getXmlElement();

This will return an XML (document) element that contains the names and values of each of the customer’s value properties, along with the titles of reference properties, and also the number of items in collections.

As well as obtaining the XML snapshot, it is also possible to obtain an XSD schema that the XML snapshot conforms to.

XmlSnapshot snapshot = ...;
Element customerAsXml = snapshot.getXmlElement();
Element customerXsd = snapshot.getXsdElement();

This can be useful for some tools. For example, Altova Stylevision can use the XML and XSD to transform into reports. Please note that this link does not imply endorsement (nor even a recommendation that this is a good design).

Builder API

The contents of the snapshot can be adjusted by including "paths" to other references or collections. To do this, the builder is used. The API for this is:

public interface XmlSnapshotService {
    ...
    public interface Builder {
        void includePath(final String path);
        void includePathAndAnnotation(String path, String annotation);
        XmlSnapshotService.Snapshot build();
    }
    @Programmatic
    public XmlSnapshotService.Builder builderFor(Object domainObject);
}

We start by obtaining a builder:

XmlSnapshot.Builder builder = xmlsnapshotService.builderFor(customer);

Suppose now that we want the snapshot to also include details of the customer’s address, where address in this case is a reference property to an instance of the Address class. We can "walk-the-graph" by including these references within the builder.

builder.includePath("address");

We could then go further and include details of every order in the customer’s orders collection, and details of every product of every order:

builder.includePath("orders/product");

When all paths are included, then the builder can build the snapshot:

XmlSnapshot snapshot = builder.build();
Element customerAsXml = snapshot.getXmlElement();

All of this can be strung together in a fluent API:

Element customerAsXml = xmlsnapshotService.builderFor(customer)
                        .includePath("address")
                        .includePath("orders/product")
                        .build()
                        .getXmlElement();

As you might imagine, the resultant XML document can get quite large very quickly with only a few "include"s.

If an XSD schema is beng generated (using snapshot.getXsdElement() then note that for the XSD to be correct, the object being snapshotted must have non-null values for the paths that are `include()’d. If this isn’t done then the XSD will not be correct reflect for another snapshotted object that does have non-null values.

Automatic inclusions

If the domain object being snapshotted implements the SnapshottableWithInclusions interace, then this moves the responsibility for determining what is included within the snapshot from the caller to the snapshottable object itself:

public interface SnapshottableWithInclusions extends Snapshottable {
    List<String> snapshotInclusions();
}

If necessary, both approaches can be combined.

As an alternative to using include(), you might consider building a view model domain object which can reference only the relevant information required for the snapshot. For example, if only the 5 most recent Orders for a Customer were required, a CustomerAndRecentOrders view model could hold a collection of just those 5 Orders. Typically such view models would implement SnapshottableWithInclusions.

One reason for doing this is to provide a stable API between the domain model and whatever it is that might be consuming the XML. With a view model you can refactor the domain entities but still preserve a view model such that the XML is the same.

See Also