Provides support for the encoding of objects, and the objects reachable from them,
into XML
; and the complementary reconstruction of the
object graph from XML
.
Key Advantages:
Serializable
) to be implemented. The default XML mapping for a class and its sub-classes is typically defined using
a static final
{@link javolution.xml.XMLFormat XMLFormat} instance.
For example:[code]
public abstract class Graphic implements XMLSerializable {
private boolean _isVisible;
private Paint _paint; // null if none.
private Stroke _stroke; // null if none.
private Transform _transform; // null if none.
// Default XML format with name associations (members identified by an unique name).
// See XMLFormat for examples of positional associations.
protected static final XMLFormat GRAPHIC_XML = new XMLFormat
(Graphic.class) {
public void write(Graphic g, OutputElement xml) throws XMLStreamException {
xml.setAttribute("isVisible", g._isVisible);
xml.add(g._paint, "Paint");
xml.add(g._stroke, "Stroke");
xml.add(g._transform, "Transform");
}
public void read(InputElement xml, Graphic g) throws XMLStreamException {
g._isVisible = xml.getAttribute("isVisible", true);
g._paint = xml.get("Paint");
g._stroke = xml.get("Stroke");
g._transform = xml.get("Transform");
}
};
}[/code]
Sub-classes may override the inherited XML format:[code]
public class Area extends Graphic {
private Shape _geometry;
// Adds geometry to format.
protected static final XMLFormat AREA_XML = new XMLFormat(Area.class) {
public void write(Area area, OutputElement xml) throws XMLStreamException {
GRAPHIC_XML.write(area, xml); // Calls parent write.
xml.add(area._geometry, "Geometry");
}
public void read(InputElement xml, Area area) throws XMLStreamException {
Graphic.XML.read(xml, area); // Calls parent read.
area._geometry = xml.get("Geometry");
}
};
}[/code]
The following writes a graphic area to a file, then reads it:[code]
// Creates some useful aliases for class names.
XMLBinding binding = new XMLBinding();
binding.setAlias(Color.class, "Color");
binding.setAlias(Polygon.class, "Polygon");
binding.setClassAttribute("type"); // Use "type" instead of "class" for class attribute.
// Writes the area to a file.
XMLObjectWriter writer = XMLObjectWriter.newInstance(new FileOutputStream("C:/area.xml"));
writer.setBinding(binding); // Optional.
writer.setIndentation("\t"); // Optional (use tabulation for indentation).
writer.write(area, "Area", Area.class);
writer.close();
// Reads the area back
XMLObjectReader reader = XMLObjectReader.newInstance(new FileInputStream("C:/area.xml"));
reader.setBinding(binding);
Area a = reader.read("Area", Area.class);
reader.close();
[/code]
Here is an example of valid XML representation for an area:[code]
The following table illustrates the variety of XML representations supported (Foo class with a single String member named text):
XML FORMAT | XML DATA |
[code]XMLFormat |
<!-- Member as attribute --> <Foo text="This is a text"/> |
[code]XMLFormat |
<!-- Member as anonymous nested element --> <Foo> <java.lang.String value="This is a text"/> </Foo> |
[code]XMLFormat |
<!-- Member as Character Data --> <Foo>This is a text</Foo> |
[code]XMLFormat |
<!-- Member as named element of unknown type --> <Foo> <Text class="java.lang.String" value="This is a text"/> </Foo> |
[code]XMLFormat |
<!-- Member as named element of actual type known --> <Foo> <Text value="This is a text"/> </Foo> |
The {@link javolution.xml.XMLFormat XMLFormat} does not have to use the class
public no-arg constructor, instances can be created using factory methods,
private constructors (with constructor parameters set from the XML element) or even retrieved from a collection
(if the object is shared or unique). For example:[code]
public final class Point implements XMLSerializable {
// Default XMLFormat can be private as the class cannot be extended.
static final XMLFormat
Document cross-references are supported, including circular references.
Let's take for example:[code]
public class Polygon implements Shape, XMLSerializable {
private Point[] _vertices;
static final XMLFormat
Our {@link javolution.xml.XMLObjectReader XMLObjectReader}/{@link javolution.xml.XMLObjectWriter XMLObjectWriter} are in fact simple wrappers around our Javolution high-performance StAX-like {@link javolution.xml.stream.XMLStreamReader XMLStreamReader} and {@link javolution.xml.stream.XMLStreamWriter XMLStreamWriter} classes. The logic of these wrappers is described below:
OutputElement.add(object, name, uri, class): 1. if (object == null) return 2. getStreamWriter().writeStartElement(uri, name) 3. isReference = referenceResolver.writeReference(object, this) 4. if (!isReference) binding.getFormat(class).write(object, this) 5. getStreamWriter().writeEndElement() 6. end InputElement.get(name, uri, class): 1. if (!getStreamReader().getLocalName().equals(name) || !getStreamReader().getNamespaceURI().equals(uri)) return null 2. object = referenceResolver.readReference(inputElement) 3. if (object != null) Goto 8 // Found reference 4. format = binding.getFormat(class) 5. object = format.newInstance(class, inputElement) 6. referenceResolver.createReference(object, inputElement) // Done before parsing to support circular references. 7. format.read(inputElement, object) 8. getStreamReader().nextTag() 9. end