Allows for Other Improvements
Finally, as we have gone through this process, we are happy to report that the overall design of Aspire is cleaner and more programmer-friendly throughout. Some improvements enabled by this change include:
- Merging of "Job properties" with "document variables"
- Both have been merged into a single list inside the job, called "Job Variables"
- AspireObject is much simpler than the older AspireDocument or AXML
- AXML and AspireDocument previously had about 87 java methods.
- This has been reduced in AspireObject to just 36 methods.
- Code intended to encourage use of Aspire "standards" will be moved to a new "Standards" class inside the framework.
- Much of this was originally in AspireDocument. It will be moved to Standards.
- This further reduces the churn and size of the AspireObject implementations
- Note that Job result data will also be represented in AspireObject
- Access to AspireObjects in Groovy scripting will now be much simpler
- DOMCategory is no longer needed
- Contents of objects can be accessed directly, as if they were fields of objects
Configuration is Still XML
Note that Aspire configuration files are still represented in XML. There is no plan to change this at any time in the future.
But Component Status is Now Aspire Objects
AspireObjects Support JSON
The new metadata holder is called an "object" because it is equivalent to a JSON object, with extensions that make it more compatible with XML.
JSON support in AspireObject now includes:
Maps: All AspireObject instances can have a nested Map of name/value pairs.
Primitive Types: Values stored in AspireObject's can be primitive types such as Integer, Float, Boolean, etc., not just String.
Lists: AspireObject values can hold lists of objects. Items in the list can be any type, from a primitive object to a full embedded map.
Created from JSON: AspireObjects can be created from strings and streams of characters which represent JSON content.
Write to JSON: AspireObjects can be written out as valid JSON.
AspireObjects Support XML
The AspireObject has extensions to simple JSON objects specifically to improve handling of XML. This is critically important because XML is used so heavily in and throughout Aspire document processing.
Therefore, several extensions to JSON were added to create an AspireObject which works well with XML:
AspireObject's are named - Every AspireObject has a name. This makes AspireObject's roughly equivalent to XML elements.
Attribute Handling - Special methods exist on AspireObject for setting and getting attributes. Attributes can be added to any object (i.e. Strings, Integers, etc.) which are stored in AspireObject's.
Repeated Elements - Repeated elements are automatically stored as embedded arrays in AspireObject when read from XML. These are automatically unwound when writing the object back to XML.
Mixed Content Handling - Elements which contain mixed text and embedded markup are automatically stored as arrays of content.
XML can be read directly into AspireObject's - AspireObject can be built from SAX streams, allowing XML to automatically build an AspireObject.
AspireObjects can be written to XML - AspireObject's can be used as an XMLReader which produces a SAX event stream. This can be serialized to files or transformed into W3C DOM as necessary.
AspireObjects can be transformed - Since AspireObjects can produce SAX streams, they can also be inputs to XSLT transformations.
AspireObjects can be searched with XPath - Apache JXPath has been implemented as a method for searching AspireObject trees, so that standard XPath expressions can be used to access data from AspireObjects.
Basic AspireObject Usage
Creating New Objects
Creates an empty AspireObject.
Adding Named Elements to an Object
Using set() to Change a Value
AspireObjects Have Names
Like XML Elements, AspireObjects have names:
These named objects are represented in JSON as a parent-map:
In the above example, "doc" is not actually a key for any map. It is merely the name of the AspireObject.
All children added to an Aspire object will also have names:
These are stored in the aspireObj in a map and are represented in JSON as a nested JSON object:
And in XML:
All Named Child Elements are AspireObjects Also
...even those which are just simple Strings or Integers.
When adding named elements to an AspireObject using add(), new AspireObject's are created for each element added:
This small amount of extra overhead allows for these objects to contain attributes, an important feature of XML.
In the above example, titleObj is an AspireObject which contains a String, and not String itself.
In addition, child elements are all named:
Multiple Children with the Same Name Become Embedded Arrays
The following XML structure:
Will be represented in JSON as an array:
This structure will be implemented automatically using the add() method of AspireObject:
Note that get("file") will return just the first child:
Therefore, get() will always return an AspireObject. The value can be null if the child can not be found.
A special method exists to fetch all children for a node:
getAll() will always return a List<AspireObject> which is never null. If there are no children, it will return an empty list.
Fetching All Children
Because all AspireObjects are named, this makes fetching children quite useful:
In the above example, childrenList contains all children, including both instances of the "file" child.
AspireObjects can have both content as well as name/value pairs. This is an extension over standard JSON objects to provide better XML support.
Special Methods are Available for Accessing Content
AspireObjects with Just Content are Represented as Such
AspireObjects with Content and Attributes are Displayed as "$" in JSON
Multiple Adds to Content are Stored as a List
...to handle embedded markup
XML content can have text and embedded tags mixed. When this occurs, the content will be represented in the AspireObject as a list of items. This can be implemented with addContent(), as follows:
When written to JSON, this will look like this:
Note that when the content is a list, it must be put under the "$" tag, to distinguish it from situations where there are multiple XML tags with the same name.
In XML it looks like this:
Now suppose the document has attributes:
The JSON will now look like this:
AspireObjects have special built-in support for attributes to make them more compatible with XML.
Special methods are available for setting and getting attributes
Attribute Values Can Be String Only
Attributes are Represented in JSON as Names Preceded by "@"
It is possible to create an object with null as its content and with no named elements:
In JSON this is represented as:
In XML this is represented as:
Adding Named Elements with No Content
New named elements added to an AspireObject with no content will be assumed to be null:
In JSON this is represented as:
In XML this is represented as:
Adding Null Content is Generally Ignored
The following are ignored (do not throw an exception):
What is Not Supported
- Ordered elements of varying names
- Since elements are stored in HashMaps in AspireObject, the order of these elements is not preserved.
- However, multiple children of the same name are stored in a list in AspireObject, and so their order IS preserved.
- XML Namespaces
- Uncertain what is possible here or will be ultimately supported here
- XML Processing Directives
- These can not be represented in AspireObject
- XML Comments
- Names must be valid XML names
- Currently, we expect that all names in JSON name/value pairs will need to be valid XML names. For example, names must start with a letter or underscore, and can contain letter, underscore, dash or period.