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.java.nio.ByteBuffer
). The default XML mapping for a class and its sub-classes is typically defined using
a For multiple objects transmissions over open I/O streams,
{@link javolution.xml.XmlInputStream} and {@link javolution.xml.XmlOutputStream} are recommended.static final
{@link javolution.xml.XmlFormat XmlFormat} instance.
For example:[code]
public abstract class Graphic {
private boolean _isVisible;
private Paint _paint; // null if none.
private Stroke _stroke; // null if none.
private Transform _transform; // null if none.
// XML format with name associations (members identified by an unique name).
// See XmlFormat for examples of positional associations.
protected static final XmlFormat XML = new XmlFormat
(Graphic.class) {
public void format(Graphic g, XmlElement xml) {
xml.setAttribute("isVisible", g._isVisible);
xml.add(g._paint, "Paint");
xml.add(g._stroke, "Stroke");
xml.add(g._transform, "Transform");
}
public Graphic parse(XmlElement xml) {
Graphic g = xml.object();
g._isVisible = xml.getAttribute("isVisible", true);
g._paint = xml.get("Paint");
g._stroke = xml.get("Stroke");
g._transform = xml.get("Transform");
return g;
}
};
}[/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 XML = new XmlFormat(Area.class) {
public void format(Area area, XmlElement xml) {
Graphic.XML.format(area, xml); // Call parent format.
xml.add(area._geometry,"Geometry");
}
public Area parse(XmlElement xml) {
Area area = (Area) Graphic.XML.parse(xml); // Call parent parse.
area._geometry = xml.get("Geometry");
return area;
}
};
}[/code]
The following writes a graphic area to a file, then reads it:[code]
ObjectWriter areaWriter = new ObjectWriter();
areaWriter.setPackagePrefix("g", "org.jscience.graphics.geom2d"); // Use namespace for package (optional).
areaWriter.write(area, new FileOutputStream("C:/area.xml"));
...
Area a = new ObjectReader().read(new FileInputStream("C:/area.xml"));[/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> <![CDATA[This is a text]]> </Foo> |
[code]XmlFormat |
<!-- Member as named element of unknown type --> <Foo> <text j:class="java.lang.String" value="This is a text"/> </Foo> |
[code]XmlFormat |
<!-- Member as named element of known type --> <Foo> <text value="This is a text"/> </Foo> |
Applications may also temporarily change the classes {@link javolution.xml.XmlFormat#setAlias aliases} and associated {@link javolution.xml.XmlFormat#setFormat formats} during parsing/formatting.
The {@link javolution.xml.XmlFormat XmlFormat} does not have to use the class
public no-arg constructor ({@link javolution.xml.XmlElement#object xml.object()}), 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 { // Immutable, no no-arg constructor.
protected static final XmlFormat
Document cross-references are supported, including circular references for xml format
implementing {@link javolution.xml.XmlFormat#allocate XmlFormat.allocate(xml)}.
Here is the XML representation of a list of three polygons
(the first one and the last one being shared) when references are {@link javolution.xml.ObjectWriter#setReferencesEnabled enabled}:[code]
Finally, here is a code excerpt illustrating how objects can be
efficiently transmitted over the network using the java.nio
facility
instead of classic I/O (slower):[code]
// Client thread.
ObjectReader or = new ObjectReader();
ByteBuffer bb = ByteBuffer.allocateDirect(XML_SIZE);
SocketChannel sc = SocketChannel.open(new InetSocketAddress(LOCAL_HOST, PORT));
sc.read(bb); // Reads socket into byte buffer.
bb.flip();
Object obj = or.read(bb); // Parses byte buffer.
bb.clear();
...
// Server thread.
ObjectWriter ow = new ObjectWriter();
ByteBuffer bb = ByteBuffer.allocateDirect(XML_SIZE);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(PORT));
SocketChannel sc = ssc.accept(); // Waits for connections.
ow.write(obj, bb); // Formats object into byte buffer.
bb.flip();
sc.write(bb); // Sends byte buffer.
bb.clear();[/code]
When using NIO, the ByteBuffer
capacity has to be large enough to hold
the largest XML representation of the objects being transmitted.