...

martes, marzo 24, 2009

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.

Seguidores

Archivo del blog

Datos personales

Mariano Guerra
Soy Mariano Guerra nacido en 1985 estudio ingenieria en sistemas me gusta mucho pero mucho la musica, tambien me gusta la informatica etc
Ver todo mi perfil