Friday, July 21, 2017

Serializing Java Objects with JAXB

Java Model

The model class Team

package j2s.team;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/* 
 * As the class Team is annotated with @XmlRootElement, JAXB transforms a Team 
 * instance into an XML document whose root element is <team> 
 */
@XmlRootElement(name="team")
@XmlType(propOrder={"name","description","playerList"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Team {

 // fields
 private String name;
 private String description;

 @XmlElement(name = "player")
 @XmlElementWrapper(name = "players")
 private List<Player> playerList;

 public Team(String name, String description) {
  this.name = name;
  this.description = description;
  this.playerList = new ArrayList<Player>();
 }

 // default constructor required by JAXB
 public Team() {
  this("team name", "team descr");
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getDescription() {
  return description;
 }

 public void setDescription(String description) {
  this.description = description;
 }

 public List<Player> getPlayers() {
  return playerList;
 }

 public void setPlayers(List<Player> playerList) {
  this.playerList = playerList;
 }

 public void addPlayer(Player dev) {
  playerList.add(dev);
 }

 @Override
 public String toString() {
  return "Team [name=" + name + ", description=" + description + ", players=" + playerList + "]";
 }

}

The model class Player

package j2s.team;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder={"name","number"})
public class Player {
 
 public Player(String name, int number, String position){
  this.name=name;
  this.number=number;
  this.position=position;
 }

 private String name;
 private int number;
 @XmlAttribute
 private String position;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public int getNumber() {
  return number;
 }

 public void setNumber(int number) {
  this.number = number;
 }

 public String getPosition() {
  return position;
 }

 public void setPosition(String position) {
  this.position = position;
 }

 @Override
 public String toString() {
  return "Player [name=" + name + ", position=" + position + "]";
 }
}

Use the Java Model with JAXB APIs

The JAXB Client class

package j2s.team.app;

import java.io.File;
import java.io.FileOutputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.namespace.QName;

import j2s.team.Player;
import j2s.team.Team;

public class JAXBClient {

 Team buildTeam() {
  Team team = new Team("Chelsea Football Club", "club based in Fulham, London, England");
  team.addPlayer(new Player("Willy Caballero", 1, "goalkeeper"));
  team.addPlayer(new Player("Marcos Alonso", 3, "defender"));
  team.addPlayer(new Player("Cesc Fabregas", 4, "midfielder"));
  team.addPlayer(new Player("N'Golo Kante", 7, "midfielder"));
  team.addPlayer(new Player("Loïc Remy", 8, "forward"));
  team.addPlayer(new Player("Pedro", 11, "midfielder"));
  team.addPlayer(new Player("Thibaut Courtois", 13, "goalkeeper"));
  team.addPlayer(new Player("Victor Moses", 15, "midfielder"));   
  return team;
 }

 void marshallToSystemOutStream(Team team) {
  try {
   JAXBContext jaxbContext = JAXBContext.newInstance(Team.class);
   Marshaller marshaller = jaxbContext.createMarshaller();
   marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
   marshaller.marshal(team, System.out);
  } catch (JAXBException e) {
   e.printStackTrace();
  }
 }

 void marshallToFileStream(Team team) {
  try {
   JAXBContext jaxbContext = JAXBContext.newInstance(Team.class);
   Marshaller marshaller = jaxbContext.createMarshaller();
   marshaller.marshal(team, new FileOutputStream(new File("team.mar")));
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 // from Team object to XML document
 @XmlElementDecl(namespace = "http://www.software-demon.com/team", name = "team")
 JAXBElement toXml(Team team) {
  return new JAXBElement(new QName("team"), Team.class, team);
 }

}

The JUnit test class

package j2s.team.app;

import static org.junit.Assert.*;
import javax.xml.bind.JAXBElement;
import j2s.team.app.JAXBClient;
import j2s.team.Team;

public class JAXBClientTest {
 
 private Team team;
 
 @org.junit.Before
 public void build(){
  team = new JAXBClient().buildTeam();
 }
 
 @org.junit.Test
 public void testRun() {
  JAXBElement jaxbElem = new JAXBClient().toXml(team);
  assertSame("the same team object", team, jaxbElem.getValue());  
 } 
 
 @org.junit.After
 public void print(){
  new JAXBClient().marshallToSystemOutStream(team);
 }

}

The JUnit test outputs:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<team>
    <name>Chelsea Football Club</name>
    <description>club based in Fulham, London, England</description>
    <players>
        <player position="goalkeeper">
            <name>Willy Caballero</name>
            <number>1</number>
        </player>
        <player position="defender">
            <name>Marcos Alonso</name>
            <number>3</number>
        </player>
        <player position="midfielder">
            <name>Cesc Fabregas</name>
            <number>4</number>
        </player>
        <player position="midfielder">
            <name>N'Golo Kante</name>
            <number>7</number>
        </player>
        <player position="forward">
            <name>Loïc Remy</name>
            <number>8</number>
        </player>
        <player position="midfielder">
            <name>Pedro</name>
            <number>11</number>
        </player>
        <player position="goalkeeper">
            <name>Thibaut Courtois</name>
            <number>13</number>
        </player>
        <player position="midfielder">
            <name>Victor Moses</name>
            <number>15</number>
        </player>
    </players>
</team>

Most important java annotations used by JAXB

  • @XmlRootElement: maps a class or enum type to a XML schema global type
  • @XmlType: maps class or enum type to a XML schema type
  • @XmlElement: maps a JavaBean property to a XML element

Most important binding strategies used by JAXB

  • @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER): default binding strategy, every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
  • @XmlAccessorType(XmlAccessType.FIELD): every non static, non transient field in a JAXB-bound class will be automatically bound to XML

Common Terminology

  • Serialization: the process of translating object state into a data format (a byte stream) that can be stored or transmitted and reconstructed later
    • for example java serialization, database serialization, file serialization, RPC serialization (Microsoft RPC serialization, google GWT RPC serialization).
  • Marshaling: the process of transforming the memory representation of an object to a data format suitable for storage or transmission
    • for example CORBA marshaling, RMI marshaling
Serialization and marshaling are similar terms. In Java, to marshal means to record object's state and object's codebase. The codebase is a collection of URLs where the object code can be loaded from. So, for JAXB, it is more correct to use the term serialization.

No comments :

Post a Comment