Comunidad de diseño web y desarrollo en internet online

Modificar XML con ActionScript 3

Citar            
MensajeEscrito el 04 Ago 2010 01:22 pm
Hola a todos. Hace poco abrí un tema sobre cómo cargar datos XML, que me ayudasteis a resolver tan amablemente. Pues bien, siguiendo con la aplicación, ahora necesito escribir datos en ese mismo documento XML, ya que se trata de los récords de un videojuego. He aquí el documento:

Código XML :

<HighScores>
   <Score id = '1'>
      <nombre>juan</nombre>
      <puntos>50</puntos>
   </Score>

   <Score id = '2'>
      <nombre>paco</nombre>
      <puntos>45</puntos>
   </Score>

   <Score id = '3'>
      <nombre>pepe</nombre>
      <puntos>30</puntos>
   </Score>

   <Score id = '4'>
      <nombre>luis</nombre>
      <puntos>20</puntos>
   </Score>

   <Score id = '5'>
      <nombre>samuel</nombre>
      <puntos>0</puntos>
   </Score>
</HighScores>



¿Hay alguna manera de borrar nodos? Todo lo que he encontrado es que no se pueden modificar archivos XML, pero me parece raro que existan instrucciones para añadir nodos si esto es así (insertChildBefore e insertChildAfter, por ejemplo).

Una vez modificada la variable XML, ¿cómo podría hacer que los cambios se hicieran efectivos en el archivo? He oido acerca de php, etc, pero no tengo ni idea de cómo va.

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 04 Ago 2010 02:52 pm
BuenAS:

Si te digo la verdad nunca he eliminado un nodo, imagino que podrá hacerse.

Por otro lado, puedes guardar de forma local un archivo con FileReference.save(); pero tiene un serio inconveniente. Por razones de seguridad es OBLIGATORIA la intervención del usuario; o sea, debe hacer click en un boton (por ejemplo, un botón salvar) o pulsar una tecla (CTRL + S, por ejemplo), y siempre aparecerá la ventana de Windows solicitando el sitio donde se quiere guardar. Esto NO se puede hacer de forma transparente para el usuario; SIEMPRE tiene que intervenir. Cosa perfectamente lógica, por que de ser posible guardar archivos en el equipo del usuario sin su participación, sería una puerta abierta a posibles virus.

Otra cosa es que estés haciendo una aplicación AIR, en este caso en lugar de FileReference puedes usar File (SÓLO es válido cuando publicas para AIR) que sí permite sin la intervención del usuario leer, guardar, borrar, crear carpetas... cualquier proceso del sistema de archivos.

Y efectivamente si lo que quieres es guardar el xml en un servidor, lo más adecuado es recurrir a php o a otro script de servidor para esta tarea.

Un saludo.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 04 Ago 2010 08:39 pm
Creo que me conviene el FileReference, aunque .save en concreto no lo he visto en el manual, pero ya te comentaré.

En cuanto a modificar los documentos con AS antes de almacenarlos etc., me podrías dar alguna directriz de cómo eliminar nodos?

Muchas gracias!!

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 04 Ago 2010 09:04 pm

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 04 Ago 2010 09:18 pm
...ah! y borrar nodos, directamente con "delete". Si tienes un xml:

Código XML :

<root>
   <item>
      <subitem>Item 10</subitem>
      <subitem>Item 11</subitem>
   </item>
   <item>
      <subitem>Item 20</subitem>
      <subitem>Item 21</subitem>
   </item>
   <item>
      <subitem>Item 3</subitem>
      <subitem>Item 31</subitem>
      <subitem>Item 32</subitem>
   </item>
</root>


y escribes:

Código :

_xml.item[2].subitem[1];


borrarías el ítem 32

Un saludo.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 04 Ago 2010 09:21 pm
...perdón pero no ha salido bien el código al publicarlo, le faltaba lo fundamental, el comando "delete":

Código ActionScript :

delete xml.item[2].subitem[1];

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 04 Ago 2010 09:39 pm
Realmente, subirlos puedo subirlos normalmente con:

Código ActionScript :

cargador.load(new URLRequest(url));

¿Es compatible subirlo de esa manera con guardarlo con Filereference?

Por otra parte, tener que modificar y guardar de nuevo los archivos XML ha trastocado del todo mis planes xD

Código ActionScript :

public function cargarXML(url:String){
         cargador.addEventListener(Event.COMPLETE, cargaCompleta);
         //loadUrl.addEventListener (SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
         //loadUrl.addEventListener (IOErrorEvent.IO_ERROR, ioErrorHandler)
         cargador.load(new URLRequest(url));
      }
 
      private function cargaCompleta(e:Event):void {      //Funcion que se ejecuta cuando termina la carga.
         cargado = true;              //****************************************
         datos = new XML(e.target.data);
         datos.ignoreWhite = true; 
      }


Antes podía hacerlo todo en la función cargaCompleta. Accedía a los nodos, cogía la info, y listo. Pero ahora, he roto la función cargaCompleta en cargaCompleta (ésa de arriba), leerXML, y escribirXML, en función de lo que deba hacer. En todos los casos ejecuto cargarXML y por tanto cargaCompleta, y luego ya aplico leer o escribir según lo necesite.

Código ActionScript :

public function leerXML(){
              while (cargado != true);        //****************************************************
         var i:int = 0;
         var miRaiz = datos.child("Score");
         if (miRaiz != null){
            for each(var nodo:XML in miRaiz){
               i++;
               miRoot.textoRecords.text += "#" + i +": " + nodo.nombre + "\t\t" + nodo.puntos + "\n";
            }
         }
      }
      
      public function escribirXML(puntuacion:String){
                     while (cargado != true);        //****************************************************
         var temp:String;
         var miRaiz = datos.child("Score");
         var j:int = 0;
         puntuacion = "<Score/>";
         
         if (miRaiz != null){
            for each (var nodo:XML in miRaiz){
               j++;
               //Devuelve el atributo puntuacion
               temp = nodo.puntos;
               if (puntuacion > temp){
                  nodo.insertChildBefore(miRaiz.Score[j], puntuacion);
                  nodo.removeNode();       //************************************
                  //remove(miRaiz.Score[miRaiz.length]); //**************************************
                  break;
               }
            }
         }
      }


En las cinco líneas de asteriscos está el problema. Ahora que lo tengo separado en 3 funciones, tanto leerXML como escribirXML han de esperar a que se cargue el documento. He descubierto que ésa no es una buena forma (se queda en un bucle infinito). Las otras dos líneas de asteriscos son porque no sé borrar nodos xD.

Una vez resuelto eso, como puedo usar el Filereference? He de cambiar también la carga?

Perdón por el tocho U_U

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 04 Ago 2010 09:42 pm
Leídos los dos últimos. Un trillón de gracias

Juro y perjuro que no he visto nada parecido en los manuales consultados. Qué simple! :D

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 05 Ago 2010 06:08 am
BuenAS:

Veo que estás utilizando cosas de la clase XML, y del paquete flash.xml (XMLNode) Yo particularmente nunca los he mezclado y suelo trabajar sólo con la clase XML. Pero tampoco puedo decir qué problemas puede presentar. Siempre que no te emita mensajes de error de tipificación imagino que todo irá bien.

Por otro lado, FileReference es una clase que permite abrir y guardar archivos, siempre que publiques para Flash Player 10, de forma local; o sea, EN EL ORDENADOR DEL USUARIO y SIEMPRE con la participación del usuario (siempre tiene que elegir el/ella la ruta del archivo a guardar) Y creo que por las características de la aplicación (un juego) no va a ser lo que andas buscando.

Para guardar el xml en un servidor se suele, como comentabamos, utilizar php y hay muchos ejemplos en internet :) Cuando estás desarrollando la aplicación no es plan de estar subiendo al servidor donde estará tu juego los archivos cada vez que publicas. Para esto es útil tener instalado en tu ordenador un servidor web, como Apache. En este enlace te puedes bajar un instalador que te ayuda a instalar Apache, php y MySql (esto último si deseas instalar esta base de datos, no es obligatorio):

XAMPP

Otro apunte, más un gusto personal que otra cosa. Yo optaría cuando cargas el xml de tradudir los datos a un modelo un poco más amigable a la hora de modificarlos. Por ejemplo, meter las puntuaciones, nombres, etc. en un Array (o vector), trabajar con dicho array, y cuando quiera mandarlos a guardar al servidor, los convierto a xml y mando dicho xml.

Un saludo.

Un saludo.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 05 Ago 2010 09:09 am
Me temo que la aplicación debe ser compatible con Flash Player 9... y de todas formas, como bien comentas no es plan de hacerle escoger la ruta cada vez que bata un Récord.

Qué bajón... xD

¿dónde dices que uso el paquete Flash.xml? Juraría que lo había quitado.

Hmmm bah, creo que renuncio a tener HighScores...

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 05 Ago 2010 09:43 am
en:

Código ActionScript :

nodo.removeNode();


nodo debería ser un XMLNode (del paquete flash.xml);

Pero como decía, si no te está dando errores de tipificación, imagino que no habrá mayor problema. Suelo trabajar con xml's sólo con la clase XML, y no tengo mucha práctica en esta parte :)

Por otro lado, si es Flash Player 9, imposible utilizar FileReference.save. La solución, como también comentabamos más arriba, es tirar de php.

Un saludo.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 05 Ago 2010 10:00 am
Como el agua. Muchas gracias!

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 06 Ago 2010 10:17 am
Una preguntilla más... cómo harías para poder hacer diferentes acciones sobre el XML en función de lo que quieras cargar? Siendo un archivo de HighScores, pues se supone que siempre vas a cargar todos los records, pero si el archivo es de cualquier otra cosa, seguramente tendrás que usar diferentes partes en diferentes momentos.

Mi solución, poner cargado = true en cargaCompleta y luego esperar a que sea true en las diferentes funciones no funciona... se pasa de 15 segundos esperando y da error.

Código ActionScript :

public function cargarXML(url:String){
         cargador.addEventListener(Event.COMPLETE, cargaCompleta);
         cargador.load(new URLRequest(url));
      }

private function cargaCompleta(e:Event):void {      //Funcion que se ejecuta cuando termina la carga.
         cargado = true;              //****************************************
         datos = new XML(e.target.data);
         datos.ignoreWhite = true; 
      }

public function leerXML(){
              while (cargado != true);        //****************************************************
        //etc
      }

public function escribirXML(puntuacion:String){
                     while (cargado != true);        //****************************************************
   //etc
      }

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 06 Ago 2010 11:33 am
BuenAS:

No me ha quedado claro lo que quieres hacer. Si te refieres a poder cargar varios xml's con una misma función cargarXML, hay que tener en cuenta que se trata de procesos asíncronos; o sea, que si llamas a la función varias veces

Código ActionScript :

   cargarXML("xml/record_1.xml");
   cargarXML("xml/record_2.xml");
   cargarXML("xml/record_3.xml");

esto NO te garantiza que se vayan a cargar en la secuencia record_1.xml, record_2.xml,r ecord_3.xml. Por ejemplo, si record_2.xml es mucho más grande que los otros xml's posiblemente será el último en cargarse. Y si los vas metiendo en un array conforme van llegando, no te garantiza que los tengas metidos en un orden dado. Para solucionar esto tienes varias opciones:

1. Te creas un Dictionary -es igual que un Array, salvo que en vez de índices numéricos, la referencia son objetos, en este caso los URLLoader que estamos utilizando-, y vas introduciendo los loaders que vas creando, y un índice: algo así

Código ActionScript :

public function cargarXML(url:String, _index:int)
{
   var loader:URLLoader = new URLLoader();
   dictionary[loader] = _index;
   loader.load(new URLRequest(_url));
   loader.dataFormat = URLLoaderDataFormat.TEXT;
   loader.addEventListener(Event.COMPLETE, cargaCompleta, false, 0, true);
}
private function cargaCompleta(e:Event):void
{
   arrayXml[dictionary[event.target]] = new XML(e.target.data);
}


Ahora en arrayXml tendrás los xml's ordenados con el orden que le diste al llamarlos:
cargarXML("record_1.xml", 0);
cargarXML("record_1.xml", 2);
cargarXML("record_1.xml", 3);

2. Otra opción es que te crees una clase, por ejemplo MiLoader, que extienda de URLLoader y añadas la propiedad "index" (o "id", o "name"). Cuando crees este loader le asignas el orden, por ejemplo a index, y después podrás recuperarlo en cargaCompleta para introducir el xml en la matriz. En este caso como en el anterior sería conveniente crear un contador, e ir incrementandolo conforme se van cargando los xml's, y al final emitir un evento, o llamar a una función para empezar a trabajar con los xml's.

3. Otra opción. Utilizar una librería como Bulkloader Es lo que hago habitualmente :) Te permite cargar xml's (o imágenes, o sonidos...) dándoles un nombre, emite un evento complete cuando ha acabado la carga, y puedes recuperarlos usando el nombre que diste. Básicamente, en plan cutre es lo que he intentado contar en los ptos. 1 y 2 :)

Un saludo.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 06 Ago 2010 12:35 pm
Hmmm no es exáctamente lo que quería, aunque es la mar de útil, ciertamente.

A lo que yo me refiero es a poder cargar el XML por una parte, y luego en función de lo que te convenga llamar a otra función para cargar unos datos, otros, modificarlo, lo que sea.

De esta manera, even.COMPLETE llama a cargaCompleta, que sólo pone a true la variable cargado, y las otras funciones se ejecutan cuando dicha variable esté a true. Pero esa forma no parece la correcta, ya que no funciona.

No sé si se me entiende

Así puedo tener:

Código ActionScript :

cargarXML(url);
leerXML(nombres);


y en otra parte del código:

Código ActionScript :

cargarXML(url);
leerXML(puntos);
escribirXML(loquesea);

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 06 Ago 2010 12:59 pm
BuenAS:

Quieres que llamando al método cargarXML, leerXML, haga una cosa u otra en función de lo que le pases .Creo que lo que quieres hacer es algo que, por ahora AS3 no soporta: la sobrecarga de métodos. Pero se puede hacer "manualmente", por ejemplo, pasando una variable de control al método, y con un switch hacer una cosa u otra. Y creo entender que quieres que sea el xml, el que discrimine.

para esto puedes crearte una etiqueta en tu xml, por ejemplo xml.config.tipoAccion, y ahí puedes introducir el valor que después capturarás cuando cargues el xml:

Código ActionScript :

//se supone que ya has cargado el xml y en el método controlador puedes poner:
hacerAlgoConXML(xml, xml.config.tipoAccion);

//y la función a la que llamamos
private function hacerAlgoConXML(xml:XML, condicion:String):void
{
   switch (condicion) {
      case "OpcionA":
         //hacer algo si has escrito OpcionA en el xml
         break;
    case "OpcionB":
         //hacer algo si has escrito OpcionB en el xml
         break;
}


Pero todo esto es a lo que se llama la lógica de tu aplicación. Una vez cargados los datos, en función de lo que quieras conseguir con tu aplicación, los procesas de una forma u otra. Yo qué se: llenas una tabla de records, cuentas un chiste aleatorio de una lista cargada :) , miras a ver si el usuario ya jugó y guardó su nombre en una partida anterior, y le saludas...

Un saludo.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 06 Ago 2010 01:26 pm
Más fácil que eso. Lo que quiero es que al llamar a leerXML o escribirXML o cualquier otra cosa, tenga garantizado que el documento está cargado. Por eso pongo en cargaCompleta cargado a true.

Otra cosa que se me había ocurrido, era escoger en la función cargaCompleta, en función de un parámetro, pero no sé como meterle parámetros en event.COMPLETE, carga completa.

La lógica me dice que While cargado != true; en las funciones que desee realizar debería funcionar, pero no es así...

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 06 Ago 2010 01:36 pm
...pero si eso lo tienes garantizado: a partir de cargaCompleta (creo que así llamas al controlador del evento Event.COMPLETE de tu loader) YA has cargado el xml y puedes trabajar con él. Toda función que llames desde aquí, o código que ejecutes desde aquí te garantiza que el xml está cargado y existe.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 06 Ago 2010 01:44 pm
Exacto. Si lo llamas desde carga completa sí.

Pero si hago:

Código ActionScript :

CargarXML(url); //que a su vez llama a cargaCompleta
LeerXML();


Nadie me garantiza que al hacer leerXML, ese documento esté cargado. Me equivoco?

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 06 Ago 2010 02:18 pm
BuenAS:

Claro, es que para eso está el evento Event.COMPLETE :) Todos los procesos de carga, desde un xml, a un swf, son asíncronos; o sea, se sabe cuándo empiezan, pero no cuándo acaban. Por eso hay que esperarse a dicho evento y, a partir de aquí, llamar a un método, distribuir un evento (que capturaremos en otra parte de nuestra aplicación), ect. para trabajar con lo que se ha cargado.

Y también lo que comentaba más arriba, si estás cargando varias cosas a la vez, y "supuestamente" le has dado un orden de carga:

cargarXML("uno.xml");
cargarXML("dos.xml");
cargarXML("tres.xml");

esto NO GARANTIZA que estos xml's se carguen en orden uno.xml, dos.xml, tres.xml

Que sea un proceso asíncrono es algo lógico ya que el archivo se va a descargar de un servidor web (local cuando estés desarrollando) y a un usuario bajar un xml de 200k le puede tardar 0.1 segundo, y a otro 1 segundo; y no es lo mismo descargar un archivo de 200k que de 2 megas.

Un saludo.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 06 Ago 2010 09:20 pm
Tienes razón. Eso es muy cierto, y lo comprendo. Por eso te quiero preguntar: Hay alguna forma de "esperar" hasta que acabe? Lo cierto es que, cuando sólo quería cargar los datos, no tardaba nada. Pero ahora que tengo leerXML y escribirXML, con un while cargado!= true; se agota el tiempo de espera y no hay manera.

Lo normal es meter la lógica en la función llamada por Event.COMPLETE, pero cuando hay varias posibilidades... ahí está el lío.

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 07 Ago 2010 08:35 am
BuenAS:

Creo que sería una buena opción recurrir a lo que comenté antes: Bulkloader (o similar, hay varios; ahora TweenMax trae un paquete para hacer precargas). Y si lo quieres hacer a mano vuelvo a comentar que hay cosas a tener en cuenta:

1. Llamar a una función cargarXML("xml/mi_archivo_xml.xml") repetidas veces NO GARANTIZA que en tu función controladora del evento Event.COMPLETE lleguen los archivos en el orden con que has llamado a cargarXML. Esto dependerá del tamaño de los archivos, de cómo está de solicitado el servidor donde lo descargas, etc.

2. Hay varias opiciones, una de ellas es pasar un flag (un indicador, por ejemplo, un índice -0, 1, 2,...) y después recuperarlo en la función controladora. Así sabemos QUÉ XML HA LLEGADO en cada momento y guardarlo, por ejemplo, ordenado en una matriz. Este rollo lo comenté más arriba, por lo que no lo repito :)

3. Depende de la lógica de tu aplicación. No es algo genérico. Puedes necesitar sólo un xml, o dos. Cargar a la vez el xml con imágenes; cargar primero el xml, procesarlo y entonces cargar las imágenes... Lo que digo, esto depende de la aplicación concreta que estés desarrollando y parece bastante complicado querer hacer una función que "se lo trague todo", no sé si me explico.

Y finalmente, todos estos problemas ya los ha tenido algún programador anteriormente, y por eso ha creado Bulkloader o LoaderMax

Un saludo.

Por Lukánicos

Claber

468 de clabLevel



Genero:Masculino  

chrome
Citar            
MensajeEscrito el 07 Ago 2010 12:56 pm
Muchas gracias! Y perdón por la pesadez xD

Por Cher0

18 de clabLevel



 

chrome
Citar            
MensajeEscrito el 09 Ago 2010 06:21 am
Hola que tal, espero que todos en el foro esten bien. Quisiera que alguien me ayudara a pasar un codigo que esta en AS2 , pasarlo a AS3. No tengo mucha experiencia en esto y por eso recurro a los expertos, espero alguien me ayude. El codigo es el siguiente.

var theXMLpp:XML = new XML();
theXMLpp.ignoreWhite = true;

theXMLpp.onLoad = function(){

var nodes = this.firstChild.childNodes;
var i=0;
var cont = 0;
galeria = new Array();
foto = new Array();

for(i=0;i<nodes.length;i++){
if(nodes[i].nodeName == "galeria")
{
galeria[cont] = nodes[i].firstChild.nodeValue ;
}
if(nodes[i].nodeName == "foto")
{
foto[cont] = nodes[i].firstChild.nodeValue ;
cont++;
}
}

foto1.text = galeria[0];
foto2.text = foto[0];
foto3.text = foto[1];
foto4.text = foto[0];
ciudadmin.text = galeria[1];
textmin.text = foto[1];


}

theXMLpp.load("http://www.galerias.com/fotos/xml/xmlgaleria.php");

---------------------------
mas que nada es como hacer el cambio para cargar el xml, espero alguien pueda ayudarme.
de antemano gracias y saludos para todos.
Responder Citando

Por depredadoor

1 de clabLevel



 

firefox

 

Cristalab BabyBlue v4 + V4 © 2011 Cristalab
Powered by ClabEngines v4, HTML5, love and ponies.