10 de marzo de 2004

Intercambiando Datos entre VFP 8.0 y .Net mediante XML Web Services

Autor: José G. Samper

Cuando estamos intercambiando datos entre VFP 8.0 y .Net mediante XML Web Services, nos hemos encontrado que la interpretación del Xml por parte a una de las dos herramientas no resulta tan transparente como desearíamos. Con este artículo pretendemos explicar como se realiza de forma sencilla esta trasferencia para que la interpretación sea mucho más sencilla por parte de la herramienta que recibe los datos.

DESDE VFP 8.0 A .NET

ENVIANDO

Si deseamos enviarlo en forma de texto

Cuando deseamos enviar datos desde VFP 8.0 mediante un XML que represente un Dataset este será enviado en forma de texto por consiguiente la interpretación por parte de .Net debe ser de esa forma. Ejemplo:

LOCAL oXMLAdapter AS XMLADAPTER,lcXml AS STRING
LOCAL loXMLAdapter AS XMLADAPTER
LOCAL loDOM, loData, loSchema, loNode
CREATE CURSOR PortalFox (ID i, nombre c(250))
INSERT INTO PortalFox (ID,nombre) VALUES (1,'PortalFox')
lcXml=''
&&&Creamos el Objeto XMLAdapter
oXMLAdapter=CREATEOBJECT('xmladapter')
WITH oXMLAdapter
  .RELEASEXML(.T.) &&& limpiamos el XML por si este objeto fue usado para interpretar otro xml
  .UTF8ENCODED = .F. &&& Colocamos UTF8Encoded en Falso para no tener problemas con los acentos al momento de interpretar el XML
  .ISDIFFGRAM = .F.
  llIncludeBefore = .F.
  llChangesOnly = .F.
  llIsFile = .F.
  lcSchemaLocation = ""
  lcAlias='PortalFox'
&&& Invocamos este metodo por cada tabla que necesitemos incluir en el Dataset
  .ADDTABLESCHEMA(lcAlias,.F.,STRCONV(lcAlias,12,1033),STRCONV("FoxData",12,1033),STRCONV("",12,1034))
&&& convertimos los datos contenidos en las tablas agregadas al esquema a Xml
  .TOXML("lcXml",lcSchemaLocation,llIsFile,llIncludeBefore,llChangesOnly)
ENDWITH
RETURN lcXml

Si deseamos enviarlo en forma de objeto XMLDOM

Cuando deseamos enviar datos desde VFP 8.0 mediante un objeto que represente un “Dataset” este será enviado por SOAP en forma de XMLDOM consiguiente la interpretación por parte de .Net debe ser de esa forma. Ejemplo:

LOCAL oXMLAdapter AS XMLADAPTER,lcXml AS STRING
LOCAL loXMLAdapter AS XMLADAPTER
LOCAL loDOM, loData, loSchema, loNode
CREATE CURSOR PortalFox (ID i, nombre c(250))
INSERT INTO PortalFox (ID,nombre) VALUES (1,'PortalFox')
lcXml=''
&&&Creamos el Objeto XMLAdapter
oXMLAdapter=CREATEOBJECT('xmladapter')
WITH oXMLAdapter
  .RELEASEXML(.T.) &&& limpiamos el XML por si este objeto fue usado para interpretar otro xml
  .UTF8ENCODED = .F. &&& Colocamos UTF8Encoded en Falso para no tener problemas con los acentos al momento de interpretar el XML
  .ISDIFFGRAM = .F.
  llIncludeBefore = .F.
  llChangesOnly = .F.
  llIsFile = .F.
  lcSchemaLocation = ""
  lcAlias='PortalFox'
&&& Invocamos este metodo por cada tabla que necesitemos incluir en el Dataset
  .ADDTABLESCHEMA(lcAlias,.F.,STRCONV(lcAlias,12,1033),STRCONV("FoxData",12,1033),STRCONV("",12,1034))
&&& convertimos los datos contenidos en las tablas agregadas al esquema a Xml
  .TOXML("lcXml",lcSchemaLocation,llIsFile,llIncludeBefore,llChangesOnly)
ENDWITH
loXMLAdapter = CREATEOBJECT('xmladapter')
loXMLAdapter.LOADXML(lcXML)
&&Buscamos la referencia del objeto XMLDOM
loDOM = loXMLAdapter.IXMLDOMELEMENT
loNode = loDOM.childNodes.ITEM(0)
IF loNode.nodeName == "xsd:schema"
  loSchema = loNode
  loData = loDOM.childNodes.ITEM(1)
ELSE
  loSchema = loDOM.childNodes.ITEM(1)
  loData = loNode
ENDIF
lcXML = loSchema.XML + loData.XML
RETURN lcXML

RECIBIENDO

Si estamos recibiendo el XML forma de texto

El error mas común al recibir el Xml en .Net, es que usamos directamente el método ReadXml del objeto Dataset y este normalmente nos devuelve el error "URI no válido: el esquema del URI no es válido.". Ejemplo:

Ws_o_Com  oComWS= new  Ws_o_Com();
string lcXml;
System.Data.DataSet dsEjemplo = new  System.Data.DataSet ();
lcXml = oComWS.metodollamado(this.TextBox1.Text);
dsEjemplo.ReadXml(lcXml);
grdDatasetSample.DataSource= dsEjemplo;
grdDatasetSample.Refresh();

Como deberíamos hacerlo, bueno la forma mas sencilla que he encontrado de hacer esto es creando los objetos System.IO.StringReader y System.Xml.XmlTextReader, los cuales me permiten la correcta interpretación del XML recibido. Ejemplo:

Ws_o_Com  oComWS= new  Ws_o_Com();
string lcXml;
System.Data.DataSet dsEjemplo = new  System.Data.DataSet ();
lcXml = oComWS.metodollamado(this.TextBox1.Text);
System.IO.StringReader Sx =  new System.IO.StringReader(lcXml);
System.Xml.XmlTextReader oXmlReader =  new System.Xml.XmlTextReader(Sx);
dsEjemplo.ReadXml(oXmlReader);
grdDatasetSample.DataSource= dsEjemplo;
grdDatasetSample.Refresh();

Si estamos recibiendo un objeto XMLDOM

Esta es la forma más sencilla de recibir datos desde VFP 8.0 a .Net debido a que .Net interpreta los datos de una forma natural. Hasta el momento solo he podido usar esta forma de recibir datos desde VFP 8.0 cuando le envió datos a un Web Service hecho en .Net, debido a que no he podido encontrar un objeto en VFP 8.0 que .Net lo convierta de forma implícita a Dataset

public bool  ActualizaDatos(DataSet  ds, ref  string cErrorMessage)
   {
        Clientes oClientes = new  Clientes();
        dsEjemplo = oClientes.Actualizar (ds, cErrorMessage);
    }

DESDE .NET A VFP 8.0

ENVIANDO

Si deseamos enviarlo en forma de texto

Cuando deseamos enviar datos desde .Net mediante un Dataset en forma de texto debemos convertir el Dataset en String y consiguiente la interpretación por parte de VFP 8.0 debe ser de esa forma. Ejemplo:

Implementamos un WebMethod que devuelva los datos solicitados y nosotros debemos hacer hacer las conversiones correspondientes.

public String GetClientes()
{
System.Data.DataSet dsEjemplo = new  System.Data.DataSet ();
  Clientes oClientes = new  Clientes ();
  dsEjemplo = oClientes.GetClientes();
System.IO.StringWriter swXML = new System.IO.StringWriter();
dsEjemplo.WriteXml(swXML,System.Data.XmlWriteMode.WriteSchema);
return swXML.ToString();
}

Se recomienda que al momento de generar el XML para ser enviado a VFP 8.0 este sea generado con el esquema para una correcta interpretación de los datos por parte de VFP 8.0

Si deseamos enviarlo en forma de objeto DataSet

Cuando deseamos enviar datos desde .Net mediante un Dataset este será enviado por SOAP en forma de XMLDOM, consiguiente la interpretación por parte de VFP 8.0 debe ser de esa forma. Ejemplo:

Implementamos un WebMethod que devuelva los datos solicitados y SOAP se encarga de hacer las conversiones correspondientes.

public DataSet  GetClientes()
{
  Clientes oClientes = new  Clientes ();
  return oClientes.GetClientes();
}

NOTA: Se recomienda que los dataset que sean enviados desde .Net a VFP 8.0 sean tipeados debido a que si VFP 8.0 recibe un tipo de datos String que no contenga el largo del mismo lo convierte automáticamente en Memo

RECIBIENDO

Cuando invocamos un Web Services de .Net desde VFP 8.0 tenemos dos formas de recibir los datos, esto depende de cómo se haya implementado el WebMethod.

1.- En forma de Objeto, .Net puede devolver un objeto “Dataset” y SOAP lo transporta como un objeto XMLDOM. En estos casos es necesario hacer un Attach del objeto recibido y no un LoadXml. Ejemplo:

LOCAL loNetWebService AS "XML Web Service"
LOCAL loException, lcErrorMsg, loWSHandler
LOCAL loObj, lxXML, loSchema
LOCAL oXMLAdapter AS XMLADAPTER
TRY
  loWSHandler = NEWOBJECT("WSHandler",IIF(VERSION(2)=0,"",HOME()+"FFC")+"_ws3client.vcx")
  loNetWebService = loWSHandler.SetupClient("http://PortalFox/NetWebService.asmx?wsdl", " NetWebService ", " NetWebService ")
  oObj = loNetWebService.wsObject.metodoallamar()
  IF VARTYPE(oObj)="O"
    loSchema = loObj.ITEM(0)
    lxXML = loObj.ITEM(1)
    oXMLAdapter=CREATEOBJECT('xmladapter')
    oXMLAdapter.RELEASEXML(.T.)
    IF VARTYPE(lxXML)="O" AND VARTYPE(loSchema)="O"
      oXMLAdapter.ATTACH(txXML, toSchema)
    ENDIF
  ENDIF
CATCH TO loException
  **Manejo de Errores
FINALLY
ENDTRY

2.- En forma de texto, .Net puede devolver un Xml y SOAP lo transporta como un String. En estos casos es necesario hacer un LoadXml del String recibido y no un Attach, se debe tener en cuenta que si el String recibido no contiene el esquema y no se posee el esquema des mismo no será interpretado por VFP 8.0. Ejemplo:

LOCAL loNetWebService AS "XML Web Service"
LOCAL loException, lcErrorMsg, loWSHandler
LOCAL lxXML
LOCAL oXMLAdapter AS XMLADAPTER
TRY
  loWSHandler = NEWOBJECT("WSHandler",IIF(VERSION(2)=0,"",HOME()+"FFC")+"_ws3client.vcx")
  loNetWebService = loWSHandler.SetupClient("http://PortalFox/NetWebService.asmx?wsdl", " NetWebService ", " NetWebService ")
  lxXML = loNetWebService.wsObject.metodoallamar()
  IF TYPE('lxXML')='C' AND !EMPTY(lxXML)
    oXMLAdapter=CREATEOBJECT('xmladapter')
    oXMLAdapter.RELEASEXML(.T.)
    oXMLAdapter.LOADXML(lxXML)
  ENDIF
CATCH TO loException
  **Manejo de Errores
FINALLY
ENDTRY

Por último lo que les puedo decir es que aunque es muy fácil y practico el intercambio de datos mediante Web Services entre VFP 8.0 y .Net, deben tener en cuenta que esto se realiza mediante paquetes SOAP, así que deben tener mucho cuidado con la cantidad de datos que envían.

Espero les Sirva

José G. Samper

No hay comentarios. :

Publicar un comentario