Package 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

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
QName This class represents unique identifiers for XML elements (tags) or attributes (names).
XMLBinding This class represents the binding between Java classes and their XML representation (XMLFormat).
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.
 

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

XML Data Binding

Key Advantages:

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 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> 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");
              }
          };
      }
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> 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");
              }
          };
      }
The following writes a graphic area to a file, then reads it:
    
      // 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();
      
Here is an example of valid XML representation for an area:
      <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
XMLFormat<Foo> XML = new XMLFormat<Foo>(Foo.class) {
    public void write(Foo foo, OutputElement xml) throws XMLStreamException {
        xml.setAttribute("text", foo.text); 
    }
    public void read(InputElement xml, Foo foo) throws XMLStreamException {
        foo.text = xml.getAttribute("text", "");
    }
};
 <!-- Member as attribute -->
 <Foo text="This is a text"/>
XMLFormat<Foo> XML = new XMLFormat<Foo>(Foo.class) {
    public void write(Foo foo, OutputElement xml) throws XMLStreamException {
        xml.add(foo.text); 
    }
    public void read(InputElement xml, Foo foo) throws XMLStreamException {
        foo.text = xml.getNext();
    }
};
 <!-- 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 write(Foo foo, OutputElement xml) throws XMLStreamException {
        xml.addText(foo.text);
        // or xml.getStreamWriter().writeCDATA(foo.text) to use CDATA block. 
    }
    public void read(InputElement xml, Foo foo) throws XMLStreamException {
        foo.text = xml.getText().toString(); // Content of a text-only element.
    }
};
 <!-- Member as Character Data -->
 <Foo>This is a text</Foo>
XMLFormat<Foo> XML = new XMLFormat<Foo>(Foo.class) {
    public void write(Foo foo, OutputElement xml) throws XMLStreamException {
        xml.add(foo.text, "Text"); 
    }
    public void read(InputElement xml, Foo foo) throws XMLStreamException {
        foo.text = xml.get("Text");
    }
};
 <!-- Member as named element of unknown type  -->
 <Foo>
     <Text class="java.lang.String" value="This is a text"/>
 </Foo>
XMLFormat<Foo> XML = new XMLFormat<Foo>(Foo.class) {
    public void write(Foo foo, OutputElement xml) throws XMLStreamException {
        xml.add(foo.text, "Text", String.class); 
    }
    public void read(InputElement xml, Foo foo) throws XMLStreamException {
        foo.text = xml.get("Text", String.class);
    }
};
 <!-- 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:

        public class Polygon implements Shape, XMLSerializable { 
            private Point[] _vertices;
            static final XMLFormat<Polygon> 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);
        
Prints the following (noticed that the first polygon and last one are being shared).
      <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>

ALGORITHMS:

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:


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



Copyright © 2005-2012 Javolution. All Rights Reserved.