{"id":123,"date":"2017-04-22T16:01:12","date_gmt":"2017-04-22T21:01:12","guid":{"rendered":"http:\/\/delphienmovimiento.mx\/wp\/?p=123"},"modified":"2020-02-05T18:54:37","modified_gmt":"2020-02-05T18:54:37","slug":"consumir-un-servicio-web-con-delphi-10-2-starter-y-curl","status":"publish","type":"post","link":"https:\/\/www.delphienmovimiento.mx\/wp\/2017\/04\/22\/consumir-un-servicio-web-con-delphi-10-2-starter-y-curl\/","title":{"rendered":"Consumir un servicio web con Delphi 10.2 Starter y cURL"},"content":{"rendered":"<p style=\"text-align: left;\">Hola amigos,<\/p>\n<p style=\"text-align: justify;\">En \u00e9sta entrada les hablar\u00e9 acerca de la versi\u00f3n <em>&#8216;gratuita&#8217;<\/em> de <strong><em>Delphi<\/em><\/strong> llamada <em><strong>Delphi Starter Edition, versi\u00f3n 10.2 Tokyo<\/strong><\/em>\u00a0la cual\u00a0nos permite, seg\u00fan su contrato de licencia, generar aplicaciones comerciales aunque con algunas limitaciones propias de \u00e9ste tipo de ediciones.<\/p>\n<p style=\"text-align: justify;\">Si quieren conocer acerca de \u00e9ste producto, su licencia de uso y las\u00a0limitaciones propias de la versi\u00f3n, les proporciono una lista de enlaces que\u00a0me parecen muy interesantes.<\/p>\n<ul>\n<li style=\"padding-left: 90px;\"><em><strong>&#8211; <a href=\"https:\/\/www.embarcadero.com\/products\/delphi\/starter-faq\" target=\"_blank\" rel=\"noopener noreferrer\">Preguntas y Respuestas.<\/a><\/strong><\/em><\/li>\n<li style=\"padding-left: 90px;\"><em><strong>&#8211; <a href=\"https:\/\/www.embarcadero.com\/docs\/Delphi-Feature-Matrix.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Matriz de caracter\u00edsticas<\/a>.<\/strong><\/em><\/li>\n<li style=\"padding-left: 90px;\"><em><strong>&#8211; <a href=\"https:\/\/www.youtube.com\/watch?v=LH0W-cgYuls\" target=\"_blank\" rel=\"noopener noreferrer\">Delphi \/ C++Builder Starter \u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u30b7\u30ea\u30fc\u30ba \u30b7\u30fc\u30ba\u30f32<\/a>.<\/strong><\/em><\/li>\n<\/ul>\n<p style=\"text-align: justify; padding-left: 90px;\">Quiero\u00a0mencionar que la intenci\u00f3n de \u00e9sta entrada es la de mostrar que con \u00e9sta versi\u00f3n podemos comenzar a desarrollar soluciones reales y no solo el famoso <em><strong>\u00abHola Mundo\u00bb<\/strong><\/em>. Por supuesto al ser un producto gratuito tiene algunas limitaciones pero nada que nos evite hacer uso de nuestras capacidades y dar forma a aplicaciones de alto rendimiento.<\/p>\n<p style=\"text-align: justify;\">Para \u00e9sta entrada les sugiero que descarguen <strong><em><a href=\"https:\/\/www.soapui.org\/downloads\/soapui.html\" target=\"_blank\" rel=\"noopener noreferrer\">el programa\u00a0soapUI<\/a><\/em><\/strong> <em>el cual es open source<\/em>\u00a0 y que nos facilitar\u00e1 la vida al generar un cliente para el\u00a0consumo de Servicios Web as\u00ed como\u00a0descargar e instalar\u00a0<a href=\"https:\/\/www.embarcadero.com\/es\/products\/delphi\/starter\/promotional-download\" target=\"_blank\" rel=\"noopener noreferrer\"><strong><em>Delphi 10.2 Tokio Starter<\/em><\/strong><\/a>.<\/p>\n<p style=\"text-align: justify;\">Una de las limitantes de la versi\u00f3n <em>Starter<\/em> es que no cuenta con el asistente\u00a0para la importaci\u00f3n del archivo <strong><em>WSDL<\/em><\/strong> el cual genera la unidad\u00a0que implementa los m\u00e9todos del <em>Servicio Web<\/em> para su consumo\u00a0y aunque \u00e9sta unidad se puede obtener v\u00eda l\u00ednea de comandos he querido hacerlo con una soluci\u00f3n alternativa a trav\u00e9s de <em><strong><a href=\"https:\/\/curl.haxx.se\">un proyecto llamado cURL<\/a><\/strong><\/em>\u00a0el cual est\u00e1 orientado a la transferencia de archivos y que\u00a0soporta varios protocolos como\u00a0<em><strong>FTP, FTPS, HTTP, HTTPS, TFTP, SCP, SFTP, Telnet, DICT, FILE y LDAP<\/strong><\/em>\u00a0entre otros.<\/p>\n<p style=\"text-align: justify;\">Cabe mencionar que \u00e9ste proyecto lo conoc\u00ed hace unos a\u00f1os, para ser exactos el 14 de Marzo de 2011, cuando\u00a0nuestro\u00a0amigo\u00a0<strong><em><a href=\"https:\/\/delphi.jmrds.com\" target=\"_blank\" rel=\"noopener noreferrer\">Domingo Seaone<\/a><\/em><\/strong>\u00a0\u00a0<strong><em><a href=\"http:\/\/delphiaccess.com\/foros\/index.php\/topic\/4613-enviar-correo-de-gmail-con-libcurl\/?hl=curl\" target=\"_blank\" rel=\"noopener noreferrer\">public\u00f3 en el foro delphiaccess punto com<\/a><\/em><\/strong> una implementaci\u00f3n de \u00e9sta biblioteca desarrollando una unidad\u00a0con Delphi y que me ha\u00a0permitido\u00a0dar soluci\u00f3n a\u00a0varios requerimientos de clientes a lo largo de \u00e9stos 6 a\u00f1os que han pasado.<\/p>\n<p style=\"text-align: justify;\">Pues bien, comenzaremos por obtener los archivos XML de Request y Response del Servicio Web a trav\u00e9s de la aplicaci\u00f3n <strong><em>soapUI<\/em><\/strong>, para ello usaremos el <em>Servicio Web del Banco de M\u00e9xico<\/em> para\u00a0consultar el tipo de cambio del peso frente al D\u00f3lar, el Euro, la Libra Esterlina y el Yen. La url del servicio web es la siguiente:<\/p>\n<p style=\"text-align: center;\"><strong><em><a href=\"http:\/\/www.banxico.org.mx\/DgieWSWeb\/DgieWS?WSDL\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/www.banxico.org.mx\/DgieWSWeb\/DgieWS?WSDL<\/a><\/em><\/strong><\/p>\n<p style=\"text-align: justify;\">Creamos un nuevo proyecto SOAP seleccionamos el m\u00e9todo <strong><em>tiposDeCambioBanxico<\/em><\/strong> y\u00a0enviamos la petici\u00f3n, el Servicio Web nos regresar\u00e1 la informaci\u00f3n en un archivo soap XML conteniendo los diferentes tipos de cambio como se muestra en la imagen siguiente:<\/p>\n<p style=\"text-align: justify;\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-131 aligncenter\" src=\"http:\/\/delphienmovimiento.mx\/wp\/wp-content\/uploads\/2017\/04\/Starter-01-1024x553.png\" alt=\"\" width=\"836\" height=\"451\" \/><\/p>\n<p style=\"text-align: justify;\">La versi\u00f3n Starter de Delphi tampoco incluye el asistente <em><strong>XML Data Binding<\/strong><\/em> por lo que tenemos que construir\u00a0el <em><strong>REQUEST<\/strong><\/em> y el <em><strong>RESPONSE<\/strong><\/em> de forma manual, bueno, no todo en la vida es f\u00e1cil, pero veamos la situaci\u00f3n como una buena oportunidad\u00a0para aprender algo nuevo.\u00a0\ud83d\ude42<\/p>\n<p style=\"text-align: justify;\">Ya estamos listos para comenzar nuestro tutorial, como primer paso vamos a crear una clase que generar\u00e1\u00a0la estructura del mensaje de petici\u00f3n\u00a0<em><strong>SOAP<\/strong><\/em> para enviarla a trav\u00e9s de <strong><em>cURL<\/em><\/strong> al servidor del Servicio Web y serializar la respuesta SOAP para obtener (en este caso) \u00a0los tipos de cambio disponibles del peso mexicano contra otras monedas.<\/p>\n<p style=\"text-align: justify;\">La clase contendr\u00e1\u00a0las propiedades, variables y m\u00e9todos necesarios para el\u00a0consumo del Servicio Web como se muestra a continuaci\u00f3n.<\/p>\n<p><em><strong>TRequestResponse = class<\/strong><\/em><\/p>\n<pre class=\"prettyprint lang-pascal\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">type\n\n  TRequestResponse = class\n  private\n    xmlArch: IXMLDocument;\n    envelopeSOAP: IXMLNode;\n\n    Curl: TCURL;\n    curlResult: CURLcode;\n    curlError: string;\n\n    Stream: TMemoryStream;\n    StreamOut: TMemoryStream;\n\n    function creaRequest: IXMLDocument;\n    function enviaRequest(url, Post: AnsiString; out Reply: AnsiString): Boolean;\n    function RecursiveFindNode(ANode: IXMLNode; const SearchNodeName: string): IXMLNode;\n    function buscaTC(xml: IXMLDocument): TStrings;\n\n  public\n    function ConsultaTipoDeCambio(endPoint: AnsiString): TStrings;\n\n  end;\n<\/pre>\n<p style=\"text-align: justify;\">El m\u00e9todo <em><strong>creaRequest<\/strong><\/em> genera el mensaje de petici\u00f3n <em><strong>SOAP<\/strong><\/em> utilizando el objeto <em><strong>IXMLDocument<\/strong><\/em> el cual se encargar\u00e1 de dar el formato adecuado\u00a0para poder enviarlo al servidor donde est\u00e1 alojado el Servicio Web que queremos consumir.<\/p>\n<p><em><strong>function TRequestResponse.creaRequest: IXMLDocument;<\/strong><\/em><\/p>\n<pre class=\"prettyprint lang-pascal\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">function TRequestResponse.creaRequest: IXMLDocument;\nbegin\n  xmlArch := NewXMLDocument;\n  xmlArch.Encoding := 'UTF-8';\n  envelopeSOAP := xmlArch.AddChild('soapenv:Envelope');\n  with envelopeSOAP do\n  begin\n    Attributes['xmlns:ws'] := 'http:\/\/ws.dgie.banxico.org.mx';\n    Attributes['xmlns:soapenv'] := 'http:\/\/schemas.xmlsoap.org\/soap\/envelope\/';\n    Attributes['xmlns:xsd'] := 'http:\/\/www.w3.org\/2001\/XMLSchema';\n    Attributes['xmlns:xsi'] := 'http:\/\/www.w3.org\/2001\/XMLSchema-instance';\n    AddChild('soapenv:Header');\n    with AddChild('soapenv:Body') do\n    begin\n      with AddChild('ws:tiposDeCambioBanxico') do\n      begin\n        Attributes['soapenv:encodingStyle'] := 'http:\/\/schemas.xmlsoap.org\/soap\/encoding\/';\n      end;\n    end;\n  end;\n  Result := xmlArch;\nend;\n<\/pre>\n<p style=\"text-align: justify;\">Con \u00e9ste c\u00f3digo se\u00a0genera el mensaje SOAP necesario para realizar la petici\u00f3n al Servicio Web y que ser\u00e1 enviado por medio de cURL.<\/p>\n<p style=\"text-align: justify;\">Por supuesto que habr\u00e1 formas m\u00e1s efectivas de hacer esto, sin embargo \u00e9sta me parece a mi la m\u00e1s simple y controlada.<\/p>\n<pre class=\"prettyprint lang-xml\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;soapenv:Envelope xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\" \n                  xmlns:soapenv=\"http:\/\/schemas.xmlsoap.org\/soap\/envelope\/\" xmlns:ws=\"http:\/\/ws.dgie.banxico.org.mx\"&gt;\n\t&lt;soapenv:Header\/&gt;\n\t&lt;soapenv:Body&gt;\n\t\t&lt;ws:tiposDeCambioBanxico soapenv:encodingStyle=\"http:\/\/schemas.xmlsoap.org\/soap\/encoding\/\"\/&gt;\n\t&lt;\/soapenv:Body&gt;\n&lt;\/soapenv:Envelope&gt;\n<\/pre>\n<p style=\"text-align: justify;\">A continuaci\u00f3n agregaremos la unidad creada por mi amigo <em><strong><a href=\"https:\/\/delphi.jmrds.com\" target=\"_blank\" rel=\"noopener noreferrer\">Domingo Seoane<\/a><\/strong><\/em> la cual encapsula el m\u00e9todo para enviar la petici\u00f3n al Servicio Web, dicha unidad se llama <em><strong><a href=\"http:\/\/delphiaccess.com\/foros\/index.php\/topic\/4613-enviar-correo-de-gmail-con-libcurl\/#entry48551\" target=\"_blank\" rel=\"noopener noreferrer\">uCurl.pas<\/a><\/strong><\/em> la cual public\u00f3 en DelphiAccess.<\/p>\n<p style=\"text-align: justify;\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-179 aligncenter\" src=\"http:\/\/delphienmovimiento.mx\/wp\/wp-content\/uploads\/2017\/04\/Starter-02.png\" alt=\"\" width=\"308\" height=\"388\" \/><\/p>\n<p style=\"text-align: justify;\">A continuaci\u00f3n agregamos la funci\u00f3n para realizar\u00a0la llamada a dicha\u00a0unidad y que\u00a0llamaremos\u00a0<em><strong>enviaRequest<\/strong><\/em>. En \u00e9sta funci\u00f3n\u00a0necesitaremos la <em><strong>URL<\/strong><\/em> del Servicio Web y el <strong><em>Request<\/em><\/strong>\u00a0que hemos creado con la funci\u00f3n <strong><em>creaRequest<\/em><\/strong>, dicha funci\u00f3n regresar\u00e1 dos valores, uno de tipo <em><strong>out<\/strong><\/em> donde obtendr\u00e9mos el <em><strong>Response<\/strong><\/em> del Servicio Web y un <em><strong>boolean<\/strong><\/em>\u00a0que nos indicar\u00e1 si la funci\u00f3n fu\u00e9\u00a0procesada correctamente o no.<\/p>\n<p><em><strong>function TRequestResponse.enviaRequest(url, Post: AnsiString; out Reply: AnsiString): Boolean;<\/strong><\/em><\/p>\n<pre class=\"prettyprint lang-pascal\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">function TRequestResponse.enviaRequest(url, Post: AnsiString; out Reply: AnsiString): Boolean;\n\n\/\/******************************************************************************\n      function ReadFromStream(Buffer: PAnsiChar; Size, Count: Integer;\n        Stream: TStream): Integer; cdecl;\n      begin\n        Result:= Stream.Read(Buffer^,Size*Count) div Size;\n      end;\n\n      function SaveToStream(Buffer: PAnsiChar; Size, Count: Integer;\n        Stream: TStream): Integer; cdecl;\n      begin\n        Result:= Stream.Write(Buffer^,Size*Count) div Size;\n      end;\n\n      function MemoryStreamToString(M:TMemoryStream): AnsiString;\n      begin\n        SetString(Result, PAnsiChar(M.Memory), M.Size);\n      end;\n\n      procedure ErrorCurl(const mensaje: string);\n      begin\n        raise Exception.Create(mensaje);\n        exit;\n      end;\n\/\/******************************************************************************\n\nbegin\n  Result:= false;\n  Curl:= curl_easy_init;\n  if Curl &lt;&gt; nil then\n  try\n    if curl_easy_setopt(Curl, CURLOPT_VERBOSE, TRUE) &lt;&gt; CURLE_OK then\n      ErrorCurl('No se pudo asignar CURLOPT_VERBOSE.');\n    if curl_easy_setopt(Curl, CURLOPT_USE_SSL, CURLUSESSL_ALL) &lt;&gt; CURLE_OK then\n      ErrorCurl('No se pudo asignar CURLOPT_USE_SSL.');\n    if curl_easy_setopt(Curl, CURLOPT_SSL_VERIFYPEER, FALSE) &lt;&gt; CURLE_OK then\n      ErrorCurl('No se pudo asignar CURLOPT_SSL_VERIFYPEER.');\n    if curl_easy_setopt(Curl, CURLOPT_URL, PAnsiChar(url)) &lt;&gt; CURLE_OK then\n      ErrorCurl('No se pudo asignar CURLOPT_URL.');\n    if curl_easy_setopt(Curl, CURLOPT_POST, 1) &lt;&gt; CURLE_OK then\n      ErrorCurl('No se pudo asignar CURLOPT_POST.');\n    if curl_easy_setopt(Curl, CURLOPT_READFUNCTION, @ReadFromStream) &lt;&gt; CURLE_OK then\n      ErrorCurl('No se pudo asignar CURLOPT_READFUNCTION.');\n    if curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, @SaveToStream) &lt;&gt; CURLE_OK then\n      ErrorCurl('No se pudo asignar CURLOPT_WRITEFUNCTION.');\n\n    Stream    := TMemoryStream.Create;\n    StreamOut := TMemoryStream.Create;\n\n    try\n      with TStringList.Create do\n      try\n        Add(unicodeString(Post));\n        SaveToStream(Stream);\n      finally\n        Free;\n      end;\n      Stream.Position:= 0;\n      if curl_easy_setopt(Curl, CURLOPT_INFILE, Stream) &lt;&gt; CURLE_OK then\n        ErrorCurl('No se pudo asignar CURLOPT_INFILE.');\n      if curl_easy_setopt(Curl, CURLOPT_POSTFIELDSIZE, Stream.Size) &lt;&gt; CURLE_OK then\n        ErrorCurl('No se pudo asignar CURLOPT_POSTFIELDSIZE.');\n      Header := nil;\n      Header := curl_slist_append(Header, 'SOAPAction: \"\"');\n      Header := curl_slist_append(Header, 'Accept-Encoding: gzip, deflate');\n      Header := curl_slist_append(Header, 'Content-Type: text\/xml;charset=UTF-8;');\n      Header := curl_slist_append(Header, 'User-Agent: Apache-HttpClient\/4.1.1 (java 1.5)');\n      try\n        if curl_easy_setopt(Curl, CURLOPT_FILE, StreamOut) &lt;&gt; CURLE_OK then\n          ErrorCurl('No se pudo asignar CURLOPT_FILE.');\n        if curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Header) &lt;&gt; CURLE_OK then\n          ErrorCurl('No se pudo asignar CURLOPT_HTTPHEADER.');\n\n        curlResult := curl_easy_perform(Curl);\n\n        if curlResult = CURLE_OK then\n        begin\n          Reply := MemoryStreamToString(StreamOut);\n          curlError := '';\n          result := true;\n        end\n        else begin\n               curlError := curl_easy_strerror(curlResult);\n               result := false;\n        end;\n\n      finally\n        curl_slist_free_all(Header);\n      end;\n    finally\n      Stream.Free;\n      StreamOut.Free;\n    end;\n  finally\n    curl_easy_cleanup(Curl);\n  end;\nend;\n<\/pre>\n<p style=\"text-align: justify;\">Ya estamos a punto de terminar la\u00a0clase que consumir\u00e1 el Servicio Web, solo nos falta serializar el <em>Response<\/em> del Servicio Web lo cual har\u00e9mos manualmente\u00a0debido a que\u00a0tenemos ciertas limitantes al no tener disponible\u00a0el asistente <em>XML Data Binding<\/em> en el IDE de nuestro Delphi Starter. Cabe mencionar que si ustedes tienen una forma m\u00e1s elegante de hacerlo, no duden en retroalimentarnos para enriquecer \u00e9ste tutorial.<\/p>\n<p>Para ello vamos a utilizar dos funciones, una que tome prestada del sitio\u00a0 <a href=\"http:\/\/stackoverflow.com\" target=\"_blank\" rel=\"noopener noreferrer\">stackoverflow<\/a> y una que escribiremos nosotros mismos, dichas funciones son<\/p>\n<p style=\"text-align: justify;\"><em><strong>function RecursiveFindNode(ANode: IXMLNode; const SearchNodeName: string): IXMLNode;<\/strong><\/em><\/p>\n<pre class=\"prettyprint lang-pascal\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">{\n   Funci\u00f3n basada en la original que se tom\u00f3 del siguiente enlace\n   http:\/\/stackoverflow.com\/questions\/28901757\/delphi-find-xml-node\n}\nfunction TRequestResponse.RecursiveFindNode(ANode: IXMLNode; const SearchNodeName: string): IXMLNode;\nvar\n  I: Integer;\nbegin\n  if CompareText(ANode.NodeName, SearchNodeName) = 0 then\n      Result := ANode\n  else if not Assigned(ANode.ChildNodes) then\n          Result := nil\n       else begin\n              for I := 0 to ANode.ChildNodes.Count - 1 do\n              begin\n                Result := RecursiveFindNode(ANode.ChildNodes[I], SearchNodeName);\n                if Assigned(Result) then\n                   Exit;\n              end;\n       end;\nend;\n<\/pre>\n<p style=\"text-align: justify;\"><em><strong>function buscaTC(xml: IXMLDocument): TStrings;<\/strong><\/em><\/p>\n<pre class=\"prettyprint lang-pascal\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">function TRequestResponse.buscaTC(xml: IXMLDocument): TStrings;\nvar\n  I: integer;\n  nodoData: IXMLNode;\n  Linea: string;\nbegin\n  Result := TStringList.Create;\n  Result.Text := '';\n  nodoData := xml.ChildNodes[1].ChildNodes[1];\n  if nodoData &lt;&gt; nil then\n  begin\n    for I := 0 to nodoData.ChildNodes.Count-1 do\n    begin\n      if nodoData.ChildNodes[I].NodeName = 'bm:Series' then\n      begin\n         Result.Add(nodoData.ChildNodes[I].Attributes['TITULO'] + #9 +\n                    nodoData.ChildNodes[I].Attributes['BANXICO_UNIT_TYPE'] + #9 +\n                    nodoData.ChildNodes[I].ChildNodes[0].Attributes['TIME_PERIOD'] + #9 +\n                    nodoData.ChildNodes[I].ChildNodes[0].Attributes['OBS_VALUE'] );\n      end;\n    end;\n  end;\nend;\n<\/pre>\n<p>Por \u00faltimo vamos a ewscribir el c\u00f3digo de la funci\u00f3n principal de nuestra clase llamado ConsultaTipoDeCambio y que requiere de la URL del Servicio Web la cual enviaremos desde la forma principal.<\/p>\n<p><em><strong>function TRequestResponse.ConsultaTipoDeCambio(endPoint: AnsiString): TStrings;<\/strong><\/em><\/p>\n<pre class=\"prettyprint lang-pascal\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">function TRequestResponse.ConsultaTipoDeCambio(endPoint: AnsiString): TStrings;\nvar\n  nodoBody: IXMLNode;\n  nodoResult: IXMLNode;\n  xml: IXMLDocument;\n  response: AnsiString;\n\nbegin\n  xml := creaRequest;\n  xml.XML.SaveToFile(ExtractFilePath(Application.ExeName) + 'request_' +\n                               formatDateTime('yyyymmdd_hhnnss',now) + '.xml' );\n  if sendRequest(endPoint, AnsiString(xml.XML.Text), response) then\n  begin\n    try\n      xmlArch := NewXMLDocument;\n      xmlArch.XML.Text := response;\n      xmlArch.Active := true;\n      nodoBody := xmlArch.ChildNodes[1].ChildNodes[1];\n      if nodoBody &lt;&gt; nil then\n      begin\n        nodoResult := RecursiveFindNode(NodoBody, 'result');\n        if nodoBody &lt;&gt; nil then\n        begin\n          xml := TXMLDocument.Create(nil);\n          xml.XML.Text := nodoResult.ChildNodes[0].NodeValue;\n          xml.Active := true;\n          xml.XML.SaveToFile(ExtractFilePath(Application.ExeName) +\n                                                'response.xml', TEncoding.ANSI);\n          Result := BuscaTC(xml);\n        end\n        else begin\n               Result.Text := 'ERROR: No se pudo acceder al nodo \"Result\"';\n        end;\n      end\n      else begin\n             Result.Text := 'ERROR: No se pudo acceder al nodo \"Body\"';\n      end;\n    except\n      on e: exception do\n         Result.Text := 'ERROR: ' + e.Message;\n    end;\n  end;\n\nend;\n<\/pre>\n<p style=\"text-align: justify;\">Ya tenemos nuestra unidad\u00a0<strong><em>uXMLClass.pas<\/em><\/strong> la cual contiene la clase\u00a0<em><strong>TRequestResponse<\/strong><\/em> completa y lista para ser usada por el programa principal.<\/p>\n<p style=\"text-align: justify;\">En la forma principal vamos a agregar dos componentes <em><strong>TButton<\/strong><\/em> y un componente <em><strong>TMemo<\/strong><\/em>. Perd\u00f3n pero suelo ser <em>minimalista<\/em> \ud83d\ude00<\/p>\n<p style=\"text-align: justify;\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-200 aligncenter\" src=\"http:\/\/delphienmovimiento.mx\/wp\/wp-content\/uploads\/2017\/04\/Starter-03.png\" alt=\"\" width=\"871\" height=\"531\" \/><\/p>\n<p>Acomodamos los componentes a nuestro gusto y escribiremos el siguiente c\u00f3digo en el evento <strong><em>OnClick<\/em><\/strong> del componente <em><strong>TButton<\/strong><\/em>.<\/p>\n<pre class=\"prettyprint lang-pascal\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">procedure TForm1.Button1Click(Sender: TObject);\nvar\n  ReqResp: TRequestResponse;\n  retorno: TStrings;\nbegin\n  ReqResp := TRequestResponse.Create;\n  retorno := TStringList.Create;\n  retorno := ReqResp.ConsultaTipoDeCambio(AnsiString('http:\/\/www.banxico.org.mx\/DgieWSWeb\/DgieWS'));\n  Memo1.Lines := retorno;\nend;\n<\/pre>\n<p style=\"text-align: justify;\">Finalmente ejecutamos nuestra aplicaci\u00f3n y\u00a0deber\u00edamos obtener la siguiente respuesta.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-201\" src=\"http:\/\/delphienmovimiento.mx\/wp\/wp-content\/uploads\/2017\/04\/Starter-04.png\" alt=\"\" width=\"684\" height=\"430\" \/><\/p>\n<p style=\"text-align: justify;\">Con \u00e9sto concluimos \u00e9ste peque\u00f1o tutorial donde pretendemos mostrar que se puede trabajar con \u00e9sta edici\u00f3n de Delphi y crear aplicaciones listas para dar soluci\u00f3n a requerimientos\u00a0reales. Por supuesto que\u00a0hay <em>de\u00a0requerimientos a requerimientos<\/em>\u00a0pero la idea es comenzar nuestro\u00a0<em><strong>\u00abpropio negocio\u00bb<\/strong><\/em> y poder obtener ingresos suficientes para adquirir una de las versiones de pago y dar el siguiente paso para que nuestro negocio se reafirme y podamos dar soluciones completas y mas profesionales.<\/p>\n<p style=\"text-align: justify;\">Antes de despedirme les quiero decir que vamos a estar desarrollando algunos temas con Delphi Starter con lo que pretendemos\u00a0aportar nuestro granito de arena,\u00a0si tienen alg\u00fan tema que pueda ser creado\u00a0con \u00e9sta versi\u00f3n con gusto lo intentaremos desarrollar.<\/p>\n<p>Les agradecer\u00e9\u00a0que dejen sus comentarios, cr\u00edticas y\/o\u00a0palabras de aliento que me dar\u00e1n el ox\u00edgeno necesario para\u00a0continuar con \u00e9sta serie. \ud83d\ude42<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-38 alignleft\" src=\"http:\/\/delphienmovimiento.mx\/wp\/wp-content\/uploads\/2017\/03\/gone.png\" alt=\"\" width=\"137\" height=\"240\" \/><\/p>\n<p>Hasta la pr\u00f3xima<\/p>\n<hr \/>\n<blockquote>\n<p style=\"text-align: justify;\">Todo el c\u00f3digo escrito aqu\u00ed es de libre descarga y utilizaci\u00f3n\u00a0solo te pido, si te parece correcto, que menciones su origen y claro cualquier mejora que le hagas publicala que se agradecer\u00e1 enormemente.<\/p>\n<p style=\"text-align: justify;\">Muchas gracias.<\/p>\n<\/blockquote>\n\n\n","protected":false},"excerpt":{"rendered":"<p>Hola amigos, En \u00e9sta entrada les hablar\u00e9 acerca de la versi\u00f3n &#8216;gratuita&#8217; de Delphi llamada Delphi Starter Edition, versi\u00f3n 10.2 Tokyo\u00a0la cual\u00a0nos permite, seg\u00fan su contrato de licencia, generar aplicaciones comerciales aunque con algunas limitaciones propias de \u00e9ste tipo de ediciones. Si quieren conocer acerca de \u00e9ste producto, su licencia de uso y las\u00a0limitaciones propias [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2341,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[177,178],"tags":[26,181,183,61,188,189,191,157],"class_list":["post-123","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dslaserie","category-tutos","tag-banxico","tag-curl-delphi","tag-delphi-10-2-tokio","tag-delphiaccess","tag-servicio-web","tag-soapui","tag-starter","tag-wsdl"],"wppr_data":{"cwp_meta_box_check":"No"},"_links":{"self":[{"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/posts\/123","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/comments?post=123"}],"version-history":[{"count":2,"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/posts\/123\/revisions"}],"predecessor-version":[{"id":2346,"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/posts\/123\/revisions\/2346"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/media\/2341"}],"wp:attachment":[{"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/media?parent=123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/categories?post=123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.delphienmovimiento.mx\/wp\/wp-json\/wp\/v2\/tags?post=123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}