<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>sgoliver.net blog</title>
	<atom:link href="http://www.sgoliver.net/blog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.sgoliver.net/blog</link>
	<description>Pensamientos varios sobre programación, Android, .NET y Java</description>
	<lastBuildDate>Fri, 20 Apr 2012 10:45:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Acceso a Servicios Web REST en Android (2/2)</title>
		<link>http://www.sgoliver.net/blog/?p=2665&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=acceso-a-servicios-web-rest-en-android-22</link>
		<comments>http://www.sgoliver.net/blog/?p=2665#comments</comments>
		<pubDate>Sun, 04 Mar 2012 17:32:10 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[base de datos]]></category>
		<category><![CDATA[curso android]]></category>
		<category><![CDATA[Eclipse]]></category>
		<category><![CDATA[externo]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[servicios web]]></category>
		<category><![CDATA[servidor]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2665</guid>
		<description><![CDATA[En el artículo anterior dedicado a los servicios web REST hemos visto cómo crear fácilmente un servicio de este tipo utilizando el framework ASP.NET MVC 3. En esta segunda parte vamos a describir cómo podemos construir una aplicación Android que acceda a este servicio web REST. Y tal como hicimos en el caso de SOAP, vamos [...]]]></description>
			<content:encoded><![CDATA[<p>En el <a title="Acceso a Servicios Web REST en Android (1/2)" href="http://www.sgoliver.net/blog/?p=2610">artículo anterior</a> dedicado a los servicios web REST hemos visto cómo crear fácilmente un servicio de este tipo utilizando el framework ASP.NET MVC 3. En esta segunda parte vamos a describir cómo podemos construir una aplicación Android que acceda a este <strong>servicio web REST</strong>.</p>
<p>Y tal como hicimos en el caso de SOAP, vamos a crear una aplicación de ejemplo que llame a las distintas funciones de nuestro servicio web. En este caso la aplicación se compondrá de 5 botones, uno por cada una de las acciones que hemos implementado en el servicio web (insertar, actualizar, eliminar, recuperar un cliente, y listar todos los clientes).</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/03/captura-inicial-rest.png"><img class="alignnone size-medium wp-image-2671" title="captura-inicial-rest" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/03/captura-inicial-rest-300x228.png" alt="captura-inicial-rest" width="300" height="228" /></a></p>
<p>A diferencia del caso de SOAP, en esta ocasión no vamos a utilizar ninguna librería externa para acceder al servicio web, ya que Android incluye todo lo necesario para realizar la conexión y llamada a los métodos del servicio, y tratamiento de resultados en formato <strong>JSON</strong>.</p>
<p>Como ya hemos comentado, al trabajar con servicios web de tipo REST, las llamadas al servicio no se harán a través de una única URL, sino que se determinará la acción a realizar según la URL accedida y la acción HTTP utilizada para realizar la petición (<span style="font-family: 'courier new', courier;">GET</span>, <span style="font-family: 'courier new', courier;">POST</span>, <span style="font-family: 'courier new', courier;">PUT</span> o <span style="font-family: 'courier new', courier;">DELETE</span>). En los siguientes apartados veremos uno a uno la implementación de estos botones.</p>
<p><strong>Insertar un nuevo cliente</strong></p>
<p>Como ya comentamos en el artículo anterior, la inserción de un nuevo cliente la realizaremos a través de la siguiente URL:</p>
<p><span style="font-family: 'courier new', courier;">http://10.0.2.2:2731/Api/Clientes/Cliente</span></p>
<p>Utilizaremos la acción http <span style="font-family: 'courier new', courier;">POST</span> y tendremos que incluir en la petición un objeto en formato JSON que contenga los datos del nuevo cliente (tan sólo Nombre y Teléfono, ya que el ID se calculará automáticamente). El formato de este objeto de entrada será análogo al siguiente:</p>
<p><span style="font-family: 'courier new', courier;">{Nombre:&#8221;cccc&#8221;, Telefono:12345678}</span></p>
<p>Pues bien, para conseguir esto comenzaremos por crear un nuevo objeto <span style="font-family: 'courier new', courier;">HttpClient</span>, que será el encargado de realizar la comunicación HTTP con el servidor a partir de los datos que nosotros le proporcionemos. Tras esto crearemos la petición <span style="font-family: 'courier new', courier;">POST</span> creando un nuevo objeto <span style="font-family: 'courier new', courier;">HttpPost</span> e indicando la URL de llamada al servicio. Modificaremos mediante <span style="font-family: 'courier new', courier;">setHeader()</span> el atributo http <span style="font-family: 'courier new', courier;">content-type</span> para indicar que el formato de los datos que utilizaremos en la comunicación, que como ya indicamos será JSON (cuyo <em>MIME-Type</em> correspondiente es &#8220;<span style="font-family: 'courier new', courier;">application/json</span>&#8220;).</p>
<pre class="brush: java; title: ; notranslate">
HttpClient httpClient = new DefaultHttpClient();

HttpPost post =
    new HttpPost(&quot;http://10.0.2.2:2731/Api/Clientes/Cliente&quot;);

post.setHeader(&quot;content-type&quot;, &quot;application/json&quot;);
</pre>
<p>El siguiente paso será crear el objeto JSON a incluir con la petición, que deberá contener los datos del nuevo cliente a insertar. Para ello creamos un nuevo objeto <span style="font-family: 'courier new', courier;">JSONObject</span> y le añadimos mediante el método <span style="font-family: 'courier new', courier;">put()</span> los dos atributos necesarios (nombre y teléfono) con sus valores correspondientes, que los obtenemos de los cuadros de texto de la interfaz, llamados <span style="font-family: 'courier new', courier;">txtNombre</span> y <span style="font-family: 'courier new', courier;">txtTelefono</span>.</p>
<p>Por último asociaremos este objeto JSON a nuestra petición HTTP convirtiéndolo primero al tipo <span style="font-family: 'courier new', courier;">StringEntity</span> e incluyéndolo finalmente en la petición mediante el método <span style="font-family: 'courier new', courier;">setEntity()</span>.</p>
<pre class="brush: java; title: ; notranslate">
//Construimos el objeto cliente en formato JSON
JSONObject dato = new JSONObject();

dato.put(&quot;Nombre&quot;, txtNombre.getText().toString());
dato.put(&quot;Telefono&quot;, Integer.parseInt(txtTelefono.getText().toString()));

StringEntity entity = new StringEntity(dato.toString());
post.setEntity(entity);
</pre>
<p>Una vez creada nuestra petición HTTP y asociado el dato de entrada, tan sólo nos queda realizar la llamada al servicio mediante el método <span style="font-family: 'courier new', courier;">execute()</span> del objeto <span style="font-family: 'courier new', courier;">HttpClient</span> y recuperar el resultado mediante <span style="font-family: 'courier new', courier;">getEntity()</span>. Este resultado lo recibimos en forma de objeto HttpEntity, pero lo podemos convertir fácilmente en una cadena de texto mediante el método estático <span style="font-family: 'courier new', courier;">EntityUtils.toString()</span>.</p>
<pre class="brush: java; title: ; notranslate">
HttpResponse resp = httpClient.execute(post);
String respStr = EntityUtils.toString(resp.getEntity());

if(respStr.equals(&quot;true&quot;))
	lblResultado.setText(&quot;Insertado OK.&quot;);
</pre>
<p>En nuestro caso, el método de inserción devuelve únicamente un valor <em>booleano</em> indicando si el registro se ha insertado correctamente en la base de datos, por lo que tan sólo tendremos que verificar el valor de este <em>booleano</em> (&#8220;true&#8221; o &#8220;false&#8221;) para conocer el resultado de la operación, que mostraremos en la interfaz en una etiqueta de texto llamada <span style="font-family: 'courier new', courier;">lblResultado</span>.</p>
<p><strong>Actualizar un cliente existente</strong></p>
<p>La URL utilizada para la actualización de clientes será la misma que la anterior:</p>
<p><span style="font-family: 'courier new', courier;">http://10.0.2.2:2731/Api/Clientes/Cliente</span></p>
<p>Pero en este caso, el objeto JSON a enviar como entrada deberá contener no sólo los nuevos valores de nombre y teléfono sino también el ID del cliente a actualizar, por lo que tendría una estructura análoga a la siguiente:</p>
<p>{Id:123, Nombre:&#8221;cccc&#8221;, Telefono:12345678}</p>
<p>Para actualizar el cliente procederemos de una forma muy similar a la ya comentada para la inserción, con las únicas diferencias de que en este caso la acción HTTP utilizada será <span style="font-family: 'courier new', courier;">PUT</span> (objeto <span style="font-family: 'courier new', courier;">HttpPut</span>) y que el objeto JSON de entrada tendrá el campo ID adicional.</p>
<pre class="brush: java; title: ; notranslate">
HttpClient httpClient = new DefaultHttpClient();

HttpPut put = new HttpPut(&quot;http://10.0.2.2:2731/Api/Clientes/Cliente&quot;);
put.setHeader(&quot;content-type&quot;, &quot;application/json&quot;);

try
{
	//Construimos el objeto cliente en formato JSON
	JSONObject dato = new JSONObject();

	dato.put(&quot;Id&quot;, Integer.parseInt(txtId.getText().toString()));
	dato.put(&quot;Nombre&quot;, txtNombre.getText().toString());
	dato.put(&quot;Telefono&quot;, Integer.parseInt(txtTelefono.getText().toString()));

	StringEntity entity = new StringEntity(dato.toString());
	put.setEntity(entity);

      	HttpResponse resp = httpClient.execute(put);
       	String respStr = EntityUtils.toString(resp.getEntity());

       	if(respStr.equals(&quot;true&quot;))
       		lblResultado.setText(&quot;Actualizado OK.&quot;);
}
catch(Exception ex)
{
       	Log.e(&quot;ServicioRest&quot;,&quot;Error!&quot;, ex);
}
</pre>
<p><strong>Eliminación de un cliente</strong></p>
<p>La eliminación de un cliente la realizaremos a través de la URL siguiente:</p>
<p><span style="font-family: 'courier new', courier;">http://10.0.2.2:2731/Api/Clientes/Cliente/<em>id_cliente</em></span></p>
<p>donde <span style="font-family: 'courier new', courier;">id_cliente</span> será el ID del cliente a eliminar. Además, utilizaremos la acción http <span style="font-family: 'courier new', courier;">DELETE</span> (objeto <span style="font-family: 'courier new', courier;">HttpDelete</span>) para identificar la operación que queremos realizar. En este caso no será necesario pasar ningún objeto de entrada junto con la petición, por lo que el código quedará aún más sencillo que los dos casos anteriores.</p>
<pre class="brush: java; title: ; notranslate">
HttpClient httpClient = new DefaultHttpClient();

String id = txtId.getText().toString();

HttpDelete del =
	new HttpDelete(&quot;http://10.0.2.2:2731/Api/Clientes/Cliente/&quot; + id);

del.setHeader(&quot;content-type&quot;, &quot;application/json&quot;);

try
{
       	HttpResponse resp = httpClient.execute(del);
       	String respStr = EntityUtils.toString(resp.getEntity());

      	if(respStr.equals(&quot;true&quot;))
       		lblResultado.setText(&quot;Eliminado OK.&quot;);
}
catch(Exception ex)
{
      	Log.e(&quot;ServicioRest&quot;,&quot;Error!&quot;, ex);
}
</pre>
<p>Como podéis ver, al principio del método obtenemos el ID del cliente desde la interfaz de la aplicación y lo concatenamos con la URL base para formar la URL completa de llamada al servicio.</p>
<p><strong>Obtener un cliente</strong></p>
<p>Esta operación es un poco distinta a las anteriores, ya que en este caso el resultado devuelto por el servicio será un objeto JSON y no un valor simple como en los casos anteriores. Al igual que en el caso de eliminación de clientes, la URL a utilizar será del tipo:</p>
<p><span style="font-family: 'courier new', courier;">http://10.0.2.2:2731/Api/Clientes/Cliente/<em>id_cliente</em></span></p>
<p>En este caso utilizaremos un tipo de petición http GET (objeto <span style="font-family: 'courier new', courier;">HttpGet</span>) y la forma de realizar la llamada será análoga a las anteriores. Donde aparecerán las diferencias será a la hora de tratar el resultado devuelto por el servicio tras llamar al método <span style="font-family: 'courier new', courier;">getEntity()</span>. Lo que haremos será crear un nuevo objeto <span style="font-family: 'courier new', courier;">JSONObject</span> a partir del resultado textual de <span style="font-family: 'courier new', courier;">getEntity()</span>. Hecho esto, podremos acceder a los atributos del objeto utilizando para ello los métodos <span style="font-family: 'courier new', courier;">get()</span> correspondientes, según el tipo de cada atributo (<span style="font-family: 'courier new', courier;">getInt()</span>, <span style="font-family: 'courier new', courier;">getString()</span>, etc). Tras esto mostraremos los datos del cliente recuperado en la etiqueta de resultados de la interfaz (<span style="font-family: 'courier new', courier;">lblResultados</span>).</p>
<pre class="brush: java; title: ; notranslate">
HttpClient httpClient = new DefaultHttpClient();

String id = txtId.getText().toString();

HttpGet del =
	new HttpGet(&quot;http://10.0.2.2:2731/Api/Clientes/Cliente/&quot; + id);

del.setHeader(&quot;content-type&quot;, &quot;application/json&quot;);

try
{
       	HttpResponse resp = httpClient.execute(del);
       	String respStr = EntityUtils.toString(resp.getEntity());

       	JSONObject respJSON = new JSONObject(respStr);

       	int idCli = respJSON.getInt(&quot;Id&quot;);
       	String nombCli = respJSON.getString(&quot;Nombre&quot;);
       	int telefCli = respJSON.getInt(&quot;Telefono&quot;);

       	lblResultado.setText(&quot;&quot; + idCli + &quot;-&quot; + nombCli + &quot;-&quot; + telefCli);
}
catch(Exception ex)
{
      	Log.e(&quot;ServicioRest&quot;,&quot;Error!&quot;, ex);
}
</pre>
<p>Una vez más como podéis comprobar el código es muy similar al ya visto para el resto de operaciones.</p>
<p><strong>Obtener listado completo de clientes</strong></p>
<p>Por último vamos a ver cómo podemos obtener el listado completo de clientes. El interés de esta operación está en que el resultado recuperado de la llamada al servicio será un array de objetos de tipo cliente, por supuesto en formato JSON. La acción http utilizada será una vez más la acción <span style="font-family: 'courier new', courier;">GET</span>, y la URL para recuperar el listado de clientes será:</p>
<p><span style="font-family: 'courier new', courier;">http://10.0.2.2:2731/Api/Clientes</span></p>
<p>De nuevo, la forma de llamar al servicio será análoga a las anteriores hasta la llamada a <span style="font-family: 'courier new', courier;">getEntity()</span> para recuperar los resultados. En esta ocasión, dado que recibimos un array de elementos, convertiremos este resultado a un objeto <span style="font-family: 'courier new', courier;">JSONArray</span>, y hecho esto podremos acceder a cada uno de los elementos del array mediante una llamada a <span style="font-family: 'courier new', courier;">getJSONObject()</span>, al que iremos pasando el índice de cada elemento. Para saber cuántos elementos contiene el array podremos utilizar el método<span style="font-family: 'courier new', courier;"> length()</span> del objeto <span style="font-family: 'courier new', courier;">JSONArray</span>. Por último, el acceso a los atributos de cada elemento del array lo realizamos exactamente igual como ya lo hicimos en la operación anterior de obtención de cliente por ID.</p>
<pre class="brush: java; title: ; notranslate">
HttpClient httpClient = new DefaultHttpClient();

HttpGet del =
	new HttpGet(&quot;http://10.0.2.2:2731/Api/Clientes&quot;);

del.setHeader(&quot;content-type&quot;, &quot;application/json&quot;);

try
{
       	HttpResponse resp = httpClient.execute(del);
       	String respStr = EntityUtils.toString(resp.getEntity());

       	JSONArray respJSON = new JSONArray(respStr);

       	String[] clientes = new String[respJSON.length()];

       	for(int i=0; i&lt;respJSON.length(); i++)
       	{
       		JSONObject obj = respJSON.getJSONObject(i);

        	int idCli = obj.getInt(&quot;Id&quot;);
        	String nombCli = obj.getString(&quot;Nombre&quot;);
        	int telefCli = obj.getInt(&quot;Telefono&quot;);

       		clientes[i] = &quot;&quot; + idCli + &quot;-&quot; + nombCli + &quot;-&quot; + telefCli;
       	}

       	//Rellenamos la lista con los resultados
       	ArrayAdapter&lt;String&gt; adaptador =
       	       	new ArrayAdapter&lt;String&gt;(ServicioWebRest.this,
		        android.R.layout.simple_list_item_1, clientes);

	lstClientes.setAdapter(adaptador);
}
catch(Exception ex)
{
       	Log.e(&quot;ServicioRest&quot;,&quot;Error!&quot;, ex);
}
</pre>
<p>Tras obtener nuestro array de clientes, para mostrar los resultados hemos añadido a la interfas de nuestra aplicación de ejemplo un control tipo <span style="font-family: 'courier new', courier;">ListView</span> (llamado <span style="font-family: 'courier new', courier;">lstClientes</span>) que hemos rellenado a través de su adaptador con los datos de los clientes recuperados.</p>
<p>A modo de ejemplo, en la siguiente imagen puede verse el resultado de ejecutar la operación de listado completo de clientes:</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/03/captura-final-rest.png"><img class="alignnone size-medium wp-image-2672" title="captura-final-rest" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/03/captura-final-rest-200x300.png" alt="captura-final-rest" width="200" height="300" /></a></p>
<p>Y con esto hemos terminado. Espero haber ilustrado con claridad en los dos últimos artículos la forma de construir servicios web tipo REST mediante ASP.NET y aplicaciones cliente Android capaces de acceder a dichos servicios.</p>
<p>Como siempre, podéis descargar el código fuente completo de este artículo mediante <a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/03/android-ws-rest.zip">este enlace</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2665</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Acceso a Servicios Web REST en Android (1/2)</title>
		<link>http://www.sgoliver.net/blog/?p=2610&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=acceso-a-servicios-web-rest-en-android-12</link>
		<comments>http://www.sgoliver.net/blog/?p=2610#comments</comments>
		<pubDate>Sun, 04 Mar 2012 17:31:13 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[base de datos]]></category>
		<category><![CDATA[curso android]]></category>
		<category><![CDATA[externo]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[servicios web]]></category>
		<category><![CDATA[servidor]]></category>
		<category><![CDATA[sql server]]></category>
		<category><![CDATA[visual studio]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2610</guid>
		<description><![CDATA[En los dos artículos anteriores (éste y éste) del Curso de Programación Android nos hemos ocupado de describir la forma de construir un sistema formado por un servicio web SOAP que accede a una base de datos externa y una aplicación Android que, a través de este servicio, es capaz de manipular dichos datos. En [...]]]></description>
			<content:encoded><![CDATA[<p>En los dos artículos anteriores (<a title="Acceso a Servicios Web SOAP en Android (1/2)" href="http://www.sgoliver.net/blog/?p=2571">éste</a> y <a title="Acceso a Servicios Web SOAP en Android (2/2)" href="http://www.sgoliver.net/blog/?p=2594">éste</a>) del <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313">Curso de Programación Android</a> nos hemos ocupado de describir la forma de construir un sistema formado por un servicio web SOAP que accede a una base de datos externa y una aplicación Android que, a través de este servicio, es capaz de manipular dichos datos.</p>
<p>En este nuevo artículo vamos a crear un sistema similar, pero esta vez haciendo uso de la otra alternativa por excelencia a la hora de crear servicios web, y no es otra de utilizar servicios web tipo <strong>REST</strong>. Las famosas <em>APIs</em> que publican muchos de los sitios web actualmente no son más que servicios web de este tipo, aunque en la mayoría de los casos con medidas de seguridad adicionales tales como autenticación OAuth o similares.</p>
<p>REST también se asienta sobre el protocolo HTTP como mecanismo de transporte entre cliente y servidor, ya veremos después en qué medida. Y en cuanto al formato de los datos transmitidos, a diferencia de SOAP, no se impone ninguno en concreto, aunque lo más habitual actualmente es intercambiar la información en formato XML o JSON. Ya que en el caso de SOAP utilizamos XML, en este nuevo artículo utilizaremos JSON para construir nuestro ejemplo.</p>
<p>También vamos a utilizar un framework distinto para construir el servicio, aunque seguiremos haciéndolo en Visual Studio y en lenguaje C#. En este caso, en vez de utilizar ASP.NET <em>a secas</em>, vamos a utilizar el framework específico <strong>ASP.NET MVC 3</strong>, cuyo sistema de direccionamiento se ajusta mejor a los principios de REST, donde cada <em>recurso</em> [en nuestro caso cada c<em>liente</em>] debería ser accesible mediante su propia URL única. Podéis descargar MVC3 desde su <a title="Descarga ASP.NET MVC 3" href="http://www.asp.net/mvc/mvc3" target="_blank">página oficial</a> de Microsoft.</p>
<p>En este primer artículo sobre servicios REST vamos a describir la construcción del servicio web en sí, y dedicaremos un segundo artículo a explicar cómo podemos acceder a este servicio desde una aplicación Android.</p>
<p>Empezamos. Lo primero que vamos a hacer será crear un nuevo proyecto en Visual Studio utilizando esta vez la plantilla llamada &#8220;<em>ASP.NET MVC 3 Web Application</em>&#8220;, lo llamaremos &#8220;<em>ServicioWebRest</em>&#8220;.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/nuevo-proyecto-rest.png"><img class="alignnone size-medium wp-image-2612" title="nuevo-proyecto-rest" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/nuevo-proyecto-rest-300x149.png" alt="nuevo-proyecto-rest" width="300" height="149" /></a></p>
<p>En la ventana de opciones del proyecto dejaremos todos los datos que aparecen por defecto y seleccionaremos como plantilla &#8220;<em>Empty</em>&#8221; para crear una aplicación vacía.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/opciones-proyecto-rest.png"><img class="alignnone size-medium wp-image-2613" title="opciones-proyecto-rest" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/opciones-proyecto-rest-300x274.png" alt="opciones-proyecto-rest" width="300" height="274" /></a></p>
<p>Esto debería crearnos el nuevo proyecto con la estructura de carpetas necesaria, que como veréis es bastante elaborada.  En nuestro caso vamos a crear el servicio web de forma aislada del resto de la aplicación web, y para ello lo primero que vamos a hacer es añadir una nueva <em>Area</em> al proyecto, a la que llamaremos por ejemplo &#8220;<em>Api</em>&#8220;, lo que nos creará una estructura de carpetas similar a la de la aplicación principal pero dentro de una carpeta independiente. Esto nos permite aislar todo el código y recursos de nuestro servicio web del resto de la aplicación web (que en nuestro caso no existirá porque no es el objetivo de este artículo, pero que podríamos crear sin problemas si lo necesitáramos).</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/menu-nueva-area.png"><img class="alignnone size-medium wp-image-2614" title="menu-nueva-area" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/menu-nueva-area-300x79.png" alt="menu-nueva-area" width="300" height="79" /></a></p>
<p>Con esto, la estructura de nuestro proyecto será la siguiente:</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/estructura-proyecto-mvc3.png"><img class="alignnone size-full wp-image-2615" title="estructura-proyecto-mvc3" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/estructura-proyecto-mvc3.png" alt="estructura-proyecto-mvc3" width="232" height="295" /></a></p>
<p>Una vez que ya tenemos preparada toda la estructura de nuestro proyecto empecemos a añadir los elementos necesarios. Lo primero que vamos a crear será una nueva clase <span style="font-family: 'courier new', courier;">Cliente</span>, igual que hicimos en el ejemplo anterior con SOAP. La colocaremos en la carpeta &#8220;Api/Models&#8221; y el código es el mismo que ya vimos:</p>
<pre class="brush: csharp; title: ; notranslate">
namespace ServicioWebRest.Areas.Api.Models
{
    public class Cliente
    {
        public int Id { get; set; }
        public string Nombre { get; set; }
        public int Telefono { get; set; }
    }
}
</pre>
<p>El siguiente elemento a añadir será una nueva clase que contenga todas las operaciones que queramos realizar sobre nuestra base de datos de clientes. Llamaremos a la clase <span style="font-family: 'courier new', courier;">ClienteManager</span>. En este caso sí vamos a añadir las cuatro operaciones básicas sobre clientes, y una adicional para obtener el listado completo, de forma que más tarde podamos mostrar la implementación en Android de todos los posibles tipos de llamada al servicio. Los métodos que añadiremos serán los siguientes:</p>
<ul>
<li><span style="font-family: 'courier new', courier;">Cliente ObtenerCliente(int id)</span></li>
<li><span style="font-family: 'courier new', courier;">List&lt;Clientes&gt; ObtenerClientes()</span></li>
<li><span style="font-family: 'courier new', courier;">bool InsertarCliente(Cliente c)</span></li>
<li><span style="font-family: 'courier new', courier;">bool ActualizarCliente(Cliente c)</span></li>
<li><span style="font-family: 'courier new', courier;">bool EliminarCliente(int id)</span></li>
</ul>
<p>Los dos primeros métodos nos servirán para recuperar clientes de la base de datos, tanto por su ID para obtener un cliente concreto, como el listado completo que devolverá una lista de clientes. Los otros tres métodos permitirán insertar, actualizar y eliminar clientes a partir de su ID y los datos de entrada (si aplica). El código de todos estos métodos es análogo a los ya implementados en el caso de SOAP, por lo que no nos vamos a parar en volverlos a comentar, tan sólo decir que utilizan la api clásica de ADO.NET para el acceso a SQL Server. En cualquier caso, al final del artículo tenéis como siempre el código fuente completo para poder consultar lo que necesitéis. A modo de ejemplo veamos la implementación de los métodos <span style="font-family: 'courier new', courier;">ObtenerClientes()</span> e <span style="font-family: 'courier new', courier;">InsertarCliente()</span>.</p>
<pre class="brush: csharp; title: ; notranslate">
public bool InsertarCliente(Cliente cli)
{
      SqlConnection con = new SqlConnection(cadenaConexion);

      con.Open();

      string sql = &quot;INSERT INTO Clientes (Nombre, Telefono) VALUES (@nombre, @telefono)&quot;;

      SqlCommand cmd = new SqlCommand(sql,con);

      cmd.Parameters.Add(&quot;@nombre&quot;, System.Data.SqlDbType.NVarChar).Value = cli.Nombre;
      cmd.Parameters.Add(&quot;@telefono&quot;, System.Data.SqlDbType.Int).Value = cli.Telefono;

      int res = cmd.ExecuteNonQuery();

      con.Close();

      return (res == 1);
}

public List&lt;Cliente&gt; ObtenerClientes()
{
      List&lt;Cliente&gt; lista = new List&lt;Cliente&gt;();

      SqlConnection con = new SqlConnection(cadenaConexion);

      con.Open();

      string sql = &quot;SELECT IdCliente, Nombre, Telefono FROM Clientes&quot;;

      SqlCommand cmd = new SqlCommand(sql,con);

      SqlDataReader reader =
            cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);

      while (reader.Read())
      {
          Cliente cli = new Cliente();

          cli = new Cliente();
          cli.Id = reader.GetInt32(0);
          cli.Nombre = reader.GetString(1);
          cli.Telefono = reader.GetInt32(2);

          lista.Add(cli);
      }

      reader.Close();

      return lista;
}
</pre>
<p>Hasta ahora, todo el código que hemos escrito es bastante genérico y nada tiene que ver con que nuestro proyecto sea de tipo MVC. Sin embargo, los dos siguientes elementos sí que están directamente relacionados con el tipo de proyecto que tenemos entre manos.</p>
<p>Lo siguiente que vamos a añadir será un <em>controlador</em> a nuestro servicio web. Este controlador (clase <span style="font-family: 'courier new', courier;">ClientesController</span>) será el encargado de contener las diferentes <em>acciones</em> que se podrán llamar según la URL y datos HTTP que recibamos como petición de entrada al servicio. Para nuestro ejemplo, añadiremos tan sólo dos acciones, una primera dirigida a gestionar todas las peticiones que afecten a un único cliente (insertar, actualizar, eliminar y obtener por ID), y otra que trate la petición del listado completo de clientes. Las llamaremos <span style="font-family: 'courier new', courier;">Clientes()</span> y <span style="font-family: 'courier new', courier;">Cliente()</span> respectivamente. Estas acciones harán uso de una instancia de la clase <span style="font-family: 'courier new', courier;">ClienteManager</span> creada anteriormente para realizar las acciones necesarias contra la base de datos. Cada acción será también responsable de formatear sus resultados al formato de comunicación que hayamos elegido, en nuestro caso JSON.</p>
<p>La acción <span style="font-family: 'courier new', courier;">Clientes</span> es muy sencilla, se limitará a llamar al método <span style="font-family: 'courier new', courier;">ObtenerClientes()</span> y formatear los resultados como JSON. Para hacer esto último basta con crear directamente un objeto <span style="font-family: 'courier new', courier;">JsonResult</span> llamado al método <span style="font-family: 'courier new', courier;">Json()</span> pasándole como parámetro de entrada el objeto a formatear. Todo esto se reduce a una sola linea de código:</p>
<pre class="brush: csharp; title: ; notranslate">
[HttpGet]
public JsonResult Clientes()
{
    return Json(this.clientesManager.ObtenerClientes(),
                JsonRequestBehavior.AllowGet);
}
</pre>
<p>Habréis notado también que hemos precedido el método con el atributo <span style="font-family: 'courier new', courier;">[HttpGet]</span>. Para intentar explicar esto me hace falta seguir hablando de los principios de diseño de REST. Este tipo de servicios utiliza los propios tipos de petición definidos por el protocolo HTTP para diferenciar entre las operaciones a realizar por el servicio web. Así, el propio tipo de petición HTTP realizada (<span style="font-family: 'courier new', courier;">GET</span>, <span style="font-family: 'courier new', courier;">POST</span>, <span style="font-family: 'courier new', courier;">PUT</span> o <span style="font-family: 'courier new', courier;">DELETE</span>), junto con la dirección URL especificada en la llamada, nos determinará la operación a ejecutar por el servicio web. En el caso ya visto, el atributo <span style="font-family: 'courier new', courier;">[HttpGet]</span> nos indica que dicho método se podrá ejecutar al recibirse una petición de tipo <span style="font-family: 'courier new', courier;">GET</span>.</p>
<p>Entenderemos todo esto mejor ahora cuando veamos el código de la acción <span style="font-family: 'courier new', courier;">Cliente()</span>. En esta acción, dependiente del tipo de petición HTTP recibida, tendremos que llamar a un método u otro del servicio web. Así, usaremos <span style="font-family: 'courier new', courier;">POST</span> para las inserciones de clientes, <span style="font-family: 'courier new', courier;">PUT</span> para las actualizaciones, <span style="font-family: 'courier new', courier;">GET</span> para la consulta por ID y <span style="font-family: 'courier new', courier;">DELETE</span> para las eliminaciones. En este caso no precedemos el método por ningún atributo, ya que la misma acción se encargará de tratar diferentes tipos de petición.</p>
<pre class="brush: csharp; title: ; notranslate">
public JsonResult Cliente(int? id, Cliente item)
{
    switch (Request.HttpMethod)
    {
        case &quot;POST&quot;:
            return Json(clientesManager.InsertarCliente(item));
        case &quot;PUT&quot;:
            return Json(clientesManager.ActualizarCliente(item));
        case &quot;GET&quot;:
            return Json(clientesManager.ObtenerCliente(id.GetValueOrDefault()),
                        JsonRequestBehavior.AllowGet);
        case &quot;DELETE&quot;:
            return Json(clientesManager.EliminarCliente(id.GetValueOrDefault()));
    }

    return Json(new { Error = true, Message = &quot;Operación HTTP desconocida&quot; });
}
</pre>
<p>Algunos de vosotros seguro que os estáis preguntando cómo distinguirá el servicio cuándo llamar a la acción <span style="font-family: 'courier new', courier;">Clientes()</span> para obtener el listado completo, o a la acción <span style="font-family: 'courier new', courier;">Cliente()</span> para obtener un único cliente por su ID, ya que para ambas operaciones hemos indicado que se recibirá el tipo de petición http <span style="font-family: 'courier new', courier;">GET</span>.</p>
<p>Pues bien, aquí es donde nos va a ayudar el último elemento a añadir al servicio web. Realmente no lo añadiremos, sino que lo modificaremos, ya que es un fichero que ya ha creado Visual Studio por nosotros. Se trata de la clase <span style="font-family: 'courier new', courier;">ApiAreaRegistration</span>. La función de esta clase será la de dirigir las peticiones recibidas hacia una acción u otra del controlador según la URL utilizada al realizarse la llamada al servicio web.</p>
<p>En nuestro caso de ejemplo, vamos a reconocer dos tipos de URL. Una de ellas para acceder a la lista completa de cliente, y otra para realizar cualquier acción sobre un cliente en concreto:</p>
<ul>
<li>Lista de clientes: <em>http://servidor/Api/Clientes</em></li>
<li>Operación sobre cliente: <em>http://servidor/Api/Clientes/Cliente/id_del_cliente</em></li>
</ul>
<p>Cada uno de estos patrones tendremos que registrarlos mediante el método <span style="font-family: 'courier new', courier;">MapRoute()</span> dentro del método <span style="font-family: 'courier new', courier;">RegisterArea()</span> que ya tendremos creado dentro de la clase <span style="font-family: 'courier new', courier;">ApiAreaRegistration</span>. Así, para registrar el primer tipo de URL haremos lo siguiente:</p>
<pre class="brush: csharp; title: ; notranslate">
context.MapRoute(
    &quot;AccesoClientes&quot;,
    &quot;Api/Clientes&quot;,
    new
    {
        controller = &quot;Clientes&quot;,
        action = &quot;Clientes&quot;
    }
);
</pre>
<p>Como primer parámetro de MapRoute() indicamos un nombre descriptivo para el patrón de URL. El segundo parámetro es el patrón en sí, que en este caso no tiene partes variables. Por último indicamos el controlador al que se dirigirán las peticiones que sigan este patrón eliminando el sufijo &#8220;<em>Controller</em>&#8221; (en nuestro caso será el controlador <span style="font-family: 'courier new', courier;">ClientesController</span>) y la acción concreta a ejecutar dentro de dicho controlador (en nuestro caso la acción <span style="font-family: 'courier new', courier;">Clientes()</span>).</p>
<p>Para el segundo tipo de URL será muy similar, con la única diferencia de que ahora habrá una parte final variable que se corresponderá con el ID del cliente y que asignaremos al parámetro &#8220;id&#8221; de la acción. En este caso además, dirigiremos la petición hacia la acción <span style="font-family: 'courier new', courier;">Cliente()</span>, en vez de <span style="font-family: 'courier new', courier;">Clientes()</span>.</p>
<pre class="brush: csharp; title: ; notranslate">
context.MapRoute(
    &quot;AccesoCliente&quot;,
    &quot;Api/Clientes/Cliente/{id}&quot;,
    new
    {
        controller = &quot;Clientes&quot;,
        action = &quot;Cliente&quot;,
        id = UrlParameter.Optional
    }
);
</pre>
<p>Como todo esto en cuenta, y por recapitular un poco, las posibles llamadas a nuestro servicio serán las siguientes:</p>
<p style="padding-left: 30px;"><span style="font-family: 'courier new', courier;">GET  /Api/Clientes</span></p>
<p style="padding-left: 30px;">Recuperará el listado completo de clientes y lo devolverá en formato JSON.</p>
<p style="padding-left: 30px;"><span style="font-family: 'courier new', courier;">GET  /Api/Clientes/Cliente/3</span></p>
<p style="padding-left: 30px;">Recuperará el cliente con el ID indicado en la URL y lo devolverá en formato JSON.</p>
<p style="padding-left: 30px;"><span style="font-family: 'courier new', courier;">POST  /Api/Clientes/Cliente  { Nombre:&#8221;nombre&#8221;, Telefono:1234 }</span></p>
<p style="padding-left: 30px;">Insertará un nuevo cliente con los datos aportados en la petición en formato JSON.</p>
<p style="padding-left: 30px;"><span style="font-family: 'courier new', courier;">PUT  /Api/Clientes/Cliente/3  { Id:3, Nombre:&#8221;nombre&#8221;, Telefono:1234 }</span></p>
<p style="padding-left: 30px;">Actualizará el cliente con el ID indicado en la URL con los datos aportados en la petición en formato JSON.</p>
<p style="padding-left: 30px;"><span style="font-family: 'courier new', courier;">DELETE  /Api/Clientes/Cliente/3</span></p>
<p style="padding-left: 30px;">Eliminará el cliente con el ID indicado en la URL.</p>
<p>Llegados aquí, tan sólo tenemos que ejecutar nuestro proyecto y esperar a que se abra el navegador web. En principio no se mostrará un error por no encontrar la página principal de la aplicación, ya que no hemos creado ninguna, pero nos asegurará que el servidor de prueba está funcionando, por lo que nuestro servicio debería responder a peticiones.</p>
<p>Así, si escribimos en la barra de direcciones por ejemplo la siguiente dirección (el puerto puede variar):</p>
<p><em>http://localhost:1234/Api/Clientes/Cliente/4</em></p>
<p>deberíamos recibir un fichero en formato JSON que contuviera los datos del cliente con ID = 4 de nuestra base de datos. Sería un fichero con contenido similar al siguiente:</p>
<p>{&quot;Id&quot;:4,&quot;Nombre&quot;:&quot;cliente4&quot;,&quot;Telefono&quot;:4444}</p>
<p>En el siguiente artículo veremos cómo construir una aplicación Android capaz de acceder a este servicio y procesar los resultados recibidos. Podéis descargar el código fuente completo de este artículo desde <a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/03/ServicioWebRest.zip">este enlace</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2610</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Acceso a Servicios Web SOAP en Android (2/2)</title>
		<link>http://www.sgoliver.net/blog/?p=2594&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=acceso-a-servicios-web-soap-en-android-22</link>
		<comments>http://www.sgoliver.net/blog/?p=2594#comments</comments>
		<pubDate>Mon, 27 Feb 2012 18:05:52 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[base de datos]]></category>
		<category><![CDATA[curso android]]></category>
		<category><![CDATA[externo]]></category>
		<category><![CDATA[ksoap]]></category>
		<category><![CDATA[ksoap2]]></category>
		<category><![CDATA[servicios web]]></category>
		<category><![CDATA[servidor]]></category>
		<category><![CDATA[soap]]></category>
		<category><![CDATA[sql server]]></category>
		<category><![CDATA[visual studio]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2594</guid>
		<description><![CDATA[En el artículo anterior del curso vimos cómo construir un servicio web SOAP haciendo uso de ASP.NET y una base de datos externa SQL Server. En este segundo artículo veremos cómo podemos acceder a este servicio web desde una aplicación Android y probaremos todo el sistema en local para verificar su correcto funcionamiento. En primer [...]]]></description>
			<content:encoded><![CDATA[<p>En el <a title="Acceso a Servicios Web SOAP en Android (1/2)" href="http://www.sgoliver.net/blog/?p=2571">artículo anterior</a> del <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313">curso</a> vimos cómo construir un servicio web SOAP haciendo uso de ASP.NET y una base de datos externa SQL Server. En este segundo artículo veremos cómo podemos acceder a este servicio web desde una aplicación Android y probaremos todo el sistema en local para verificar su correcto funcionamiento.</p>
<p>En primer lugar hay que empezar diciendo que Android no incluye &#8220;de serie&#8221; ningún tipo de soporte para el acceso a servicios web de tipo SOAP. Es por esto por lo que vamos a utilizar una librería externa para hacernos más fácil esta tarea. Entre la oferta actual, la opción más popular y más utilizada es la librería <a title="Librería ksoap2-android" href="http://code.google.com/p/ksoap2-android/" target="_blank">ksoap2-android</a>. Esta librería es un <em>fork</em>, especialmente adaptado para Android, de la antigua librería <a title="Librería ksoap2" href="http://ksoap2.sourceforge.net/" target="_blank">kSOAP2</a>. Este framework nos permitirá de forma relativamente fácil y cómoda utilizar servicios web que utilicen el estándar SOAP. La última versión de esta librería en el momento de escribir este artículo es la 2.6.0, que puede descargarse desde <a title="Descargar ksoap2-android 2.6.0" href="http://ksoap2-android.googlecode.com/svn/m2-repo/com/google/code/ksoap2-android/ksoap2-android-assembly/2.6.0/ksoap2-android-assembly-2.6.0-jar-with-dependencies.jar" target="_blank">este enlace</a>.</p>
<p>Agregar esta librería a nuestro proyecto Android es muy sencillo. Una vez tenemos creado el proyecto en Android, accederemos al menú &#8220;<em>Project / Properties</em>&#8221; y en la ventana de propiedades accederemos a la sección &#8220;<em>Java Build Path</em>&#8220;. En esta sección accederemos a la solapa &#8220;<em>Libraries</em>&#8221; y pulsaremos el botón &#8220;<em>Add External JARs&#8230;</em>&#8220;. Aquí seleccionamos el fichero jar de la librería ksoap2-android (en este caso &#8220;<em>ksoap2-android-assembly-2.6.0-jar-with-dependencies.jar&#8221;</em>) y listo, ya tenemos nuestro proyecto preparado para hacer uso de la funcionalidad aportada por la librería.</p>
<p>Como aplicación de ejemplo, vamos a crear una aplicación sencilla que permita añadir un nuevo usuario a la base de datos. Para ello añadiremos a la vista principal dos cuadros de texto para introducir el nombre y teléfono del nuevo cliente (en mi caso se llamarán <span style="font-family: 'courier new', courier;">txtNombre</span> y <span style="font-family: 'courier new', courier;">txtTelefono</span> respectivamente) y un botón (en mi caso <span style="font-family: 'courier new', courier;">btnEnviar</span>) que realice la llamada al método <span style="font-family: 'courier new', courier;">NuevoCliente</span> del servicio web pasándole como parámetros los datos introducidos en los cuadros de texto anteriores.</p>
<p>No voy a mostrar todo el código necesario para crear esta vista y obtener las referencias a cada control porque no tiene ninguna particularidad sobre lo ya visto en multitud de ocasiones en artículos anteriores del curso (en cualquier caso al final del artículo podéis descargar todo el código fuente para su consulta). Lo que nos interesa en este caso es la implementación del evento <span style="font-family: 'courier new', courier;">onClick</span> del botón <span style="font-family: 'courier new', courier;">btnEnviar</span>, que será el encargado de comunicarse con el servicio web y procesar el resultado.</p>
<p>Lo primero que vamos a hacer en este evento es definir, por comodidad, cuatro constantes que nos servirán en varias ocasiones durante el código:</p>
<ul>
<li><span style="font-family: 'courier new', courier;">NAMESPACE</span>. Espacio de nombres utilizado en nuestro servicio web.</li>
<li><span style="font-family: 'courier new', courier;">URL</span>. Dirección URL para realizar la conexión con el servicio web.</li>
<li><span style="font-family: 'courier new', courier;">METHOD_NAME</span>. Nombre del método web concreto que vamos a ejecutar.</li>
<li><span style="font-family: 'courier new', courier;">SOAP_ACTION</span>. Equivalente al anterior, pero en la notación definida por SOAP.</li>
</ul>
<p>Aunque los valores se podrían más o menos intuir, para conocer exactamente los valores que debemos asignar a estas constantes vamos a ejecutar una vez más el proyecto de Visual Studio que construimos en el artículo anterior y vamos a acceder a la página de prueba del método <span style="font-family: 'courier new', courier;">NuevoCliente</span>. Veremos algo parecido a lo siguiente:</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/constantes-soap.png"><img class="alignnone size-medium wp-image-2600" title="constantes-soap" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/constantes-soap-223x300.png" alt="constantes-soap" width="223" height="300" /></a></p>
<p>En la imagen anterior se muestran resaltados en rojo los valores de las cuatro constantes a definir, que en nuestro caso concreto quedarían de la siguiente forma:</p>
<pre class="brush: java; title: ; notranslate">
String NAMESPACE = &quot;http://sgoliver.net/&quot;;
String URL=&quot;http://10.0.2.2:1473/ServicioClientes.asmx&quot;;
String METHOD_NAME = &quot;NuevoClienteSimple&quot;;
String SOAP_ACTION = &quot;http://sgoliver.net/NuevoClienteSimple&quot;;
</pre>
<p>Como podéis comprobar, y esto es algo importante, en la URL he sustituido el nombre de máquina <em>localhost</em> por su dirección IP equivalente, que en el caso de aplicaciones Android ejecutadas en el emulador se corresponde con la dirección <strong>10.0.2.2</strong>, en vez de la clásica <strong>127.0.0.1</strong>.</p>
<p>Los siguientes pasos del proceso serán crear la petición SOAP al servicio web, enviarla al servidor y recibir la respuesta. Aunque ya dijimos que todo este proceso sería casi transparente para el programador, por ser ésta la primera vez que hablamos del tema me voy a detener un poco más para intentar que entendamos lo que estamos haciendo y no solo nos limitemos a copiar/pegar trozos de código que no sabemos lo que hacen.</p>
<p>Volvamos a la página de prueba del método web <span style="font-family: 'courier new', courier;">NuevoCliente</span>. Justo debajo de la sección donde se solicitan los parámetros a pasar al método se incluye también un XML de muestra de cómo tendría que ser nuestra petición al servidor si tuviéramos que construirla a mano. Echémosle un vistazo:</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/estructura-peticion-soap.png"><img class="alignnone size-medium wp-image-2604" title="estructura-peticion-soap" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/estructura-peticion-soap-300x124.png" alt="estructura-peticion-soap" width="300" height="124" /></a></p>
<p>Una vez más he marcado varias zonas sobre la imagen, correspondientes a las tres partes principales de una petición de tipo SOAP. Empezando por la &#8220;parte interna&#8221; del XML, en primer lugar encontramos los datos de la petición en sí (<em>Request</em>) que contiene el nombre del método al que queremos llamar, y los nombres y valores de los parámetros en entrada. Rodeando a esta información se añaden otra serie de etiquetas y datos a modo de <em>contenedor</em> estándar que suele recibir el nombre de <em>Envelope</em>. La información indicada en este contenedor no es específica de nuestra llamada al servicio, pero sí contiene información sobre formatos y esquemas de validación del estándar SOAP. Por último, durante el envío de esta petición SOAP al servidor mediante el protocolo HTTP se añaden determinados encabezados como los que veis en la imagen. Todo esto junto hará que el servidor sea capaz de interpretar correctamente nuestra petición SOAP, se llame al método web correcto, y se devuelva el resultado en un formato similar al anterior que ya veremos más adelante. Aclarada un poco la estructura y funcionamiento general de una petición SOAP veamos lo sencillo que resulta realizarla desde nuestra aplicación Android.</p>
<p>En primer lugar crearemos la petición (<em>request</em>) a nuestro método <span style="font-family: 'courier new', courier;">NuevoCliente</span>. Para ello crearemos un nuevo objeto <span style="font-family: 'courier new', courier;">SoapObject</span> pasándole el namespace y el nombre del método web. A esta petición tendremos que asociar los parámetros de entrada mediante el método <span style="font-family: 'courier new', courier;">addProperty()</span> al que pasaremos los nombres y valores de los parámetros (que en nuestro caso se obtendrán de los cuadros de texto de la vista principal).</p>
<pre class="brush: java; title: ; notranslate">
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

request.addProperty(&quot;nombre&quot;, txtNombre.getText().toString());
request.addProperty(&quot;telefono&quot;, txtTelefono.getText().toString());
</pre>
<p>El segundo paso será crear el contenedor SOAP (envelope) y asociarle nuestra petición. Para ello crearemos un nuevo objeto <span style="font-family: 'courier new', courier;">SoapSerializationEnvelope</span> indicando la versión de SOAP que vamos a usar (versión 1.1 en nuestro caso, como puede verse en la imagen anterior). Indicaremos además que se trata de un servicio web .NET activando su propiedad <span style="font-family: 'courier new', courier;">dotNet</span>. Por último, asociaremos la petición antes creada a nuestro contenedor llamando al método <span style="font-family: 'courier new', courier;">setOutputSoapObject()</span>.</p>
<pre class="brush: java; title: ; notranslate">
SoapSerializationEnvelope envelope =
	new SoapSerializationEnvelope(SoapEnvelope.VER11);

envelope.dotNet = true;

envelope.setOutputSoapObject(request);
</pre>
<p>Como tercer paso crearemos el objeto que se encargará de realizar la comunicación HTTP con el servidor, de tipo <span style="font-family: 'courier new', courier;">HttpTransportSE</span>, al que pasaremos la URL de conexión a nuestro servicio web. Por último, completaremos el proceso realizando la llamada al servicio web mediante el método <span style="font-family: 'courier new', courier;">call()</span>.</p>
<pre class="brush: java; title: ; notranslate">
HttpTransportSE transporte = new HttpTransportSE(URL);

try
{
	transporte.call(SOAP_ACTION, envelope);

	//Se procesa el resultado devuelto
	//...
}
catch (Exception e)
{
	txtResultado.setText(&quot;Error!&quot;);
}
</pre>
<p>Tras la llamada al servicio ya estamos en disposición de obtener el resultado devuelto por el método web llamado. Esto lo conseguimos mediante el método <span style="font-family: 'courier new', courier;">getResponse()</span>. Dependiendo del tipo de resultado que esperemos recibir deberemos convertir esta respuesta a un tipo u otro. En este caso, como el resultado que esperamos es un valor simple (un número entero) convertiremos la respuesta a un objeto <span style="font-family: 'courier new', courier;">SoapPrimitive</span>, que directamente podremos convertir a una cadena de caracteres llamado a <span style="font-family: 'courier new', courier;">toString()</span>. Más adelante veremos cómo tratar valores de retorno más complejos.</p>
<pre class="brush: java; title: ; notranslate">
SoapPrimitive resultado_xml =(SoapPrimitive)envelope.getResponse();
String res = resultado_xml.toString();

if(res.equals(&quot;1&quot;))
	txtResultado.setText(&quot;Insertado OK&quot;);
</pre>
<p>Y listo, con esto ya tenemos preparada la llamada a nuestro servicio web y el tratamiento de la respuesta recibida.</p>
<p>Un detalle más antes de poder probar todo el sistema. Debemos acordarnos de conceder permiso de acceso a internet a nuestra aplicación, añadiendo la linea correspondiente al <em>Android Manifest</em>:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
</pre>
<p>Pues bien, para probar lo que llevamos hasta ahora podemos ejecutar ambos proyectos simultáneamente, en primer lugar el de Visual Studio para iniciar la ejecución del servidor local que alberga nuestro servicio web (hay que dejar abierto el explorador una vez que se abra), y posteriormente el de Eclipse para iniciar nuestra aplicación Android en el Emulador. Una vez están los dos proyectos en ejecución, podemos rellenar los datos de nuestro cliente en la aplicación Android y pulsar el botón &#8220;<em>Enviar</em>&#8221; para realizar la llamada al servicio web e insertar el cliente en la base de datos (que por supuesto también deberá estar iniciada). Si todo va bien y no se produce ningún error, deberíamos poder consultar la tabla de <span style="font-family: 'courier new', courier;">Clientes</span> a través del <em>SQL Server Management Studio</em> para verificar que el cliente se ha insertado correctamente.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/captura-insert.png"><img class="alignnone size-medium wp-image-2638" title="captura-insert" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/captura-insert-300x183.png" alt="captura-insert" width="300" height="183" /></a></p>
<p>En la imagen vemos cómo hemos insertado un nuevo cliente llamada &#8216;cliente7&#8242; con número de teléfono &#8217;7777&#8242;. Si consultamos ahora nuestra base de datos Sql Server podremos comprobar si el registro efectivamente se ha insertado correctamente.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-datos.png"><img class="alignnone size-full wp-image-2639" title="sqlserver-datos" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-datos.png" alt="sqlserver-datos" width="208" height="178" /></a></p>
<p>Con esto, ya sabemos realizar una llamada a un servicio web SOAP que devuelve un valor de retorno sencillo, en este caso un simple número entero. Lo siguiente que vamos a ver será como implementar la llamada a un método del servicio web que nos devuelva un valor algo más complejo. Y esto lo vamos a ver con la llamada al método web <span style="font-family: 'courier new', courier;">ListadoClientes()</span> que recordemos devolvía un array de objetos de tipo <span style="font-family: 'courier new', courier;">Cliente</span>.</p>
<p>En este caso, la llamada al método web se realizará de forma totalmente análoga a la ya comentada. Donde llegarán las diferencias será a la hora de tratar el resultado devuelto por el servicio, comenzando por el resultado del método <span style="font-family: 'courier new', courier;">getResponse()</span> de ksoap. En esta ocasión, dado que el resultado esperado no es ya un valor simple sino un objeto más complejo, convertiremos el resultado de <span style="font-family: 'courier new', courier;">getResponse()</span> al tipo <span style="font-family: 'courier new', courier;">SoapObject</span>, en vez de <span style="font-family: 'courier new', courier;">SoapPrimitive</span> como hicimos anteriormente. Nuestro objetivo será generar un array de objetos <span style="font-family: 'courier new', courier;">Cliente</span> (lo llamaremos <span style="font-family: 'courier new', courier;">listaClientes</span>) a partir del resultado devuelto por la llamada al servicio.</p>
<p>Como sabemos que el resultado devuelto por el servicio es también un array, lo primero que haremos será crear un array local con la misma longitud que el devuelto, lo que conseguiremos mediante el método <span style="font-family: 'courier new', courier;">getPropertyCount()</span>. Tras esto, iteraremos por los distintos elementos del array devuelto mediante el método <span style="font-family: 'courier new', courier;">getProperty(ind)</span>, donde <span style="font-family: 'courier new', courier;">ind</span> será el índice de cada ocurrencia. Cada uno de estos elementos será a su vez otro objeto de tipo <span style="font-family: 'courier new', courier;">SoapObject</span>, que representará a un <span style="font-family: 'courier new', courier;">Cliente</span>. Adicionalmente, para cada elemento accederemos a sus propiedades (Id, Nombre, y Telefono) una vez más mediante llamadas a <span style="font-family: 'courier new', courier;">getProperty()</span>, con el índice de cada atributo, que seguirá el mismo orden en que se definieron. Así, <span style="font-family: 'courier new', courier;">getProperty(0)</span> recuperará el Id del cliente, <span style="font-family: 'courier new', courier;">getProperty(1)</span> el nombre, y <span style="font-family: 'courier new', courier;">getProperty(2)</span> el teléfono. De esta forma podremos crear nuestros objetos <span style="font-family: 'courier new', courier;">Cliente</span> locales a partir de estos datos. Al final de cada iteración añadimos el nuevo cliente recuperado a nuestro array. Veamos como quedaría todo esto en el código, donde seguro que se entiende mejor:</p>
<pre class="brush: java; title: ; notranslate">
SoapObject resSoap =(SoapObject)envelope.getResponse();

Cliente[] listaClientes = new Cliente[resSoap.getPropertyCount()];

for (int i = 0; i &lt; listaClientes.length; i++)
{
       SoapObject ic = (SoapObject)resSoap.getProperty(i);

       Cliente cli = new Cliente();
       cli.id = Integer.parseInt(ic.getProperty(0).toString());
       cli.nombre = ic.getProperty(1).toString();
       cli.telefono = Integer.parseInt(ic.getProperty(2).toString());

       listaClientes[i] = cli;
}
</pre>
<p>En nuestra aplicación de ejemplo añadimos un nuevo botón y un control tipo lista (lo llamo <span style="font-family: 'courier new', courier;">lstClientes</span>), de forma que al pulsar dicho botón rellenemos la lista con los nombres de todos los clientes recuperados. La forma de rellenar una lista con un array de elementos ya la vimos en los artículos dedicados a los controles de selección, por lo que no nos pararemos a comentarlo. El código sería el siguiente (Nota: sé que todo esto se podría realizar de forma más eficiente sin necesidad de crear distintos arrays para los clientes y para el adaptador de la lista, pero lo dejo así para no complicar el tutorial con temas ya discutidos en otros artículos):</p>
<pre class="brush: java; title: ; notranslate">
//Rellenamos la lista con los nombres de los clientes
final String[] datos = new String[listaClientes.length];

for(int i=0; i&lt;listaClientes.length; i++)
    datos[i] = listaClientes[i].nombre;

ArrayAdapter&lt;String&gt; adaptador =
    new ArrayAdapter&lt;String&gt;(ServicioWebSoap.this,
        android.R.layout.simple_list_item_1, datos);

lstClientes.setAdapter(adaptador);
</pre>
<p>Por último, vamos a ver cómo llamar a un método web que recibe como parámetro algún objeto complejo. Para ilustrarlo haremos una llamada al segundo método de inserción de clientes que implementamos en el servicio, <span style="font-family: 'courier new', courier;">NuevoClienteObjeto()</span>. Recordemos que este método recibía como parámetro de entrada un objeto de tipo <span style="font-family: 'courier new', courier;">Cliente</span>.</p>
<p>Para poder hacer esto, lo primero que tendremos que hacer será modificar un poco nuestra clase <span style="font-family: 'courier new', courier;">Cliente</span>, de forma que ksoap sepa cómo serializar nuestros objetos <span style="font-family: 'courier new', courier;">Cliente</span> a la hora de generar las peticiones SOAP correspondientes. Y para esto, lo que haremos será implementar la interfaz <span style="font-family: 'courier new', courier;">KvmSerializable</span> en nuestra clase <span style="font-family: 'courier new', courier;">Cliente</span>. Para ello, además de añadir la cláusula <span style="font-family: 'courier new', courier;">implements</span> correspondiente tendremos que implementar los siguientes métodos:</p>
<ul>
<li><span style="font-family: 'courier new', courier;">getProperty(int indice)</span></li>
<li><span style="font-family: 'courier new', courier;">getPropertyCount()</span></li>
<li><span style="font-family: 'courier new', courier;">getPropertyInfo(int indice, HashTable ht, PropertyInfo info)</span></li>
<li><span style="font-family: 'courier new', courier;">setProperty(int indice, Object valor)</span></li>
</ul>
<p>El primero de ellos deberá devolver el valor de cada atributo de la clase a partir de su índice de orden. Así, para el índice 0 se devolverá el valor del atributo Id, para el índice 1 el del atributo Nombre, y para el 2 el atributo Teléfono.</p>
<pre class="brush: java; title: ; notranslate">
@Override
public Object getProperty(int arg0) {

	switch(arg0)
        {
        case 0:
            return id;
        case 1:
            return nombre;
        case 2:
            return telefono;
        }

	return null;
}
</pre>
<p>El segundo de los métodos, deberá devolver simplemente el número de atributos de nuestra clase, que en nuestro caso será 3 (Id, Nombre y Telefono):</p>
<pre class="brush: java; title: ; notranslate">
@Override
public int getPropertyCount() {
	return 3;
}
</pre>
<p>El objetivo del tercero será informar, según el índice recibido como parámetro, el tipo y nombre del atributo correspondiente. El tipo de cada atributo se devolverá como un valor de la clase <span style="font-family: 'courier new', courier;">PropertyInfo</span>.</p>
<pre class="brush: java; title: ; notranslate">
@Override
public void getPropertyInfo(int ind, Hashtable ht, PropertyInfo info) {
	switch(ind)
        {
        case 0:
            info.type = PropertyInfo.INTEGER_CLASS;
            info.name = &quot;Id&quot;;
            break;
        case 1:
            info.type = PropertyInfo.STRING_CLASS;
            info.name = &quot;Nombre&quot;;
            break;
        case 2:
            info.type = PropertyInfo.INTEGER_CLASS;
            info.name = &quot;Telefono&quot;;
            break;
        default:break;
        }
}
</pre>
<p>Por último, el método <span style="font-family: 'courier new', courier;">setProperty()</span> será el encargado de asignar el valor de cada atributo según su índice y el valor recibido como parámetro.</p>
<pre class="brush: java; title: ; notranslate">
@Override
public void setProperty(int ind, Object val) {
	switch(ind)
        {
        case 0:
            id = Integer.parseInt(val.toString());
            break;
        case 1:
            nombre = val.toString();
            break;
        case 2:
            telefono = Integer.parseInt(val.toString());
            break;
        default:
            break;
        }
}
</pre>
<p>Mediante estos métodos, aunque de forma transparente para el programados, ksoap será capaz de transformar nuestros objetos Cliente al formato XML correcto de forma que pueda pasarlos como parámetro en las peticiones SOAP a nuestro servicio.</p>
<p>Por su parte, la llamada al servicio también difiere un poco de lo ya comentado a la hora de asociar los parámetros de entrada del método web. En este caso, construiremos en primer lugar el objeto <span style="font-family: 'courier new', courier;">Cliente</span> que queremos insertar en la base de datos a partir de los datos introducidos en la pantalla de nuestra aplicación de ejemplo. Tras esto crearemos un nuevo objeto <span style="font-family: 'courier new', courier;">PropertyInfo</span>, al que asociaremos el nombre, valor y tipo de nuestro cliente mediante sus métodos <span style="font-family: 'courier new', courier;">setName()</span>, <span style="font-family: 'courier new', courier;">setValue()</span> y <span style="font-family: 'courier new', courier;">setClass()</span> respectivamente. Por último, asociaremos este cliente como parámetro de entrada al servicio llamando al metodo <span style="font-family: 'courier new', courier;">addProperty()</span> igual que hemos hecho en las anteriores ocasiones, con la diferencia de que esta vez lo llamaremos pasándole el objeto <span style="font-family: 'courier new', courier;">PropertyInfo</span> que acabamos de crear. Además de esto, tendremos también que llamar finalmente al método <span style="font-family: 'courier new', courier;">addMapping()</span> para asociar de alguna forma nuestro espacio de nombres y nombre de clase &#8220;<em>Cliente</em>&#8221; con la clase real java. Veamos el código para entenderlo mejor:</p>
<pre class="brush: java; title: ; notranslate">
Cliente cli = new Cliente();
cli.nombre = txtNombre.getText().toString();
cli.telefono = Integer.parseInt(txtTelefono.getText().toString());

PropertyInfo pi = new PropertyInfo();
pi.setName(&quot;cliente&quot;);
pi.setValue(cli);
pi.setType(cli.getClass());

request.addProperty(pi);

SoapSerializationEnvelope envelope =
    new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;

envelope.setOutputSoapObject(request);

envelope.addMapping(NAMESPACE, &quot;Cliente&quot;, cli.getClass());
</pre>
<p>Todo esto lo haremos en un nuevo botón añadido a la aplicación de ejemplo (<em>Enviar2</em>), cuyo efecto tendrá que ser idéntico al que ya creamos para la llamada al método web <span style="font-family: 'courier new', courier;">NuevoClienteSimple()</span>, aunque como acabamos de ver su implementación es algo diferente debido a los distintos parámetros de entrada utilizados.</p>
<p>Como imagen final veamos una captura de la pantalla final de nuestra aplicación de ejemplo, donde vemos los tres botones implementados, junto al resultado de la ejecución de cada uno, el mensaje &#8220;Insertado OK&#8221; de los métodos de inserción, y la lista de clientes recuperada por el método de consulta.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/captura-final1.png"><img class="alignnone size-medium wp-image-2644" title="captura-final" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/captura-final1-200x300.png" alt="captura-final" width="200" height="300" /></a></p>
<p>Espero que estos dos últimos artículos sobre servicios web SOAP y Android os sirvan para tener un ejemplo completo, tanto de la parte servidor como de la parte cliente, que os sirva de base para crear nuevos sistemas adaptados a vuestras necesidades. Como siempre, podéis descargar el código fuente completo de este artículo a través de <a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/android-ws-soap.zip">este enlace</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2594</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
		<item>
		<title>Acceso a Servicios Web SOAP en Android (1/2)</title>
		<link>http://www.sgoliver.net/blog/?p=2571&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=acceso-a-servicios-web-soap-en-android-12</link>
		<comments>http://www.sgoliver.net/blog/?p=2571#comments</comments>
		<pubDate>Mon, 27 Feb 2012 18:04:53 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[base de datos]]></category>
		<category><![CDATA[curso android]]></category>
		<category><![CDATA[externo]]></category>
		<category><![CDATA[servicios web]]></category>
		<category><![CDATA[servidor]]></category>
		<category><![CDATA[soap]]></category>
		<category><![CDATA[sql server]]></category>
		<category><![CDATA[visual studio]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2571</guid>
		<description><![CDATA[En este primer artículo que vamos a dedicar a los servicios web dentro del Curso de Programación Android nos vamos a centrar en los servicios web que utilizan el estándar SOAP como mecanismo de comunicación. A diferencia de otros tutoriales, no sólo vamos describir cómo acceder a este tipo de servicios desde una aplicación Android, [...]]]></description>
			<content:encoded><![CDATA[<p>En este primer artículo que vamos a dedicar a los <strong>servicios web</strong> dentro del <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313">Curso de Programación Android</a> nos vamos a centrar en los servicios web que utilizan el estándar <strong>SOAP</strong> como mecanismo de comunicación.</p>
<p>A diferencia de otros tutoriales, no sólo vamos describir cómo acceder a este tipo de servicios desde una aplicación Android, sino que también veremos como crear un servicio web SOAP mediante ASP.NET para acceder a una base de datos SQL Server. De esta forma pretendo ilustrar la &#8220;arquitectura&#8221; completa de una aplicación Android que acceda a datos almacenados en un servidor de base de datos externo. <strong>Nota:</strong> Aunque intentaré aportar el máximo número de detalles, es imposible abarcarlo todo en un solo artículo, por lo que el texto supondrá unos conocimientos mínimos de Visual Studio y del lenguaje C#.</p>
<p>Como caso de ejemplo, vamos a crear una aplicación sencilla capaz de gestionar un listado de &#8220;clientes&#8221; que contendrá el nombre y teléfono de cada uno de ellos. Nuestra aplicación será capaz de consultar el listado actual de clientes almacenados en el servidor externo y de insertar nuevos clientes en la base de datos. Como siempre, se trata de un ejemplo muy sencillo pero creo que lo suficientemente completo como para que sirva de base para crear otras aplicaciones más complejas.</p>
<p>Como software necesario, en este caso utilizaré <em>Visual Studio 2010</em> y <em>SQL Server 2008 R2</em> para crear el servicio web y la base de datos respectivamente.  Podéis descargar de forma gratuita las versiones <em>Express</em> de ambos productos (más que suficientes para crear una aplicación como la que describiremos en este artículo) desde la <a title="Web Microsoft - Descarga Visual Studio 2010 Express" href="http://www.microsoft.com/visualstudio/en-us/products/2010-editions/express" target="_blank">web oficial</a> de Microsoft. También es recomendable instalar <em>SQL Server 2008 Management Studio Express</em>, descargable también de forma gratuita desde <a title="Descargar SQL Server 2008 Management Studio Express" href="http://www.microsoft.com/download/en/details.aspx?id=22985" target="_blank">esta web</a>. Esta aplicación no es más que un gestor gráfico para acceder y manipular nuestras bases de datos  SQL Server con total comodidad.</p>
<p>Vamos comenzar por la base de todo el sistema, y esto es la base de datos a la que accederá el servicio web y, a través de éste, también la aplicación Android que crearemos más adelante. Para ello abrimos <em>SQL Server Management Studio</em>, nos conectamos a nuestro servidor SQL Server local, y pulsamos sobre la sección &#8220;<em>Databases</em>&#8221; del árbol de objetos que aparece a la izquierda. Sobre esta carpeta podemos acceder a la opción &#8220;<em>New Database&#8230;</em>&#8221; del menú contextual para crear una nueva base de datos.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-new-database.png"><img class="alignnone size-medium wp-image-2625" title="sqlserver-new-database" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-new-database-300x256.png" alt="sqlserver-new-database" width="300" height="256" /></a></p>
<p>En el cuadro de diálogo que aparece tan sólo indicaremos el nombre de la nueva base de datos, en mi caso la llamaré <em>DBCLIENTES</em>, y dejaremos el resto de opciones con sus valores por defecto.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-new-database-2.png"><img class="alignnone size-medium wp-image-2626" title="sqlserver-new-database-2" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-new-database-2-300x269.png" alt="sqlserver-new-database-2" width="300" height="269" /></a></p>
<p>Desplegamos el árbol de carpetas de nuestra recién creada base de datos <em>DBCLIENTES</em> y sobre la carpeta &#8220;<em>Tables</em>&#8221; ejecutamos la opción &#8220;<em>New table&#8230;</em>&#8221; para crear una nueva tabla.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-new-table.png"><img class="alignnone size-medium wp-image-2627" title="sqlserver-new-table" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-new-table-246x300.png" alt="sqlserver-new-table" width="246" height="300" /></a></p>
<p>Vamos a añadir sólo 3 campos a la tabla:</p>
<ul>
<li><span style="font-family: 'courier new', courier;">IdCliente</span>, de tipo <span style="font-family: 'courier new', courier;">int</span>, que será un código único identificativo del cliente.</li>
<li><span style="font-family: 'courier new', courier;">Nombre</span>, de tipo <span style="font-family: 'courier new', courier;">nvarchar(50)</span>, que contendrá el nombre del cliente.</li>
<li><span style="font-family: 'courier new', courier;">Telefono</span>, de tipo <span style="font-family: 'courier new', courier;">int</span>, que contendrá el teléfono del cliente.</li>
</ul>
<div></div>
<p>Marcaremos además el campo <span style="font-family: 'courier new', courier;">IdCliente</span> como <em>clave principal</em> de la tabla, y también como <em>campo de identidad autoincremental</em>, de modo que se calcule automáticamente cada vez que insertemos un nuevo cliente.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-diseno-tabla-identity.png"><img class="alignnone size-medium wp-image-2631" title="sqlserver-diseno-tabla-identity" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/sqlserver-diseno-tabla-identity-300x300.png" alt="sqlserver-diseno-tabla-identity" width="300" height="300" /></a></p>
<p>Con esto ya tenemos nuestra tabla finalizada, por lo que sólo nos queda guardarla con el nombre que deseemos, que para este ejemplo será &#8220;<em>Clientes</em>&#8220;.</p>
<p>Hecho, ya tenemos nuestra base de datos SQL Server creada y una tabla preparada para almacenar los datos asociados a nuestros clientes. El siguiente paso será crear el servicio web que manipulará los datos de esta tabla.</p>
<p>Para crear el servicio abriremos Visual Studio 2010 y crearemos un nuevo proyecto web en C# utilizando la plantilla &#8220;<em>ASP.NET Empty Web Application</em>&#8220;. En un alarde de originalidad lo llamaremos &#8220;<em>ServicioWebSoap</em>&#8220;.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/nuevo-proyecto-web.png"><img class="alignnone size-medium wp-image-2580" title="nuevo-proyecto-web" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/nuevo-proyecto-web-300x160.png" alt="nuevo-proyecto-web" width="300" height="160" /></a></p>
<p>Una vez creado el proyecto, añadiremos a éste un nuevo servicio web mediante el menú &#8220;<em>Project / Add new item&#8230;</em>&#8220;. Lo llamaremos &#8220;<em>ServicioClientes.asmx</em>&#8220;.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/nuevo-servicio-web.png"><img class="alignnone size-medium wp-image-2582" title="nuevo-servicio-web" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/nuevo-servicio-web-300x168.png" alt="nuevo-servicio-web" width="300" height="168" /></a></p>
<p>Una vez añadido aparecerá en pantalla el código fuente por defecto del nuevo servicio web, que contiene un único método de ejemplo llamado <span style="font-family: 'courier new', courier;">HelloWorld()</span>. Este método podemos eliminarlo ya que no nos servirá de nada, y además modificaremos el atributo <span style="font-family: 'courier new', courier;">WebService</span> de la clase para indicar que el namespace será &#8220;<em>http://sgoliver.net/</em>&#8221; (en vuestro caso podéis indicar otro valor). Con esto, nos quedaría un código base como éste:</p>
<pre class="brush: csharp; title: ; notranslate">
namespace ServicioWebSoap
{
    /// &lt;summary&gt;
    /// Summary description for ServicioClientes
    /// &lt;/summary&gt;
    [WebService(Namespace = &quot;http://sgoliver.net/&quot;)]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]

    public class ServicioClientes : System.Web.Services.WebService
    {
        //Métodos del servicio web
        //...
    }
}
</pre>
<p>Pues bien, dentro de esta clase <span style="font-family: 'courier new', courier;">ServicioClientes</span> es donde añadiremos todos los métodos públicos que queramos tener accesibles a través de nuestro servicio web, siempre precedidos por el atributo <span style="font-family: 'courier new', courier;">[WebMethod]</span> como veremos en breve. Para nuestro ejemplo vamos a crear tres métodos, el primero para obtener el listado completo de clientes almacenados en la base de datos, y los otros dos para insertar nuevos clientes (más adelante explicaré por qué dos, aunque adelanto que es tan sólo por motivos didácticos).</p>
<p>Antes de crear estos métodos, vamos a crear una nueva clase sencilla que nos sirva para encapsular los datos de un cliente. La añadiremos mediante la opción &#8220;<em>Project / Add class&#8230;</em>&#8221; de Visual Studio y la llamaremos &#8220;<em>Cliente.cs</em>&#8220;. Esta clase contendrá únicamente los 3 campos que ya comentamos al crear la base de datos y dos constructores, uno de ellos por defecto que tan solo inicializará los campos y otro con parámetros para crear clientes a partir de sus datos identificativos. El código de la clase es muy sencillo, y tan solo cabe mencionar que definiremos sus tres atributos como propiedades automáticas de C# utilizando para ello la notación abreviada <span style="font-family: 'courier new', courier;">{get; set;}</span></p>
<pre class="brush: csharp; title: ; notranslate">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ServicioWebSoap
{
    public class Cliente
    {
        public int Id {get; set;}
        public string Nombre {get; set;}
        public int Telefono {get; set;}

        public Cliente()
        {
            this.Id = 0;
            this.Nombre = &quot;&quot;;
            this.Telefono = 0;
        }

        public Cliente(int id, string nombre, int telefono)
        {
            this.Id = id;
            this.Nombre = nombre;
            this.Telefono = telefono;
        }
    }
}
</pre>
<p>Vamos ahora a escribir el primero de los métodos que haremos accesible a través de nuestro servicio web. Lo llamaremos <span style="font-family: 'courier new', courier;">NuevoCliente()</span>, recibirá como parámetros de entrada un nombre y un teléfono, y se encargará de insertar un nuevo registro en nuestra tabla de clientes con dichos datos. Recordemos que el ID del cliente no será necesario insertarlo de forma explícita ya que lo hemos definido en la base de datos como campo autoincremental. Para el trabajo con la base de datos vamos a utilizar la API clásica de <em>ADO.NET</em>, aunque podríamos utilizar cualquier otro mécanismo de acceso a datos, como por ejemplo <a title="Web Oficial Entity Framework" href="http://msdn.microsoft.com/es-es/library/bb399572.aspx" target="_blank">Entity Framework</a>, <a title="Web Oficial NHibernate" href="http://nhforge.org/Default.aspx" target="_blank">NHibernate</a>, etc.</p>
<p>De esta forma, el primer paso será crear una conexión a SQL Server mediante la clase <span style="font-family: 'courier new', courier;">SQLConnection</span>, pasando como parámetro la cadena de conexión correspondiente (en vuestro caso tendréis que modificarla para adaptarla a vuestro entorno). Tras esto abriremos la conexión mediante una llamada al método <span style="font-family: 'courier new', courier;">Open()</span>, definiremos el comando SQL que queremos ejecutar creando un objeto <span style="font-family: 'courier new', courier;">SQLCommand</span>. Ejecutaremos el comando llamando al método <span style="font-family: 'courier new', courier;">ExecuteNonQuery()</span> recogiendo el resultado en una variable, y finalmente cerraremos la conexión llamando a <span style="font-family: 'courier new', courier;">Close()</span>. Por último devolveremos el resultado del comando SQL como valor de retorno del método web.</p>
<p>Como podéis ver en el código siguiente, los valores a insertar en la base de datos los hemos especificado en la consulta SQL como parámetros variable (precedidos por el carácter &#8216;@&#8217;). Los valores de estos parámetros los definimos y añadimos al comando SQL mediante el método <span style="font-family: 'courier new', courier;">Add()</span> de su propiedad <span style="font-family: 'courier new', courier;">Parameters</span>. Esta opción es más recomendable que la opción clásica de concatenar directamente la cadena de texto de la sentencia SQL con los parámetros variables, ya que entre otras cosas servirá para evitar [en gran medida] posibles ataques de <a title="Wikipedia Inyección SQL" href="http://es.wikipedia.org/wiki/Inyecci%C3%B3n_SQL" target="_blank">inyección SQL</a>. El resultado devuelto por este método será el número de registros afectados por la sentencia SQL ejecutada, por lo que para verificar si se ha ejecutado correctamente bastará con comprobar que el resultado es igual a 1.</p>
<pre class="brush: csharp; title: ; notranslate">
[WebMethod]
public int NuevoClienteSimple(string nombre, int telefono)
{
    SqlConnection con =
        new SqlConnection(
            @&quot;Data Source=SGOLIVERPC\SQLEXPRESS;Initial Catalog=DBCLIENTES;Integrated Security=True&quot;);

    con.Open();

    string sql = &quot;INSERT INTO Clientes (Nombre, Telefono) VALUES (@nombre, @telefono)&quot;;

    SqlCommand cmd = new SqlCommand(sql, con);

    cmd.Parameters.Add(&quot;@nombre&quot;, System.Data.SqlDbType.NVarChar).Value = nombre;
    cmd.Parameters.Add(&quot;@telefono&quot;, System.Data.SqlDbType.Int).Value = telefono;

    int res = cmd.ExecuteNonQuery();

    con.Close();

    return res;
}
</pre>
<p>En el código anterior, podéis ver que hemos precedido el método con el atributo <span style="font-family: 'courier new', courier;">[WebMethod]</span>. Con este atributo estamos indicando que el método será accesible a través de nuestro servicio web y podrá ser llamado desde cualquier aplicación que se conecte con éste.</p>
<p>La siguiente operación que vamos a añadir a nuestro servicio web será la que nos permita obtener el listado completo de clientes registrados en la base de datos. Llamaremos al método <span style="font-family: 'courier new', courier;">ListadoClientes()</span> y devolverá un array de objetos de tipo <span style="font-family: 'courier new', courier;">Cliente</span>. El código del método será muy similar al ya comentado para la operación de inserción, con la única diferencia de que en esta ocasión la sentencia SQL será obviamente un <span style="font-family: 'courier new', courier;">SELECT</span> y que utilizaremos un objeto <span style="font-family: 'courier new', courier;">SqlDataReader</span> para leer los resultados devueltos por la consulta. Los registros leídos los iremos añadiendo a una lista de tipo <span style="font-family: 'courier new', courier;">List&lt;Clientes&gt;</span> y una vez completada la lectura convertiremos esta lista en un array de clientes llamando al método <span style="font-family: 'courier new', courier;">ToArray()</span>. Este último array será el que devolveremos como resultado del método. Veamos el código completo del método:</p>
<pre class="brush: csharp; title: ; notranslate">
[WebMethod]
public Cliente[] ListadoClientes()
{
    SqlConnection con =
        new SqlConnection(
            @&quot;Data Source=SGOLIVERPC\SQLEXPRESS;Initial Catalog=DBCLIENTES;Integrated Security=True&quot;);

    con.Open();

    string sql = &quot;SELECT IdCliente, Nombre, Telefono FROM Clientes&quot;;

    SqlCommand cmd = new SqlCommand(sql, con);

    SqlDataReader reader = cmd.ExecuteReader();

    List&lt;Cliente&gt; lista = new List&lt;Cliente&gt;();

    while (reader.Read())
    {
        lista.Add(
            new Cliente(reader.GetInt32(0),
                        reader.GetString(1),
                        reader.GetInt32(2)));
    }

    con.Close();

    return lista.ToArray();
}
</pre>
<p>Por último, como dijimos al principio, vamos a añadir un tercer método web con fines puramente didácticos. Si os fijáis en los dos métodos anteriores, veréis que en uno de los casos devolvemos como resultado un valor simple, un número entero, y en el otro caso un objeto complejo, en concreto un array de objetos de tipo <span style="font-family: 'courier new', courier;">Cliente</span>. Sin embargo, ninguno de ellos recibe como parámetro un tipo complejo, tan sólo valores simples (enteros y strings). Esto no tiene mucha relevancia en el código de nuestro servicio web, pero sí tiene ciertas peculiaridades a la hora de realizar la llamada al servicio desde la aplicación Android. Por lo que para poder explicar esto más adelante añadiremos un nuevo método de inserción de clientes que, en vez de recibir los parámetros de nombre y teléfono por separado, recibirá como dato de entrada un objeto <span style="font-family: 'courier new', courier;">Cliente</span>.</p>
<p>El código de este método, que llamaremos <span style="font-family: 'courier new', courier;">NuevoClienteObjeto()</span>, será exactamente igual al anterior método de inserción, con la única diferencia de los parámetros de entrada, por lo que no nos detendremos en comentar nada más.</p>
<pre class="brush: csharp; title: ; notranslate">
[WebMethod]
public int NuevoClienteObjeto(Cliente cliente)
{
    SqlConnection con = new SqlConnection(@&quot;Data Source=SGOLIVERPC\SQLEXPRESS;Initial Catalog=DBCLIENTES;Integrated Security=True&quot;);

    con.Open();

    string sql = &quot;INSERT INTO Clientes (Nombre, Telefono) VALUES (@nombre, @telefono)&quot;;

    SqlCommand cmd = new SqlCommand(sql, con);

    cmd.Parameters.Add(&quot;@nombre&quot;, System.Data.SqlDbType.NVarChar).Value = cliente.Nombre;
    cmd.Parameters.Add(&quot;@telefono&quot;, System.Data.SqlDbType.Int).Value = cliente.Telefono;

    int res = cmd.ExecuteNonQuery();

    con.Close();

    return res;
}
</pre>
<p>Y con esto hemos finalizado nuestro servicio web. Podemos probar su funcionamiento con la página de prueba que proporciona ASP.NET al ejecutar el proyecto en Visual Studio. Si ejecutamos el proyecto se abrirá automáticamente un explorador web que mostrará una página con todas las operaciones que hemos definido en el servicio web.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/operaciones-ws.png"><img class="alignnone size-medium wp-image-2632" title="operaciones-ws" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/operaciones-ws-300x113.png" alt="operaciones-ws" width="300" height="113" /></a></p>
<p>Si pulsamos sobre cualquiera de ellas pasaremos a una nueva página que nos permitirá dar valores a sus parámetros y ejecutar el método correspondiente para visualizar sus resultados. Si pulsamos por ejemplo en la operación <span style="font-family: 'courier new', courier;">NuevoCliente(string, int)</span> llegaremos a esta página:</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/operaciones-nuevocliente.png"><img class="alignnone size-medium wp-image-2633" title="operaciones-nuevocliente" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/operaciones-nuevocliente-300x186.png" alt="operaciones-nuevocliente" width="300" height="186" /></a></p>
<p>Aquí podemos dar valores a los dos parámetros y ejecutar el método (botón &#8220;<em>Invoke</em>&#8220;), lo que nos devolverá la respuesta codificada en un XML según el estandar SOAP.</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/operaciones-nuevocliente-res.png"><img class="alignnone size-medium wp-image-2634" title="operaciones-nuevocliente-res" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/operaciones-nuevocliente-res-300x99.png" alt="operaciones-nuevocliente-res" width="300" height="99" /></a></p>
<p>Como podéis comprobar, en principio el XML devuelto no es fácil de interpretar, pero esto es algo que no debe preocuparnos demasiado ya que en principio será transparente para nosotros, las librerías que utilizaremos más adelante en Android para la llamada a servicios SOAP se encargarán de <em>parsear</em> convenientemente estas respuestas y de darnos tan sólo aquella parte que necesitamos.</p>
<p>En el siguiente artículo nos ocuparemos de la construcción de una aplicación Android que sea capaz de conectarse a este servicio web y de llamar a los métodos que hemos definido para insertar y recuperar clientes de nuestra base de datos. Veremos además cómo podemos ejecutar y probar en local todo el sistema de forma que podamos comprobar que todo funciona como esperamos.</p>
<p>Podéis descargar el código fuente completo del servicio web desde <a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/02/ServicioWebSoap.zip">este enlace</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2571</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Alternativas para leer y escribir XML (y otros ficheros) en Android</title>
		<link>http://www.sgoliver.net/blog/?p=2544&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=alternativas-para-leer-y-escribir-xml-y-otros-ficheros-en-android</link>
		<comments>http://www.sgoliver.net/blog/?p=2544#comments</comments>
		<pubDate>Mon, 23 Jan 2012 16:48:53 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[asset]]></category>
		<category><![CDATA[curso android]]></category>
		<category><![CDATA[escribir]]></category>
		<category><![CDATA[leer]]></category>
		<category><![CDATA[local]]></category>
		<category><![CDATA[raw]]></category>
		<category><![CDATA[recursos]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2544</guid>
		<description><![CDATA[Un artículo rápido antes de seguir con más temas del Curso de Programación Android. Hace ya algún tiempo dedicamos varios artículos (I, II, III, IV) al tratamiento de ficheros XML en Android y vimos varias alternativas a la hora de leer (parsear) ficheros de este tipo. En todos los ejemplos decidí utilizar como entrada un [...]]]></description>
			<content:encoded><![CDATA[<p>Un artículo rápido antes de seguir con más temas del <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313">Curso de Programación Android</a>.</p>
<p>Hace ya algún tiempo dedicamos varios artículos (<a title="Tratamiento de XML en Android (I): SAX" href="http://www.sgoliver.net/blog/?p=1542">I</a>, <a title="Tratamiento de XML en Android (II): SAX Simplificado" href="http://www.sgoliver.net/blog/?p=1580">II</a>, <a title="Tratamiento de XML en Android (III): DOM" href="http://www.sgoliver.net/blog/?p=1588">III</a>, <a title="Tratamiento de XML en Android (IV): XmlPull" href="http://www.sgoliver.net/blog/?p=1604">IV</a>) al tratamiento de ficheros XML en Android y vimos varias alternativas a la hora de leer (<em>parsear</em>) ficheros de este tipo. En todos los ejemplos decidí utilizar como entrada un fichero <em>online</em>, que obteníamos desde una determinada URL, porque pensé que sería el caso más típico que íbamos a necesitar en la mayoría de aplicaciones. Sin embargo, desde entonces han sido muchas las consultas que me han llegado relativas a cómo utilizar estos métodos para leer un fichero XML que se encuentre en un recurso local. Adicionalmente también he recibido muchos comentarios consultando las alternativas existentes para <em>escribir</em> ficheros XML. Pues bien, aunque las alternativas son muchas, voy a dedicar este pequeño artículo a intentar resolver ambas dudas.</p>
<p><span style="text-decoration: underline;"><strong>Escribir ficheros XML en Android</strong></span></p>
<p>Para escribir ficheros XML sin meternos en muchas complicaciones innecesarias se me ocurren básicamente dos formas. La primera de ellas, y la más sencilla y directa, es construir manualmente el XML utilizando por ejemplo un objeto <span style="font-family: 'courier new', courier;">StringBuilder</span> y tras esto escribir el resultado a un fichero en memoria interna o externa tal como ya vimos en los artículos dedicados a tratamiento de ficheros (<a title="Ficheros en Android (I): Memoria Interna" href="http://www.sgoliver.net/blog/?p=2019">I</a> y <a title="Ficheros en Android (II): Memoria Externa (Tarjeta SD)" href="http://www.sgoliver.net/blog/?p=2035">II</a>). Esto quedaría de una forma similar a la siguiente:</p>
<pre class="brush: java; title: ; notranslate">
//Creamos un fichero en la memoria interna
OutputStreamWriter fout =
	new OutputStreamWriter(
		openFileOutput(&quot;prueba.xml&quot;,
			Context.MODE_PRIVATE));

StringBuilder sb = new StringBuilder();

//Construimos el XML
sb.append(&quot;&quot;);
sb.append(&quot;&quot; + &quot;Usuario1&quot; + &quot;&quot;);
sb.append(&quot;&quot; + &quot;ApellidosUsuario1&quot; + &quot;&quot;);
sb.append(&quot;&quot;);

//Escribimos el resultado a un fichero
fout.write(sb.toString());
fout.close();
</pre>
<p>Como método alternativo también podemos utilizar el <em>serializador</em> XML incluido con la API <span style="font-family: 'courier new', courier;">XmlPull</span>. Aunque no es un método tan directo como el anterior no deja de ser bastante intuitivo. Los pasos para conseguir esto serán crear el objeto <span style="font-family: 'courier new', courier;">XmlSerializer</span>, crear el fichero de salida, asignar el fichero como salida del <em>serializador</em> y construir el XML mediante los métodos <span style="font-family: 'courier new', courier;">startTag()</span>, <span style="font-family: 'courier new', courier;">text()</span> y <span style="font-family: 'courier new', courier;">endTag()</span> pasándoles los nombres de etiqueta y contenidos de texto correspondientes (aunque existen más métodos, por ejemplo para escribir atributos de una etiqueta, éstos tres son los principales). Finalmente deberemos llamar a <span style="font-family: 'courier new', courier;">endDocument()</span> para finalizar y cerrar nuestro documento XML. Un ejemplo equivalente al anterior utilizando este método sería el siguiente:</p>
<pre class="brush: java; title: ; notranslate">
//Creamos el serializer
XmlSerializer ser = Xml.newSerializer();

//Creamos un fichero en memoria interna
OutputStreamWriter fout =
	new OutputStreamWriter(
		openFileOutput(&quot;prueba_pull.xml&quot;,
			Context.MODE_PRIVATE));

//Asignamos el resultado del serializer al fichero
ser.setOutput(fout);

//Construimos el XML
ser.startTag(&quot;&quot;, &quot;usuario&quot;);

ser.startTag(&quot;&quot;, &quot;nombre&quot;);
ser.text(&quot;Usuario1&quot;);
ser.endTag(&quot;&quot;, &quot;nombre&quot;);

ser.startTag(&quot;&quot;, &quot;apellidos&quot;);
ser.text(&quot;ApellidosUsuario1&quot;);
ser.endTag(&quot;&quot;, &quot;apellidos&quot;);

ser.endTag(&quot;&quot;, &quot;usuario&quot;);

ser.endDocument();

fout.close();
</pre>
<p>Así de sencillo, no merece la pena complicarse la vida con otros métodos más complicados.</p>
<p><span style="text-decoration: underline;"><strong>Leer ficheros XML en Android desde recursos locales</strong></span></p>
<p>Para leer ficheros XML desde un recurso local se me ocurren varias posibilidades, por ejemplo leerlo desde la memoria interna/externa del dispositivo, leer un XML desde un recurso XML (carpeta /res/xml), desde un recurso Raw (carpeta /res/raw), o directamente desde la carpeta /assets de nuestro proyecto. Salvo en el segundo caso (recurso XML), en todos los demás la solución va a pasar por conseguir una referencia de tipo <span style="font-family: 'courier new', courier;">InputStream</span> (os recuerdo que cualquiera de los métodos que vimos para leer un XML partían de una referencia de este tipo) a partir de nuestro fichero o recurso XML, sea cual sea su localización.</p>
<p>Así, por ejemplo, si el fichero XML está almacenado en la memoria interna de nuestro dispositivo, podríamos acceder a él mediante el método <span style="font-family: 'courier new', courier;">openFileInput()</span> tal como vimos en los artículos dedicados a tratamiento de ficheros. Este método devuelve un objeto de tipo <span style="font-family: 'courier new', courier;">FileInputStream</span>, que al derivar de <span style="font-family: 'courier new', courier;">InputStream</span> podemos utilizarlo como entrada a cualquiera de los mecanismos de lectura de XML (<a title="Tratamiento de XML en Android (I): SAX" href="http://www.sgoliver.net/blog/?p=1542">SAX</a>, <a title="Tratamiento de XML en Android (III): DOM" href="http://www.sgoliver.net/blog/?p=1588">DOM</a>, <a title="Tratamiento de XML en Android (IV): XmlPull" href="http://www.sgoliver.net/blog/?p=1604">XmlPull</a>).</p>
<pre class="brush: java; title: ; notranslate">
//Obtenemos la referencia al fichero XML de entrada
FileInputStream fil = openFileInput(&quot;prueba.xml&quot;);

//DOM (Por ejemplo)
DocumentBuilderFactory factory =
    DocumentBuilderFactory.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(fil);

//A partir de aquí se trataría el árbol DOM como siempre.
//Por ejemplo:
Element root = dom.getDocumentElement();

//...
</pre>
<p>En el caso de encontrarse el fichero como recurso Raw, es decir, en la carpeta /res/raw, tendríamos que obtener la referencia al fichero mediante el método <span style="font-family: 'courier new', courier;">getRawResource()</span> pasándole como parámetro el ID de recurso del fichero. Esto nos devuelve directamente el stream de entrada en forma de <span style="font-family: 'courier new', courier;">InputStream</span>.</p>
<pre class="brush: java; title: ; notranslate">
//Obtenemos la referencia al fichero XML de entrada
InputStream is = getResources().openRawResource(R.raw.prueba);

//DOM (Por ejemplo)
DocumentBuilderFactory factory =
    DocumentBuilderFactory.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(is);

//A partir de aquí se trataría el árbol DOM como siempre.
//Por ejemplo:
Element root = dom.getDocumentElement();

//...
</pre>
<p>Por último, si el XML se encontrara en la carpeta /assets de nuestra aplicación, accederíamos a él a través del <span style="font-family: 'courier new', courier;">AssetManager</span>, el cual podemos obtenerlo con el método <span style="font-family: 'courier new', courier;">getAssets()</span> de nuestra actividad. Sobre este <span style="font-family: 'courier new', courier;">AssetManager</span> tan sólo tendremos que llamar al método <span style="font-family: 'courier new', courier;">open()</span> con el nombre del fichero para obtener una referencia de tipo <span style="font-family: 'courier new', courier;">InputStream</span> a nuestro XML.</p>
<pre class="brush: java; title: ; notranslate">
//Obtenemos la referencia al fichero XML de entrada
AssetManager assetMan = getAssets();
InputStream is = assetMan.open(&quot;prueba_asset.xml&quot;);

//DOM (Por ejemplo)
DocumentBuilderFactory factory =
    DocumentBuilderFactory.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(is);

//A partir de aquí se trataría el árbol DOM como siempre.
//Por ejemplo:
Element root = dom.getDocumentElement();

//...
</pre>
<p>El último caso, algo distinto a los anteriores, pasaría por leer el XML desde un recurso XML localizado en la carpeta /res/xml de nuestro proyecto. Para acceder a un recurso de este tipo debemos utilizar el método <span style="font-family: 'courier new', courier;">getXml()</span> al cual debemos pasarle como parámetro el ID de recurso del fichero XML. Esto nos devolverá un objeto <span style="font-family: 'courier new', courier;">XmlResourceParser</span>, que no es más que un parser de tipo XmlPull como el que <a title="Tratamiento de XML en Android (IV): XmlPull" href="http://www.sgoliver.net/blog/?p=1604">ya comentamos</a> en su momento. Por tanto, la forma de trabajar con este parser será idéntica a la que ya conocemos, por ejemplo:</p>
<pre class="brush: java; title: ; notranslate">
//Obtenemos un parser XmlPull asociado a nuestro XML
XmlResourceParser xrp = getResources().getXml(R.xml.prueba);

//A partir de aquí utilizamos la variable 'xrp' como
//cualquier otro parser de tipo XmlPullParser. Por ejemplo:

int evento = xrp.getEventType();

if(evento == XmlPullParser.START_DOCUMENT)
	Log.i(&quot;XmlTips&quot;, &quot;Inicio del documento&quot;);

//...
</pre>
<p>Por último, indicar que todas estas formas de acceder a un fichero en Android no se limitan únicamente a los de tipo XML, sino que pueden utilizarse para leer cualquier otro tipo de ficheros.</p>
<p>Como siempre, podéis descargar el <a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/01/android-xml-tips.zip">código fuente completo</a> de un sencillo programa de ejemplo que utiliza cada una de las alternativas comentadas.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2544</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Spam en el foro</title>
		<link>http://www.sgoliver.net/blog/?p=2536&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=spam-en-el-foro</link>
		<comments>http://www.sgoliver.net/blog/?p=2536#comments</comments>
		<pubDate>Sun, 15 Jan 2012 15:16:10 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[activación]]></category>
		<category><![CDATA[foro]]></category>
		<category><![CDATA[spam]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2536</guid>
		<description><![CDATA[Debido a la aparición de spam en el foro me he visto obligado a habilitar la activación de nuevos registros vía email, es decir, que todos los nuevos usuarios que se registren recibirán un correo electrónico con un enlace que deberán pulsar para activar su usuario y poder acceder al foro. También he añadido dos [...]]]></description>
			<content:encoded><![CDATA[<p>Debido a la aparición de spam en el foro me he visto obligado a habilitar la activación de nuevos registros vía email, es decir, que todos los nuevos usuarios que se registren recibirán un correo electrónico con un enlace que deberán pulsar para activar su usuario y poder acceder al foro. También he añadido dos nuevos campos personalizados al registro para intentar evitar el registro de bots. Son los marcados en rojo en la imagen siguiente:</p>
<p><a href="http://www.sgoliver.net/blog/wp-content/uploads/2012/01/antispam.png"><img class="alignnone  wp-image-2541" title="antispam" src="http://www.sgoliver.net/blog/wp-content/uploads/2012/01/antispam.png" alt="antispam" width="481" height="237" /></a></p>
<p>Creo que las respuestas a ambas preguntas están claras, pero si tenéis cualquier problema con el registro no tenéis más que poneros en contacto conmigo vía email.</p>
<p>Adicionalmente, he eliminado algunos de los últimos usuarios registrados, bajo la sospecha de que pueda tratarse de bots de spam. Pido disculpas si alguna de las cuentas eliminadas pertenecía a algún usuario real del foro. Si es así tan sólo tenéis que volver a realizar el registro con el mismo nombre de usuario.</p>
<p>Siento las molestias.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2536</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Estadísticas del Blog en 2011</title>
		<link>http://www.sgoliver.net/blog/?p=2496&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=estadisticas-del-blog-en-2011</link>
		<comments>http://www.sgoliver.net/blog/?p=2496#comments</comments>
		<pubDate>Wed, 28 Dec 2011 14:45:50 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[2011]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[estadísticas]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2496</guid>
		<description><![CDATA[Aunque estos datos no le interesarán a nadie, a nivel personal me parece interesante hacer balance de cómo le ha ido el año al blog durante el 2011. A nivel general, las estadísticas más importantes se muestran en la siguiente imagen: Comparando los datos con los del año pasado el porcentaje de mejora es realmente [...]]]></description>
			<content:encoded><![CDATA[<p>Aunque estos datos no le interesarán a nadie, a nivel personal me parece interesante hacer balance de cómo le ha ido el año al blog durante el 2011.</p>
<p>A nivel general, las estadísticas más importantes se muestran en la siguiente imagen:</p>
<div id="attachment_2497" class="wp-caption alignnone" style="width: 442px"><a href="http://www.sgoliver.net/blog/wp-content/uploads/2011/12/estadisticas-generales.png"><img class="size-full wp-image-2497  " title="estadisticas-generales" src="http://www.sgoliver.net/blog/wp-content/uploads/2011/12/estadisticas-generales.png" alt="estadisticas-generales" width="432" height="282" /></a><p class="wp-caption-text">Estadísiticas Generales 2011</p></div>
<p>Comparando los datos con los del año pasado el porcentaje de mejora es realmente grande. El número de visitas se ha multiplicado por 9, el promedio de páginas vistas ha crecido algo más de 1.1 páginas, el promedio de tiempo en el sitio ha aumentado en más de 2 minutos,  y el porcentaje de rebote ha disminuido más de 12 puntos. Con todo esto, acabamos el año con una media de unas 1100 visitas diarias, aunque la media en los últimos meses es muy superior.</p>
<p>En cuanto al número de visitas por país, España es naturalmente el origen mayoritario de las visitas, con más de un 55% del total, pero también ha crecido mucho el porcentaje de visitas desde paises sudamericanos. Muchas gracias a todos los que seguís el blog desde el otro lado del charco.</p>
<div id="attachment_2500" class="wp-caption alignnone" style="width: 347px"><a href="http://www.sgoliver.net/blog/wp-content/uploads/2011/12/estadisticas-paises.png"><img class="size-full wp-image-2500" title="estadisticas-paises" src="http://www.sgoliver.net/blog/wp-content/uploads/2011/12/estadisticas-paises.png" alt="estadisticas-paises" width="337" height="309" /></a><p class="wp-caption-text">Visitas por Pais</p></div>
<p>Las páginas más visitadas en este último año han sido sin lugar a dudas las dedicadas al <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313">Curso de Programación en Android</a>. Por supuesto el <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313">índice del curso</a> se lleva el mayor porcentaje (27.70% de las páginas vistas), y como mayoría dentro del <em>TOP 10</em>, los artículos de iniciación del curso (<a title="Entorno de desarrollo Android" href="http://www.sgoliver.net/blog/?p=1267">entorno de desarrollo</a>, <a title="Componentes de una aplicación Android" href="http://www.sgoliver.net/blog/?p=1295">componentes de una aplicación</a>, <a title="Estructura de un proyecto Android" href="http://www.sgoliver.net/blog/?p=1278">estructura de un proyecto</a>, y <a title="Desarrollando una aplicación Android sencilla" href="http://www.sgoliver.net/blog/?p=1316">desarrollo de la primera aplicación</a>).</p>
<div id="attachment_2502" class="wp-caption alignnone" style="width: 458px"><a href="http://www.sgoliver.net/blog/wp-content/uploads/2011/12/estadisticas-paginas.png"><img class="size-full wp-image-2502" title="estadisticas-paginas" src="http://www.sgoliver.net/blog/wp-content/uploads/2011/12/estadisticas-paginas.png" alt="estadisticas-paginas" width="448" height="437" /></a><p class="wp-caption-text">Páginas más visitadas</p></div>
<p>Por último, si miramos las fuentes del tráfico que llega a la web vemos el siguiente desglose.</p>
<div id="attachment_2503" class="wp-caption alignnone" style="width: 421px"><a href="http://www.sgoliver.net/blog/wp-content/uploads/2011/12/estadisticas-fuentes.png"><img class="size-full wp-image-2503" title="estadisticas-fuentes" src="http://www.sgoliver.net/blog/wp-content/uploads/2011/12/estadisticas-fuentes.png" alt="estadisticas-fuentes" width="411" height="156" /></a><p class="wp-caption-text">Fuentes del tráfico</p></div>
<p>Más de la mitad de las visitas han llegado desde alguna búsqueda en un buscador de internet (Google en un 98%) lo que hace pensar que el posicionamiento del blog no funciona del todo mal. Además, ha crecido en gran medida el <em>tráfico de referencia</em> lo que también es una buena noticia, que te enlacen desde otras webs siempre es del agrado de todos y por supuesto beneficioso para los usuarios.</p>
<p>En fin, que a pesar de tratarse de números modestos, parece que poco a poco seguimos mejorando. Esperemos que el año próximo podamos decir lo mismo. Felices fiestas, y buen comienzo de año a todos.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2496</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Cambios estéticos en el blog</title>
		<link>http://www.sgoliver.net/blog/?p=2445&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cambios-esteticos-en-el-blog</link>
		<comments>http://www.sgoliver.net/blog/?p=2445#comments</comments>
		<pubDate>Tue, 27 Dec 2011 14:47:56 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[actualización]]></category>
		<category><![CDATA[aspecto]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[tema]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2445</guid>
		<description><![CDATA[Como podéis comprobar estoy realizando algunos cambios en la estética del blog. Lo que veis en este momento no es definitivo, por lo que muy probablemente en los próximos días iréis notando algunos cambios más por aquí y por allá. De forma paralela estoy actualizando algunos contenidos para que se adapten todo lo posible al [...]]]></description>
			<content:encoded><![CDATA[<p>Como podéis comprobar estoy realizando algunos cambios en la estética del blog. Lo que veis en este momento no es definitivo, por lo que muy probablemente en los próximos días iréis notando algunos cambios más por aquí y por allá.</p>
<p>De forma paralela estoy actualizando algunos contenidos para que se adapten todo lo posible al nuevo diseño, tratando de evitar descuadres y/o errores de formato que impidan una correcta lectura de los textos y visualización de las imágenes.</p>
<p>Espero que este periodo de transición no se alargue demasiado en el tiempo. Mientras tanto, espero disculpéis los posibles problemas que pudieran aparecer.</p>
<p>Por supuesto, si detectáis cualquier problema o tenéis alguna sugerencia/crítica sobre los cambios realizados no dudéis en poneros en contacto conmigo a través de cualquiera de los canales disponibles (comentarios, email, foro, twitter o google+).</p>
<p>Muchas gracias.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2445</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Segunda versión del Curso de Programación Android en PDF</title>
		<link>http://www.sgoliver.net/blog/?p=2313&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=segunda-version-del-curso-de-programacion-android-en-pdf</link>
		<comments>http://www.sgoliver.net/blog/?p=2313#comments</comments>
		<pubDate>Mon, 21 Nov 2011 19:34:27 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[curso]]></category>
		<category><![CDATA[pdf]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2313</guid>
		<description><![CDATA[Después de una larga espera, por fin está disponible la segunda versión del Curso de Programación Android en formato PDF. Como ya indicaba en el artículo anterior, aunque pensaba publicar este segundo volumen como un documento independiente, suplementario al que ya había publicado, finalmente he decidido aunar todo el contenido en un solo documento. Aprovechando [...]]]></description>
			<content:encoded><![CDATA[<p>Después de una larga espera, por fin está disponible la segunda versión del <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313">Curso de Programación Android</a> en formato PDF.</p>
<p>Como ya indicaba en el artículo anterior, aunque pensaba publicar este segundo volumen como un documento independiente, suplementario al que ya había publicado, finalmente he decidido aunar todo el contenido en un solo documento. Aprovechando esta segunda recopilación he remaquetado todo el contenido para intentar dar al curso una forma mucho más homogénea y parecida a un libro técnico, y así alejarlo un poco de la simple recopilación de artículos que suponía la primera parte publicada. Adicionalmente, he modificado un poco el aspecto visual, he corregido algunas erratas que me habéis ido comunicando, y he sustituido todas las imágenes por su versión a tamaño completo para mejorar su visualización y facilitar la lectura. Espero que todos estos cambios hagan de este documento un recurso de más valor para todos.</p>
<p>El sistema de donaciones para su adquisición se mantiene a través de PayPal. La donación mínima pasará a ser de <strong>1,50 euros</strong>. Espero entendáis y disculpéis este pequeño incremento, pero el libro tiene muchísimo trabajo detrás y creo sinceramente que os merecerá la pena disponer de esta versión completa y revisada del curso, en vez de contar con dos documentos independientes.</p>
<p>Podéis consultar el índice completo del curso, descargar un fragmento de muestra del documento, y acceder al sistema de donación desde la <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313">página principal del proyecto</a>, o desde el botón &#8220;<em>Donar</em>&#8221; disponible al final de este artículo.</p>
<p>Muchas gracias a todos por vuestros comentarios de ánimo, vuestro apoyo, y vuestra paciencia.</p>
<p><strong>Curso de Programación Android en PDF</strong></p>
<div style="border: 1px solid #DEE256; background: #F9FF5E; padding: 5px; margin-bottom: 15px;">Este curso también está <strong>disponible en PDF</strong>. <a title="Curso Programación Android en PDF" href="http://www.sgoliver.net/blog/?p=1831">Descubre cómo&#8230;</a></div>
<div style="border: 1px solid #DDDDDD; background: #EEEEEE; padding: 5px; margin-bottom: 15px;">¿Te ha sido de utilidad el <strong><a title="Desarrollo en Android" href="http://www.sgoliver.net/blog/?p=1313">Curso de Programación Android</a></strong>? ¿Quieres colaborar de forma económica con el proyecto? Puedes contribuir con cualquier cantidad, unos céntimos, unos euros, cualquier donación será bienvenida.<strong> Si tu donativo es superior a 1.50 euros </strong>recibirás como agradecimiento un documento en PDF con todos los artículos del primer y segundo volumen del curso. Muchas gracias!</p>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="N9K44849SQE9W" />
<input style="background: none; padding: 5px; border-style:none;" type="image" name="submit" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/es_ES/ES/i/btn/btn_donateCC_LG.gif" alt="PayPal. La forma rápida y segura de pagar en Internet." /> <img style="background: none; padding: 5px;" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/es_ES/i/scr/pixel.gif" alt="" width="1" height="1" border="0" /></form>
<p>Más información:</p>
<ul>
<li><a title="Donaciones Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1797">¿Por qué donar?</a><strong> </strong></li>
<li><a href="http://www.sgoliver.net/blog/wp-content/uploads/2011/04/Manual-Programacion-Android-sgoliver.net-v2.0-muestra.zip">Indice de contenidos y capítulo de muestra</a><strong> </strong></li>
<li><a title="Donaciones - Preguntas Frecuentes" href="http://www.sgoliver.net/blog/?p=1811">Preguntas frecuentes</a></li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2313</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
		<item>
		<title>Cuenta atrás para la segunda parte del curso&#8230;</title>
		<link>http://www.sgoliver.net/blog/?p=2214&#038;utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cuenta-atras-para-la-segunda-parte-del-curso</link>
		<comments>http://www.sgoliver.net/blog/?p=2214#comments</comments>
		<pubDate>Mon, 07 Nov 2011 13:35:00 +0000</pubDate>
		<dc:creator>sgoliver</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Noticias]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[curso]]></category>
		<category><![CDATA[documento]]></category>
		<category><![CDATA[manual]]></category>
		<category><![CDATA[pdf]]></category>
		<category><![CDATA[segunda parte]]></category>

		<guid isPermaLink="false">http://www.sgoliver.net/blog/?p=2214</guid>
		<description><![CDATA[Finalmente, y aunque en un principio tenía previsto un artículo adicional sobre geolocalización, doy por finalizada la segunda parte del Curso de Programación Android. El motivo no es otro que el tema que pensaba tratar (geolocalización inversa) no va muy fino en determinadas versiones del SDK y emulador de Android, y no me gustaría incluir [...]]]></description>
			<content:encoded><![CDATA[<p>Finalmente, y aunque en un principio tenía previsto un artículo adicional sobre geolocalización, doy por finalizada la segunda parte del <a title="Curso Programación Android" href="http://www.sgoliver.net/blog/?p=1313"><strong>Curso de Programación Android</strong></a>. El motivo no es otro que el tema que pensaba tratar (geolocalización inversa) no va muy fino en determinadas versiones del SDK y emulador de Android, y no me gustaría incluir en el documento ningún tema que no pueda ser probado por todos fácilmente. De cualquier forma, prometo para más adelante un artículo en el blog que cubra este tema. Son por tanto los artículos que aparecen a día de hoy (11/11/2011) en el <a title="Curso de Programación Android" href="http://www.sgoliver.net/blog/?p=1313">índice del curso</a> los que formarán parte del nuevo documento.</p>
<p>Comienza entonces la cuenta atrás para publicar la segunda parte del curso en formato PDF. Aunque pensaba publicar este segundo volumen como un documento independiente, suplementario al que ya había publicado, finalmente he decidido aunar todo el contenido en un solo documento. Aprovechando esta segunda recopilación he remaquetado todo el contenido para intentar dar al curso una forma mucho más homogénea y parecida a un libro técnico, y así alejarlo un poco de la simple recopilación de artículos que suponía la primera parte publicada. Adicionalmente, he modificado un poco el aspecto visual, he corregido algunas erratas que me habéis ido comunicando, y he sustituido todas las imágenes por su versión a tamaño completo para mejorar su visualización y facilitar la lectura. Espero que todos estos cambios hagan de este documento un recurso de más valor para todos.</p>
<p>El sistema de donaciones para su adquisición se mantiene a través de PayPal, siento no haber podido habilitar finalmente el mecanismo de transferencia bancaria. La donación mínima pasará a ser de <strong>1,50 euros</strong>. Espero entendáis y disculpéis este pequeño incremento, pero el libro tiene muchísimo trabajo detrás y creo sinceramente que os merecerá la pena disponer de esta versión completa y revisada del curso, en vez de contar con dos documentos independientes.</p>
<p>Estoy en pleno proceso de revisión del documento, en unos días publicaré un fragmento de muestra para que podáis comprobar su nuevo aspecto, y en algo menos de 2 semanas estará disponible el documento final.</p>
<p>En definitiva, siento la larga espera para ver publicada esta segunda parte del curso, pero espero que el resultado final compense toda vuestra paciencia.</p>
<p>Saludos.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sgoliver.net/blog/?feed=rss2&#038;p=2214</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

