Ir al contenido principal

This is my blog, more about me at marianoguerra.github.io

🦋 @marianoguerra.org 🐘 @marianoguerra@hachyderm.io 🐦 @warianoguerra

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
creamos los getters/setters y dos constructores, uno sin argumentos que inicializa los valores por default y uno con todos los parametros.

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.