Tuesday, March 22, 2011

More on XML serialization with JAXB

I posted some days ago an article explaining how to “XML-serialize” objects with JAXB in Eclipse. The downside of using XSD for XML serialization is the dependency on the XML schema that will generate java classes; this is because I wanted to extend the classes to have extra-functionality, more constructors and so on. So I started looking for a way to serialize objects to XML without creating XSD definitions… but I found a problem: every time java classes are generated, an additional class, called ObjectFactory.java is also created. It is also needed for XML marshalling/unmarshalling. The only viable solution I found was to write my one ObjectFactory.java class, to be used by JAXB when serializing objects.

But I creating ObjectFactory is not necessary. We can simply serialize from simple xml files(see here).

But if we want we can create our own ObjectFactory Class. Example:

Step 1: Create the java classes that you need. In my case, I had two main entities, Node and Line, and two additional collections, Lines and Nodes. I also created a Graph entity, holding a list with nodes and a list with lines. Below are presented Line.java, Lines.java and Graph.java files

Line.java

package com.vaani.jaxb.objects;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Line", propOrder = { "startNode", "endNode" })
public class Line {

@XmlAttribute(name = "StartNode")
protected int startNode;
@XmlAttribute(name = "EndNode")
protected int endNode;

public Line(int start, int end) {
this.setStartNode(start);
this.setEndNode(end);
}

public Line() {
this(0, 0);
}

public int getStartNode() {
return startNode;
}

public void setStartNode(int value) {
this.startNode = value;
}

public int getEndNode() {
return endNode;
}

public void setEndNode(int value) {
this.endNode = value;
}

}


Lines.java

package com.vaani.jaxb.objects;

import java.util.*;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "lines", propOrder = { "lines" })
public class Lines {

@XmlElement(name = "Line")
protected List<Line> lines;

public List<Line> getLines() {
if (lines == null) {
lines = new ArrayList<Line>();
}
return this.lines;
}
}



Graph.java

package com.vaani.jaxb.objects;

import java.io.*;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Graph")
public class Graph {

@XmlElement(name = "Nodes", required = true)
protected Nodes nodes = new Nodes();
@XmlElement(name = "Lines", required = true)
protected Lines lines = new Lines();
@XmlAttribute(name = "StartNode")
protected int startNode;
@XmlAttribute(name = "EndNode")
protected int endNode;

public void addNode(int x, int y) {
getNodes().add(new Node(x, y, getNodes().size() + 1));
}

public void addLine(int start, int end) {
getLines().add(new Line(start, end));
}

public List<Node> getNodes() {
return nodes.getNodes();
}

public void setNodes(Nodes value) {
this.nodes = value;
}

public List<Line> getLines() {
return lines.getLines();
}

public void setLines(Lines value) {
this.lines = value;
}

public int getStartNode() {
return startNode;
}

public void setStartNode(int value) {
this.startNode = value;
}

public int getEndNode() {
return endNode;
}

public void setEndNode(int value) {
this.endNode = value;
}

public void marshalToFile(String fileName) {
try {
JAXBContext jc = JAXBContext.newInstance(this.getClass().getPackage().getName());
Marshaller m = jc.createMarshaller();
m.marshal( this, new FileOutputStream(fileName) );
} catch(Exception e) {
e.printStackTrace();
}
}

public static Graph unmarshalFromFile(String fileName) {
try {
JAXBContext jc = JAXBContext.newInstance((new Graph()).getClass().getPackage().getName());
Unmarshaller um = jc.createUnmarshaller();
return (Graph) um.unmarshal(new File(fileName));
} catch (Exception e) {
return new Graph();
}
}


}


In order to be able to serialize the above classes, we must use the XML annotations like @XmlRootElement, @XmlElement, @XmlAttribute, @XmlType. For the serialization, we use the same JAXBContext, Marshaller and Unmarshaller classes.

Step 2: Implement the ObjectFactory.java class

package com.vaani.jaxb.objects;

import javax.xml.bind.annotation.XmlRegistry;

@XmlRegistry
public class ObjectFactory {

public ObjectFactory() {
}

public Graph createGraph() {
return new Graph();
}

public Node createNode() {
return new Node();
}

public Line createLine() {
return new Line();
}

}


As you can see, the class must be preceded by @XmlRegistry annotation.

Step 3: Test the serialization

package com.vaani.jaxb.objects;

import org.junit.*;
import static org.junit.Assert.*;

public class GraphTest {

Graph graph;

@Test
public void test_graph()
{
createGraph();
graph.marshalToFile("newGraphOutput.xml");
Graph secondGraph = Graph.unmarshalFromFile("newGraphOutput.xml");
assertTrue(secondGraph.getLines().size() == 2);
assertTrue(secondGraph.getNodes().size() == 3);
}

private void createGraph()
{
graph = new Graph();
graph.addNode(60, 60);
graph.addNode(110, 110);
graph.addNode(60, 110);
graph.addLine(1, 3);
graph.addLine(2, 3);
}
}


Step 4: Check the result

image

No comments:

Post a Comment

Chitika