Documentation of 'javolution37.javolution.xml.package-summary' Java class
javolution37.javolution.xml

Package javolution37.javolution.xml

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.

See: Description

Package javolution37.javolution.xml Description

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:

  • Very fast and small memory footprint (unmarshalling performed using our real-time pull parser).
  • Performance on a par or better than default JavaTM Serialization/Deserialization (See bindmark for performance comparison).
  • Runs on any platform including J2ME CLDC 1.0 It does not require reflection or or any interface (e.g. Serializable) to be implemented.
  • The XML mapping can be defined for a top class (or interface) and is automatically inherited by all sub-classes (or all implementing classes).
  • Allows for sharing and unicity (through the use of factory methods instead of constructors).
  • Supports document cross-references (to avoid expanding objects already formatted).
  • The XML mapping is customizable and is not tight to the class internal representation (e.g. impervious to obfuscation). It can also be used with xml data produced by other products.
  • The XML mapping can be dynamically changed at run-time.
  • Integrated with NIO high-performance framework (e.g. java.nio.ByteBuffer).
  • XML formats can be generated from the source code using the JavoClipse Eclipse plug-in (see Javolution Tools).

The default XML mapping for a class and its sub-classes is typically defined using a static final XmlFormat instance. For example:

      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<Graphic> XML = new XmlFormat<Graphic>(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;
              }
          };
      }
Sub-classes may override the inherited XML format:
      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 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;
              }
          };
      }
The following writes a graphic area to a file, then reads it:
      ObjectWriter<Area> areaWriter = new ObjectWriter<Area>();
      areaWriter.setPackagePrefix("g", "org.jscience.graphics.geom2d"); // Use namespace for package (optional).
      areaWriter.write(area, new FileOutputStream("C:/area.xml"));
      ...
      Area a = new ObjectReader<Area>().read(new FileInputStream("C:/area.xml"));

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

Here is an example of valid XML representation for an area:
      <g:Area xmlns:j="http://javolution.org" xmlns:g="java:org.jscience.graphics.geom2d" isVisible="true">
          <Paint j:class="java.awt.Color" rgb="#F3EBC6">
          <Geometry j:class="org.jscience.graphics.geom2d.Polygon" id="1">
              <Point x="123" y="-34">
              <Point x="-43" y="-34">
              <Point x="-12" y="123">
          </Geometry>
      </g:Area>

The following table illustrates the variety of xml representations supported (Foo class with a single String member named text):

XML FORMAT XML DATA
XmlFormat<Foo> XML = new XmlFormat<Foo>(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;
    }
};
 <!-- Member as attribute -->
 <Foo text="This is a text"/>
XmlFormat<Foo> XML = new XmlFormat<Foo>(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;
    }
};
 <!-- Member as anonymous nested element -->
 <Foo>
     <java.lang.String value="This is a text"/>
 </Foo>
XmlFormat<Foo> XML = new XmlFormat<Foo>(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;
    }
};
 <!-- Member as Character Data -->
 <Foo>
     <![CDATA[This is a text]]>
 </Foo>
XmlFormat<Foo> XML = new XmlFormat<Foo>(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;
    }
};
 <!-- Member as named element of unknown type  -->
 <Foo>
     <text j:class="java.lang.String" value="This is a text"/>
 </Foo>
XmlFormat<Foo> XML = new XmlFormat<Foo>(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;
    }
};
 <!-- Member as named element of known type -->
 <Foo>
     <text value="This is a text"/>
 </Foo>

Applications may also temporarily change the classes aliases and associated formats during parsing/formatting.

The XmlFormat does not have to use the class public no-arg constructor (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:

        public final class Point { // Immutable, no no-arg constructor.
            protected static final XmlFormat<Point> XML = new XmlFormat<Point>(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<MyPrivateGraphic> XML = new XmlFormat<MyPrivateGraphic>(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. 
                }
            };
        }

Document cross-references are supported, including circular references for xml format implementing 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 enabled:

      <java.util.ArrayList xmlns:j="http://javolution.org" xmlns:geom2d="java:org.jscience.graphics.geom2d" j:id="0">
          <geom2d:Polygon j:id="1">
              <Point x="123" y="-34"/>  
              <Point x="-43" y="-34"/>
              <Point x="-12" y="123"/>
          </geom2d:Polygon>
          <geom2d:Polygon j:id="2">
              <Point x="-43" y="-34"/>
              <Point x="123" y="-34"/>
              <Point x="-12" y="123"/>
          </geom2d:Polygon>
          <geom2d:Polygon j:ref="1"/>
      </java.util.ArrayList>

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):

      // 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();

When using NIO, the ByteBuffer capacity has to be large enough to hold the largest XML representation of the objects being transmitted.

DMelt 3.0 © DataMelt by jWork.ORG

You see the box below because you did not login.