|
J avolution v5.2 (J2SE 1.5+) | ||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
XML
; and the complementary reconstruction of the
object graph from XML
.
See:
Description
Interface Summary | |
---|---|
XMLSerializable | This interface identifies classes supporting XML serialization
(XML serialization is still possible for classes not implementing this
interface through dynamic XMLBinding though). |
Class Summary | |
---|---|
XMLBinding | This class represents the binding between Java classes and
their XML representation (XMLFormat ); the binding may be shared
among multiple XMLObjectReader / XMLObjectWriter
instances (thread-safe). |
XMLFormat<T> | This class represents the format base class for XML serialization and deserialization. |
XMLFormat.InputElement | This class represents an input XML element (unmarshalling). |
XMLFormat.OutputElement | This class represents an output XML element (marshalling). |
XMLObjectReader | This class restores objects which have been serialized in XML
format using an XMLObjectWriter . |
XMLObjectWriter | This class takes an object and formats it to XML; the resulting
XML can be deserialized using a XMLObjectReader . |
XMLReferenceResolver | This class represents a resolver for XML cross references during the marshalling/unmarshalling process. |
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:
XMLFormat
is basically a "smart"
wrapper around our real-time StAX-like
XMLStreamReader
and
XMLStreamWriter
.Serializable
) to be implemented. The default XML mapping for a class and its sub-classes is typically defined using
a static final
XMLFormat
instance.
For example:
Sub-classes may override the inherited XML format:
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>(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");
}
};
}
The following writes a graphic area to a file, then reads it:
public class Area extends Graphic {
private Shape _geometry;
// Adds geometry to format.
protected static final XMLFormat<Area> XML = new XMLFormat<Area>(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");
}
};
}
Here is an example of valid XML representation for an area:
// 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();
<Area isVisible="true">
<Paint type="Color" rgb="#F3EBC6" />
<Geometry type="Polygon">
<Vertex x="123" y="-34" />
<Vertex x="-43" y="-34" />
<Vertex x="-12" y="123" />
</Geometry>
</Area>
The following table illustrates the variety of XML representations supported (Foo class with a single String member named text):
XML FORMAT | XML DATA |
|
<!-- Member as attribute --> <Foo text="This is a text"/> |
|
<!-- Member as anonymous nested element --> <Foo> <java.lang.String value="This is a text"/> </Foo> |
|
<!-- Member as Character Data --> <Foo>This is a text</Foo> |
|
<!-- Member as named element of unknown type --> <Foo> <Text class="java.lang.String" value="This is a text"/> </Foo> |
|
<!-- Member as named element of actual type known --> <Foo> <Text value="This is a text"/> </Foo> |
The 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:
public final class Point implements XMLSerializable {
// Default XMLFormat can be private as the class cannot be extended.
static final XMLFormat<Point> XML = new XMLFormat<Point>(Point.class) {
public boolean isReferencable() {
return false; // Always manipulates by value.
}
public Point newInstance(Class<Point> cls, InputElement xml) throws XMLStreamException {
return Point.valueOf(xml.getAttribute("x", 0), xml.getAttribute("y", 0));
}
public void write(Point point, OutputElement xml) throws XMLStreamException {
xml.setAttribute("x", point._x);
xml.setAttribute("y", point._y);
}
public void read(InputElement xml, Point point) throws XMLStreamException {
// Do nothing immutable.
}
};
private int _x;
private int _y;
private Point() {}; // No-arg constructor not visible.
public static Point valueOf(int x, int y) { ... }
}
Document cross-references are supported, including circular references.
Let's take for example:
Prints the following (noticed that the first polygon and last one are being shared).
public class Polygon implements Shape, XMLSerializable {
private Point[] _vertices;
static final XMLFormat<Polygon> XML = new XMLFormat<Polygon>(Polygon.class) {
public void write(Polygon polygon, OutputElement xml) throws XMLStreamException {
xml.setAttibutes("count", _vertices.length);
for (int i=0; i < _vertices.length; i++) {
xml.add(_vertices[i], "Vertex", Point.class);
}
}
public void read(InputElement xml, Polygon polygon) throws XMLStreamException {
int count = xml.getAttributes("count", 0);
polygon._vertices = new Point[count];
for (int i=0; i < count; i++) {
_vertices[i] = xml.get("Vertex", Point.class);
}
}
};
}
Polygon[] polygons = new Polygon[] {p1, p2, p1};
...
TextBuilder xml = TextBuilder.newInstance();
AppendableWriter out = new AppendableWriter().setOutput(xml)
XMLObjectWriter writer = XMLObjectWriter.newInstance(out);
writer.setXMLReferenceResolver(new XMLReferenceResolver()); // Enables cross-references.
writer.write(polygons, "Polygons", Polygon[].class);
writer.close();
System.out.println(xml);
<Polygons length="3">
<Polygon id="0" count="3">
<Vertex x="123" y="-34" />
<Vertex x="-43" y="-34" />
<Vertex x="-12" y="123" />
</Polygon>
<Polygon id="1" count="3">
<Vertex x="-43" y="-34" />
<Vertex x="123" y="-34" />
<Vertex x="-12" y="123" />
</Polygon>
<Polygon ref="0"/>
</Polygons>
Our XMLObjectReader
/XMLObjectWriter
are in fact simple wrappers around our Javolution high-performance StAX-like
XMLStreamReader
and
XMLStreamWriter
classes.
The logic of these wrappers is described below:
Marshalling: Input: object, referenceResolver, binding Output: outputElement 1. class = object.getClass() 2. outputElement.getStreamWriter().writeStartElement(name/uri) The new element name/uri is: a - Specified by caller, e.g. add(object, name, uri, class) If the class is not specified e.g. add(object, name, uri) a class attribute is written. b - binding.getLocalName(class)/binding.getURI(class), e.g add(object) 3. isReference = referenceResolver.writeReference(object, outputElement) 4. if (!isReference) binding.getFormat(class).write(object, outputElement) 5. outputElement.getStreamWriter().writeEndElement() 6. end Unmarshalling: Input: inputElement, referenceResolver, binding Output: object 1. object = referenceResolver.readReference(inputElement) 2. if (object != null) Goto 8 // Found reference 3. class is either: a - Specified by caller, e.g. get("name", class) b - binding.getClass(class attribute value) c - binding.getClass(element name, element uri) 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. inputElement.getStreamReader().nextTag() 9. end
|
J avolution v5.2 (J2SE 1.5+) | ||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |