Class XMLObjectSerializer

java.lang.Object
io.permazen.util.AbstractXMLStreaming
io.permazen.core.util.XMLObjectSerializer

public class XMLObjectSerializer extends AbstractXMLStreaming
Utility methods for serializing and deserializing Database objects in a Transaction to/from XML.

XML Structure

The overall XML format looks like this:

  <database>
      <schemas>
          ...
      </schemas>
      <objects>
          ...
      </objects>
  </database>
  
The <schemas> tag contains the database's schema definitions. Providing these definitions makes it possible to import into a completely empty Database, i.e., one with no prior knowledge of the objects' schema(s).

The <objects> tag contains the actual object data. Each object may belong to a different schema.

Object Formats

There are two supported formats for <object> tags. The "plain" object format uses standard XML elements and identifies objects and fields with a "name" attribute using standardized. This format supports all possible database object and field names:

  <objects schema="Schema_12e983a72e72ed56741ddc45e47d3377">
      <object type="Person" id="64a971e1aef01cc8">
          <field name="name">George Washington</field>
          <field name="wasPresident">true</field>
          <field name="attributes">
              <entry>
                  <key>teeth</key>
                  <value>wooden</value>
              </entry>
          </field>
          <field name="spouse">c8b84a08e5c2b1a2</field>
      </object>
      ...
  </objects>
  
The "custom" object format uses custom XML tags to specify object types and field names and is more readable. However, it doesn't support object type names and field names that are not valid XML tags:
  <objects>
      <Person id="64a971e1aef01cc8">
          <name>George Washington</name>
          <wasPresident>true</wasPresident>
          <attributes>
              <entry>
                  <key>teeth</key>
                  <value>wooden</value>
              </entry>
          </attributes>
          <spouse>c8b84a08e5c2b1a2</spouse>
      </Person>
      ...
  </objects>
  
When parsing input, the format is auto-detected on a per-element basis, depending on whether or not there is a type="..." attribute (for objects) or name="..." attribute (for fields).

Schema Determination

The schema against which each <object> is interpreted is determined as follows:

  • If the <object> tag has a schema="..." attribute, interpret it as the SchemaId of a SchemaModel and use the corresponding schema.
  • Otherwise, if the containing <objects> tag has a schema="..." attribute, use that.
  • Either way, an explicit schema must either be defined earlier in the XML or already exist in the database.
  • Otherwise if no explicit schema is specified, use the schema associated with the Transaction being written into.

Object ID Generation

Any object ID (including the "id" attribute) may have the special form generated:TYPE:SUFFIX, where TYPE is the object type name and SUFFIX is an arbitrary string. In this case, a random, unassigned object ID is generated on the first occurrence, and on subsequent occurences the previously generated ID is recalled. This facilitates automatically generated input (e.g., using XSL's generate-id() function) and forward references.

When using object ID generation, the configured GeneratedIdCache keeps track of generated IDs.

Other Details

  • The "id" attribute may be omitted, in which case a random unassigned ID is generated
  • Simple fields that are equal to their default values and complex fields that are empty may be omitted
  • XML elements and annotations are expected to be in the null namespace; elements and annotations in other namespaces are ignored
  • Field Details

    • NS_URI

      public static final String NS_URI
      The supported XML namespace URI.

      Currently this is XMLConstants.NULL_NS_URI, i.e., the null/default namespace.

      XML tags and attributes whose names are in other namespaces are ignored.

      See Also:
    • ELEMENT_TAG

      public static final QName ELEMENT_TAG
    • ENTRY_TAG

      public static final QName ENTRY_TAG
    • FIELD_TAG

      public static final QName FIELD_TAG
    • KEY_TAG

      public static final QName KEY_TAG
    • OBJECTS_TAG

      public static final QName OBJECTS_TAG
    • OBJECT_TAG

      public static final QName OBJECT_TAG
    • DATABASE_TAG

      public static final QName DATABASE_TAG
    • SCHEMAS_TAG

      public static final QName SCHEMAS_TAG
    • VALUE_TAG

      public static final QName VALUE_TAG
    • ID_ATTR

      public static final QName ID_ATTR
    • NAME_ATTR

      public static final QName NAME_ATTR
    • NULL_ATTR

      public static final QName NULL_ATTR
    • SCHEMA_ATTR

      public static final QName SCHEMA_ATTR
    • TYPE_ATTR

      public static final QName TYPE_ATTR
  • Constructor Details

  • Method Details

    • getFieldTruncationLength

      public int getFieldTruncationLength()
      Get the maximum length (number of characters) of any written simple field.

      By default, this value is set to -1, i.e., truncation is disabled.

      Returns:
      maximum simple field length, or zero for empty simple fields, or -1 if truncation is disabled
      See Also:
    • setFieldTruncationLength

      public void setFieldTruncationLength(int length)
      Set the maximum length (number of characters) of any written simple field.

      Simple field values longer than this will be truncated. If set to zero, all simple field values are written as empty tags. If set to -1, truncation is disabled.

      Truncation is mainly useful for generating human-readable output without very long lines. Obviously, when truncation is enabled, the resulting output, although still valid XML, will be missing some information and therefore cannot successfully be read back in by this class.

      Parameters:
      length - maximum simple field length, or zero for empty simple fields, or -1 to disable truncation
      Throws:
      IllegalArgumentException - if length < -1
    • isOmitDefaultValueFields

      public boolean isOmitDefaultValueFields()
      Get whether to omit fields whose value equals the default value for the field's type.

      Default true.

      Returns:
      whether to omit fields with default values
    • setOmitDefaultValueFields

      public void setOmitDefaultValueFields(boolean omitDefaultValueFields)
      Set whether to omit fields whose value equals the default value for the field's type.

      Default true.

      Parameters:
      omitDefaultValueFields - true to omit fields with default values
    • getUnresolvedReferences

      public ObjIdMap<ReferenceField> getUnresolvedReferences()
      Get all unresolved forward object references.

      When read() is invoked with allowUnresolvedReferences = true, unresolved forward object references do not trigger an exception; this allows forward references to span multiple invocations. Instead, these references are collected and made available to the caller in the returned map. Callers may also modify the returned map as desired between invocations.

      Returns:
      mapping from unresolved forward object reference to some referring field
    • getGeneratedIdCache

      public GeneratedIdCache getGeneratedIdCache()
      Get the GeneratedIdCache associated with this instance.
      Returns:
      the associated GeneratedIdCache
    • setGeneratedIdCache

      public void setGeneratedIdCache(GeneratedIdCache generatedIdCache)
      Set the GeneratedIdCache associated with this instance.
      Parameters:
      generatedIdCache - the GeneratedIdCache for this instance to use
      Throws:
      IllegalArgumentException - if generatedIdCache is null
    • read

      public int read(InputStream input) throws XMLStreamException
      Import objects pairs into the Transaction associated with this instance from the given XML input.

      This is a convenience method, equivalent to:

       read(input, false)
       
      Parameters:
      input - XML input
      Returns:
      the number of objects read
      Throws:
      XMLStreamException - if an error occurs
      IllegalArgumentException - if input is null
    • read

      public int read(InputStream input, boolean allowUnresolvedReferences) throws XMLStreamException
      Import objects pairs into the Transaction associated with this instance from the given XML input.

      The input format is auto-detected for each <object> based on the presence of the "type" attribute.

      Can optionally check for unresolved object references after reading is complete. If this checking is enabled, an exception is thrown if any unresolved references remain. In any case, the unresolved references are available via getUnresolvedReferences().

      Parameters:
      input - XML input
      allowUnresolvedReferences - true to allow unresolved references, false to throw an exception
      Returns:
      the number of objects read
      Throws:
      XMLStreamException - if an error occurs
      IllegalArgumentException - if input is null
      DeletedObjectException - if allowUnresolvedReferences is true and any unresolved references remain when loading is complete
    • read

      public int read(XMLStreamReader reader) throws XMLStreamException
      Import objects into the Transaction associated with this instance from the given XML input. This method expects to see an opening <objects> as the next event (not counting whitespace, comments, etc.), which is then consumed up through the closing </objects> event. Therefore this tag could be part of a larger XML document.

      The input format is auto-detected for each <object> based on the presence of the "type" attribute.

      Parameters:
      reader - XML reader
      Returns:
      the number of objects read
      Throws:
      XMLStreamException - if an error occurs
      IllegalArgumentException - if reader is null
    • write

      public int write(OutputStream output, boolean customFormat, boolean indent) throws XMLStreamException
      Export all objects from the Transaction associated with this instance to the given output.
      Parameters:
      output - XML output; will not be closed by this method
      customFormat - true for custom format, false for plain Format
      indent - true to indent output, false for all on one line
      Returns:
      the number of objects written
      Throws:
      XMLStreamException - if an error occurs
      IllegalArgumentException - if output is null
    • write

      public int write(Writer writer, boolean customFormat, boolean indent) throws XMLStreamException
      Export all objects from the Transaction associated with this instance to the given writer.
      Parameters:
      writer - XML output; will not be closed by this method
      customFormat - true for custom format, false for plain Format
      indent - true to indent output, false for all on one line
      Returns:
      the number of objects written
      Throws:
      XMLStreamException - if an error occurs
      IllegalArgumentException - if writer is null
    • write

      public int write(XMLStreamWriter writer, boolean customFormat, Stream<? extends ObjId> objIds) throws XMLStreamException
      Export the specified objects from the Transaction associated with this instance to the given XML output.

      This method writes a start element as its first action, allowing the output to be embedded into a larger XML document. Callers not embedding the output may with to precede invocation of this method with a call to writer.writeStartDocument().

      Parameters:
      writer - XML writer; will not be closed by this method
      customFormat - true for custom format, false for plain Format
      objIds - object IDs
      Returns:
      the number of objects written
      Throws:
      XMLStreamException - if an error occurs
      IllegalArgumentException - if writer or objIds is null
    • next

      protected QName next(XMLStreamReader reader) throws XMLStreamException
      Skip forward until either the next opening tag is reached, or the currently open tag is closed. This override ignores XML tags that are not in our namespace.
      Overrides:
      next in class AbstractXMLStreaming
      Parameters:
      reader - XML input
      Returns:
      the XML opening tag found, or null if a closing tag was seen first
      Throws:
      XMLStreamException - if no opening tag is found before the current tag closes