Create XSD file with JAXB

2009/09/26

This post is a sequel of the two previous ones talking about serializing Java classes to XML through JAXB in Eclipse. So, it started when I wanted to serialize objects without using an XSD schema… and the only solution I found was by creating the ObjectFactory.java class that JAXB needs when going through marshalling process (check More on XML serialization with JAXB post).

Still, this solution was not good enough for me, I wanted to find a mechanism inside JAXB to do the task, not outside it, or by cheating… So I googled some more and found a way to generate an XML schema (XSD file) from Java classes; not what I was looking for, but interesting (and tricky) stuff. In order to do this, add to your Java project (described in previous 2 articles) the following method:

public void generateSchema(String suggestedSchemaName) throws Exception
{
    final File parentFolder = new File(".");
    final String schemaName = suggestedSchemaName;
        
    JAXBContext jc = JAXBContext.newInstance(Graph.class);
    jc.generateSchema(new SchemaOutputResolver() {
        @Override
        public Result createOutput(String namespace, String schema) throws IOException {
            return new StreamResult(new File(parentFolder, schemaName));
        }
    });
}

As you can see, JAXBContext class has the generateSchema method, which takes an instance of a SchemaOutputResolver class, that needs to override the createOutput method in order to output the XSD file. So you can afterwards call the method presented above as follow:

generateSchema(“graphXmlSchema.xsd”);
Advertisements

More on XML serialization with JAXB

2009/09/24

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.

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 drawgraph.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 drawgraph.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 drawgraph.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 drawgraph.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 drawgraph.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

01MoreXmlJaxbFileOutput


Using JAXB in Eclipse for XML de/serialization

2009/09/17

I was looking for a simple, straightforward tutorial on how to use JAXB (Java Architecture for XML Binding) with Eclipse, as myself am kind of newbie to Java, although worked some time ago with this programming language. So, here it is some example that I hope will help some other programmer when trying to serialize objects to XML:

Step 1: Install JAXB plugin for Eclipse, named XJC

For this, download the latest version of the JAXB XJC generator plugin; copy the content of the archive into the %Eclipse_Folder%/plugins folder. Restart Eclipse if required.

Step 2: Create a new Eclipse project as shown bellow (I called it DrawGraph, as it handles nodes and edges of a graph):

DrawGraph project

Step 3: Add a new folder under src directory, called xsd; under this folder, create a new XML Schema file, using Eclipse wizard. The content of the file is displayed below:

BaseGraph.xsd file

As you can see, the BaseGraph.xsd file defines restrictions on how to format an XML file that will represent a graph object containing vertices/nodes and edges/lines; the graph will contain a list of nodes and one of lines, and also a start and end node, if necessary.

Step 4: Run XJC plugin in order to generate Java classes associated with the business objects described by the XML schema. This is done by using the context menu of the BaseGraph.xsd file:

Run XJC plugin

After setting the Package name (drawgraph.objects in my case) and the Output directory (for my project I used D:\Work\Java\Projects\DrawGraph\src), if the xsd is well defined, the plugin will generate six classes under drawgraph.objects package:

  • BaseGraph.java – the graph object containing the list of lines and nodes
  • Line.java – the line/edge object
  • LineList.java – an object containing a list with lines/edges
  • Node.java – the node/vertex object
  • NodeList.java – an object containing a list with nodes/vertices
  • ObjectFactory.java – class for creating the upper-mentioned class instances

If you’d like to run the XJC from the command line, you can try something like:

xjc -p outputFolder BaseGraph.xsd

Step 5: Use JAXBContext, Marshaller and Unmarshaller classes from javax.xml.bind.* package in order to serialize/deserialize to/from XML file. In my case, I extended BaseGraph.java class by implementing Graph.java, which contains the marshalling and unmarshalling methods (and some other extra methods, for handling graph entities), as presented below:

package drawgraph.objects;

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

public class Graph extends BaseGraph

{
    protected ObjectFactory objectFactory = new objectFactory();

    public Graph() {
        this.setNodeList(objectFactory.createNodeList());
        this.setLineList(objectFactory.createLineList());
    }
    
    public Graph(BaseGraph baseGraph) {
        this.setNodeList(baseGraph.getNodeList());
        this.setLineList(baseGraph.getLineList());
    }
    
    public List getLines() {
        return this.getLineList().getLineList();
    }    
    
    public List getNodes() {
        return this.getNodeList().getNodeList();
    }    
    
    public void addNode(int x, int y) {
        Node node = objectFactory.createNode();
        node.setX(x);
        node.setY(y);
        node.setIndex(this.getNodes().size());
        this.getNodes().add(node);
    }
    
    public void addLine(int start, int end) {
        Line line = objectFactory.createLine();
        line.setStartNode(start);
        line.setEndNode(end);
        this.getLines().add(line);
    }
    
    public void marshalToFile(String fileName) {
        try {
            JAXBContext jc = JAXBContext.newInstance(“drawgraph.objects”);
            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(“drawgraph.objects”);
            Unmarshaller um = jc.createUnmarshaller();
            BaseGraph baseGraph = (BaseGraph) um.unmarshal(new File(fileName));
            return new Graph(baseGraph);
        } catch (Exception e) {
            return new Graph();
        }
    }
    
}

Step 6:Test the marshalling and unmarshalling, using JUnit capabilities (or just test 🙂 ):

package drawgraph.objects;

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

public class GraphTest {
    Graph graph;
    
    @Test
    public void test_graph() {
        createGraph();
        graph.marshalToFile(“graphOutput.xml”);
        Graph secondGraph = Graph.unmarshalFromFile(“graphOutput.xml”);
        assertTrue(secondGraph.getLines().size() == 2);
        assertTrue(secondGraph.getNodes().size() == 3);
    }
    
    private void createGraph() {
        graph = new Graph();
        graph.addNode(50, 50);
        graph.addNode(100, 100);
        graph.addNode(50, 100);
        graph.addLine(1, 2);
        graph.addLine(2, 3);
    }
}


%d bloggers like this: