Colabora
 

Envío de una clase como parámetro entre dos servicios web

[Serialización y deserialización de clases definidas en servicios web]

 

Fecha: 06/Jun/2007 (29 mayo 2007)
Autor: Julio Aguilar Castillo - [email protected]

 


Introducción


La idea central es invocar  un servicio web desde otro (servicio web naturalmente) pasandole parametros de tipo clase. Detallo a continuacion el problema a solucionar:
Un cliente invoca un servicio web "A" y envia como parametro una clase (objeto definido en "A"), el servicio recibe la peticion e invoca a otro servicio web ("B"), este servicio recibe como parametro una clase parecida pero no igual a la definida en "A",  el servicio web "A" hace la llamada a "B" , este retorna el resultado al servicio web "A" y este se encarga de reenviarlo finalmente al cliente.
Aparentemente no tiene nada de extraordinario, sin embargo existe varios puntos a considerar:

- 1ro el cliente usa solo un metodo para obtener la información, se conecta a "A" asimismo instancia su clase y  la envia  como parametro. Para el la consulta a otro servicios es transparente (osea nunca se entera de ello).

- 2do el servicio web "A" se encarga de ingresar los parametros al servicio "B". Es decir hace una transformacion de su clase a la clase que necesita para invocar a "B".

 

Contenido

Veamos como vamos a resolver este problema:

Definimos la clase de "A" y el servicio web:

' Es clase es el parametro que debemos enviar al servicio "A"
public class clpersona()
{
    public string  nombre;
    public string  apellido;
    public string  direccion;
}

El servicio web :
[WebService(Namespace= "http://200.43.48.125/servicioA")]
public class servicioA : System.web.services.webservice{
    [WebMethod]
    [XmlInclude(typeof(clpersona))]
    public string  obtenerpersonaA(clpersona parampersonaA){

        .........
    }
}

Definimos a la clase de "B" y el servicio web
' Esta clase es el parametro que debemos enviar al servicio "B"
public class
clpersona()
{
    public string  nombre;
    public string  apellido;
}

El servicio web:
[WebService(Namespace= "http://200.40.78.155/servicioB")]
public class servicioB : System.web.services.webservice{
    [WebMethod]
    [XmlInclude(typeof(clpersona))]
    public string  obtenerpersonaB(clpersona parampersona){

        .........
    }
}

Note que las clases clpersona de A y clpersona de B son casi identicas salvo el atributo direccion de la clase clpersona de A. Cuando invoquemos al servicio de B se debería asignar solamente los campos comunes y obviar los demas.
El cliente instancia la clase clpersonaA y hace la llamada al servicio A. 

clpersona varpersonaA = new clpersona();
servicioA servicio = new servicioA();
varpersonaA.nombre = "Juanito";
varpersonaA.apellido = "Alimana";
varpersonaA.direccion = "direccion";
servicio.obtenerpersonaA(varpersonaA);

El servicio A recibe la clase y ahora debe invocar al servicio B

clpersona varpersonaB = new clpersona();
servicioB servicio = new servicioB();
varpersonaB.nombre = parampersonaA.nombre
varpersonaB.apellido = parampersonaA.apellido
servicio.obtenerpersonaB(varpersonaB);
    
Ahora como vemos es simple, sin embargo cuando la clase persona de A sea mas compleja, por decir tenga herencia, tenga mas de 15 parametros o quizas en vez de una instancia de la clase sea todo un arreglo de clases, pues la cosa se complica y el problema se presenta en la asignacion de una clase a la otra.
La solucion que encontre es la siguiente:
- Definir un proceso de serializacion de la clase persona de A y deserializacion en la clase persona de B. Es decir llevo la clase persona de A a xml y luego de xml construyo la clase persona de B.
- Modificar el namespaces http://200.40.78.155/servicioB  al serializar persona de A porque si quiero deserializaro en la clase persona de B no voy a poder puesto que persona de B tiene otro nombre de espacio.
- Acotamos que las clases pueden o no tener los mismos atributos, pueden o no tener el mismo nombre (para este caso es necesario hacer mas cambios en la serialización que en este ejemplo no estamos considerando).
- Cuando se deserialice solo se asignaran los campos comunes y es necesario que los nombres de las clases sean las mismas.

Serializacion

El procedimiento sera el siguiente:

XmlSerializer serializer= null;
FileStream stream= null;
string path= HttpContext.CurrentServer.MapPath(".") + "archivoserializado.xml";
XmlAttributes atributos = new XmlAttributes();
XmlTypeAttribute tipo = new XmlTypeAttribute();
//sin esta linea la deserializacion no funcionara
tipo.namespace="namespace del servicio B";
atributos.XmlType= tipo;
XmlAttributesOverrides atributosserialisation = new XmlAttributesOverrides();
atributosserialisation.Add(typeof(servicioA.clpersona), atributos );
serializer= new XmlSerializer(typeof(servicioA.clpersonaA),atributosserialisation);
stream= new FileStream(path, FileMode.Create, FileAcces.Write);
serializer.Serialize(stream,< instancia  de la clase A>);

Ahora veamos la deserializacion. 

Deserializacion


clPersona resultadopersona= new clPersona();
XmlSerializer deserializer= new XmlSerializer(typeof(clpersona);//persona de B
//estas dos lineas de codigo para controlar las excepciones en caso de que algun campo no coincida
desserializer.UnKnownNode=+ new XmlNodeEventHandler(nombredeevento1);
desserializer.UnKnownAttribute=+ new XmlNodeEventHandler(nombredeevento2);
string path= HttpContext.CurrentServer.MapPath(".") + "archivoserializado.xml";
XmlReader lector = new XmlAttributes();
resultadopersona = (clpersona)deserializer.Deserialize(lector);

Espero que el ejemplo este comprensible y sobre todo que pueda servir para aquellos que desean implementar sistemas con mas de un servicio web y que se encuentren distribuidos en toda la red.
GBY

 

Espacios de nombres usados en el código de este artículo:

System.xml
System.xml.serialization.
System.IO.

 



 

Ir al índice principal de el Guille