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