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.

XML marshalling/unmarshalling facility:

Key Advantages:

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 { 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]

For multiple objects transmissions over open I/O streams, {@link javolution.xml.XmlInputStream} and {@link javolution.xml.XmlOutputStream} are recommended.

Here is an example of valid XML representation for an area:[code] [/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 XML = new XmlFormat(Foo.class) { public void format(Foo foo, XmlElement xml) { xml.setAttribute("text", foo.text); } public Foo parse(XmlElement xml) { Foo foo = xml.object(); foo.text = xml.getAttribute("text", ""); return foo; } };[/code]
 <!-- Member as attribute -->
 <Foo text="This is a text"/>
[code]XmlFormat XML = new XmlFormat(Foo.class) { public void format(Foo foo, XmlElement xml) { xml.add(foo.text); } public Foo parse(XmlElement xml) { Foo foo = xml.object(); foo.text = (String) xml.getNext(); return foo; } };[/code]
 <!-- Member as anonymous nested element -->
 <Foo>
     <java.lang.String value="This is a text"/>
 </Foo>
[code]XmlFormat XML = new XmlFormat(Foo.class) { public void format(Foo foo, XmlElement xml) { xml.add(CharacterData.valueOf(foo.text)); } public Foo parse(XmlElement xml) { Foo foo = xml.object(); foo.text = xml.getNext().toString(); return foo; } };[/code]
 <!-- Member as Character Data -->
 <Foo>
     <![CDATA[This is a text]]>
 </Foo>
[code]XmlFormat XML = new XmlFormat(Foo.class) { public void format(Foo foo, XmlElement xml) { xml.add(foo.text, "text"); } public Foo parse(XmlElement xml) { Foo foo = xml.object(); foo.text = xml.get("text"); return foo; } };[/code]
 <!-- Member as named element of unknown type  -->
 <Foo>
     <text j:class="java.lang.String" value="This is a text"/>
 </Foo>
[code]XmlFormat XML = new XmlFormat(Foo.class) { public void format(Foo foo, XmlElement xml) { xml.add(foo.text, "text", String.class); } public Foo parse(XmlElement xml) { Foo foo = xml.object(); foo.text = xml.get("text", String.class); return foo; } };[/code]
 <!-- 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 XML = new XmlFormat(Point.class) { public String identifier() { return null; // Do not use references for points (always expanded). } public void format(Point point, XmlElement xml) { xml.setAttribute("x", point._x); xml.setAttribute("y", point._y); } public Point parse(XmlElement xml) { return Point.valueOf(xml.getAttribute("x", 0.0), xml.getAttribute("y", 0.0)); } }; private double _x; private double _y; private Point() {}; // No-arg constructor not visible. public static Point valueOf(double x, double y) { ... } } ... private class MyPrivateGraphic extends Graphic { // Private class (can be inner class). static final XmlFormat XML = new XmlFormat(MyPrivateGraphic.class) { public MyPrivateGraphic allocate(XmlElement xml) { return new MyPrivateGraphic(); } public void format(MyPrivateGraphic g, XmlElement xml) { Graphic.XML.format(g, xml); // Calls parent format. } public MyPrivateGraphic parse(XmlElement xml) { return (MyPrivateGraphic) Graphic.XML.parse(xml); // Calls parent parse. } }; }[/code]

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] [/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.