Serializar/desserializar objetos java a/de XML/JSON
este post es la segunda parte de este post: http://marianoguerra.blogspot.com/2009/03/webservices-rest-en-java-con-jetty-y.html
Para tener webservices REST como la gente necesitamos poder transformar nuestros objetos a una representacion que pueda ser entendida por muchos lenguajes y de vuelta de esa representacion a objetos java, para ello vamos a crear los objetos de nuetro modelo y los vamos anotar para poder serializar/desserializar a XML y JSON, yo particularmente prefiero JSON porque es mas simple, legible y interactua muy bien con javascript, que suele ser el otro extremo de cualquier aplicacion web, pero no importa ya que de la forma que voy a mostrar nos permite hacer los dos por el precio de uno.
Para ello creamos primero el paquete model dentro de nuestro proyecto, en mi caso addressbook.model y adentro creamos dos clases, AddressBook y AddressBookEntry
para poder usar las anotaciones de serializacion agregamos los siguientes jars
jaxb-api-2.1.jar
jaxb-impl-2.1.jar
para poder serializar a JSON agregamos el siguiente jar
jettison-1.0.1.jar
para buscar versiones mas nuevas podes ir a http://download.java.net/maven/1/javax.xml.bind/jars/ y http://repository.codehaus.org/org/codehaus/jettison/jettison/ respectivamente.
ahora creamos la clase AddressBookEntry que simplemente va a contener los siguientes atributos privados, todos strings:
- firstName
- lastName
- address
- homePhone
- workPhone
- cellPhone
ahora llega la hora de decorar la clase para poder serializarla, el resultado es el siguiente:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package addressbook.model;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author mariano
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="addressBookEntity")
public class AddressBookEntry implements Serializable{
private String firstName;
private String lastName;
private String address;
private String homePhone;
private String workPhone;
private String cellPhone;
public AddressBookEntry() {
}
public AddressBookEntry(String firstName, String LastName, String address, String homePhone, String workPhone, String cellPhone) {
this.firstName = firstName;
this.lastName = LastName;
this.address = address;
this.homePhone = homePhone;
this.workPhone = workPhone;
this.cellPhone = cellPhone;
}
public String getLastName() {
return lastName;
}
public void setLastName(String LastName) {
this.lastName = LastName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCellPhone() {
return cellPhone;
}
public void setCellPhone(String cellPhone) {
this.cellPhone = cellPhone;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getHomePhone() {
return homePhone;
}
public void setHomePhone(String homePhone) {
this.homePhone = homePhone;
}
public String getWorkPhone() {
return workPhone;
}
public void setWorkPhone(String workPhone) {
this.workPhone = workPhone;
}
}
si se fijan hay dos anotaciones nomas, uno que le dice el nombre de la entidad cuando la serialize y el otro le dice si debe buscar las anotaciones de los atributos en la definicion de los atributos o en los getters/setters.
programamos la clase que contiene muchos AddressBookEntry y la decoramos para poder serializarla:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package addressbook.model;
import java.util.Vector;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author mariano
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="addressBook")
public class AddressBook {
@XmlElement(name="entry")
private Vector<AddressBookEntry> entries;
public AddressBook() {
entries = new Vector<AddressBookEntry>();
}
public AddressBook(Vector<AddressBookEntry> entries) {
this.entries = entries;
}
public Vector<AddressBookEntry> getEntries() {
return entries;
}
public void setEntries(Vector<AddressBookEntry> entries) {
this.entries = entries;
}
public void addEntry(AddressBookEntry entry)
{
entries.add(entry);
}
public boolean removeEntry(AddressBookEntry entry)
{
return entries.remove(entry);
}
public Vector<AddressBookEntry> search(String query)
{
Vector<AddressBookEntry> result = new Vector<AddressBookEntry>();
result.addAll(searchByFirstName(query));
result.addAll(searchByLastName(query));
return result;
}
public Vector<AddressBookEntry> searchByFirstName(String query)
{
Vector<AddressBookEntry> result = new Vector<AddressBookEntry>();
for(AddressBookEntry entry: entries)
{
if(entry.getFirstName().indexOf(query) != -1)
{
result.add(entry);
}
}
return result;
}
public Vector<AddressBookEntry> searchByLastName(String query)
{
Vector<AddressBookEntry> result = new Vector<AddressBookEntry>();
for(AddressBookEntry entry: entries)
{
if(entry.getLastName().indexOf(query) != -1)
{
result.add(entry);
}
}
return result;
}
}
Lo unico que cambia con respecto al anterior es que a la coleccion le digo cual es el tag de cada elemento interno de la coleccion.
con este decorador
@XmlElement(name="entry")le agregue algunos metodos de busqueda que nos van a servir mientras no tengamos persistencia.
por ultimo modifique la clase que expone el webservice para que genere json y xml y cree un metodo temporal para llenar con datos el addressbook (hasta que haya persistencia).
aca esta el codigo de addressbook.ws.AddressBook:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package addressbook.ws;
import addressbook.model.AddressBookEntry;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
/**
*
* @author mariano
*/
@Path("book")
public class AddressBook {
private static addressbook.model.AddressBook addressBook;
public static addressbook.model.AddressBook search(String type, String query)
{
if(type.equals("firstName"))
{
return new addressbook.model.AddressBook(addressBook.searchByFirstName(query));
}
else if(type.equals("lastName"))
{
return new addressbook.model.AddressBook(addressBook.searchByLastName(query));
}
else
{
return new addressbook.model.AddressBook(addressBook.search(query));
}
}
public static addressbook.model.AddressBook getAddressBook()
{
if(addressBook == null)
addressBook = new addressbook.model.AddressBook();
return addressBook;
}
public static void fill()
{
getAddressBook();
addressBook.addEntry(new AddressBookEntry("Bob", "Esponja", "Fondo de Bikini 1", "123", "321", "5551"));
addressBook.addEntry(new AddressBookEntry("Gary", "Caracol", "Fondo de Bikini 2", "234", "432", "5552"));
addressBook.addEntry(new AddressBookEntry("Arenita", "Ardilla", "Fondo de Bikini 3", "345", "543", "5553"));
addressBook.addEntry(new AddressBookEntry("Patricio", "Estrella", "Fondo de Bikini 4", "456", "654", "5554"));
addressBook.addEntry(new AddressBookEntry("Calamardo", "Calamar", "Fondo de Bikini 5", "567", "765", "5555"));
}
@GET
@Path("xml/all")
@Produces("application/xml")
public static addressbook.model.AddressBook getAllXml()
{
return addressBook;
}
@GET
@Path("json/all")
@Produces("application/json")
public static addressbook.model.AddressBook getAllJson()
{
return addressBook;
}
@GET
@Path("xml/search/{type}/{query}")
@Produces("application/xml")
public static addressbook.model.AddressBook searchXml(@PathParam("type") String type, @PathParam("query") String query)
{
return search(type, query);
}
@GET
@Path("json/search/{type}/{query}")
@Produces("application/json")
public static addressbook.model.AddressBook searchJson(@PathParam("type") String type, @PathParam("query") String query)
{
return search(type, query);
}
}
en el main agregue una linea antes de server.start(); en la que llamo a addressbook.ws.AddressBook.fill();
ahora con todo eso puesto, si corremos el ejemplo vamos a obtener resultados serializados, ejemplos de urls para probar los gets:
para obtener todos en xml:
http://0.0.0.0:9999/ws/book/xml/all
para obtener todos en json:
http://0.0.0.0:9999/ws/book/json/all
para buscar los que contienen en el nombre "ob" en xml:
http://0.0.0.0:9999/ws/book/xml/search/firstName/ob/
para buscar los que contienen en el apellido "a" en json:
http://0.0.0.0:9999/ws/book/json/search/lastName/a/
para buscar los que contienen en el nombre o apellido "e" en xml:
http://0.0.0.0:9999/ws/book/xml/search/all/e/
supongo que van entendiendo ;)
bueno, eso es todo por ahora en la proxima como escribir un modulo para javascript para consumir los webservices json y transformarlos en objetos y tambien como "dar de alta" nuevos AddressBookEntry.