xml2object
Escrito el 2/06/2004 por Xavi Beumala
Ya que ayer monté el MTcodeBeutifier pues hoy toca poner algo de código y así quitarme el gusanillo ;-)
Carlos Rovira publicaba, creo que fue ayer, en su blog un artículo muy interesante sobre una técnica para parsear archivos xml.
Siguiendo es misma linea hoy pongo unas clases para parsear de una forma semi-recursiva un archivo xml y convertir sus nodos en un objeto: el típico XML2Object. Realmente este tipo de clases son de gran ayuda para proyectos basados en xml
El funcionamiento que he planteado es el siguiente:
- Cualquier nodo se convierte en un Objeto.
- Cualquier objeto tiene la propiedad ‘attributes’ de la que cuelgan como propiedades los distintos atributos del nodo correspondiente
- Si el nodo en cuestión es un nodo de texto, se añade al objeto correspondiente la propiedad ‘data’ cuyo valor será el del nodo de texto.
- Si existe más de un nodo correlativo con el mismo nombre, se genera un Objeto Array que los contiene a todos. Cada una de las posiciones del array se comporta de la misma forma que he descrito.
Creo dicho así suena un poco duro, pero realmente es sencillo, así que mejor pongo un ejemplo:
Imaginemos el siguiente xml:
<?xml version="1.0" encoding="utf-8"?> <fmd> <config> <author>Xavi Beumala</author> <date>02052004</date> </config> <item type="active"> <archive _y="363.95" _x="290.95" instance="Player" file="Player" src=""/> <timeline type="Line" to="1000" from="0"> <initPoint _y="0" _x="0"/> <endPoint _y="-171.95" _x="210"/> <property _scale="0" _rotate="0"/> </timeline> </item> <item type="active"> <archive _y="262.95" _x="549.95" instance="Player" file="Player" src=""/> <timeline type="Line" to="1000" from="0"> <initPoint _y="0" _x="0"/> <endPoint _y="-58.95" _x="-326"/> <property _scale="0" _rotate="0"/> </timeline> </item> </fmd>
Pues el Objeto en el que se convertirá será el siguiente:
[object] |-> config |-> author |-> data = Xavi Beumala |-> date |-> date = 02/05/2004 |-> item |-> attributes |-> type = active |-> item item[0] |-> attributes |-> type = active |-> archive |-> attributes |-> _y |-> _x |-> instance |-> file |-> src |-> timeline |--> attributes |-> type |-> from |-> to |--> initPoint |--> _x |--> _y |--> endPoint |--> _x |--> _y |--> property |--> _scale |--> _rotate item[1] |-> attributes |-> type = active |-> archive |-> attributes |-> _y |-> _x |-> instance |-> file |-> src |-> timeline |--> attributes |-> type |-> from |-> to |--> initPoint |--> _x |--> _y |--> endPoint |--> _x |--> _y |--> property |--> _scale |--> _rotate
Para ahorrarme un poco de tiempo en la relación jerárquica anterior he obviado los valores de las propiedades.
Pues ahora ya solo falta el código. Está compuesto por tres clases:
- SmartCallback: Clase que únicamente gestiona callbacks, utilizada par ala comunicación entre clases.
- ParserXML:Esta clase es la que contiene toda la lógica del parseo y la que se encarga de gestionar la carga del archivo xml.
- XML2Object: Esta clase se encarga de recoger el resultado del parseo así como de darle la información sobre el archivo a almacenar a ParserXML.
Al final de todo he puesto un link para que os descarguéis las clases así como un .fla de ejemplo.
La forma de utilización es la siguiente, en el .fla donde lo queráis utilizar tenéis que añadir el siguiente código:
import com.code4net.XML.XML2Object;
// Instanciamos un objeto
myReader = new XML2Object();
// método invocado cuando ha terminado la
// carga y el parseo. Recibe un parámetro que indica
// si el proceso ha sido correcto o no
myReader.onLoad = function(success) {
if(success) {
trace("parsed ok");
}else{
trace("parsed ko");
}
}
// Cargamos el archivo xml en cuestión
myReader.load("jugada.xml");
El código para la clase ParserXML es:
import com.code4net.system.SmartCallback;
class com.code4net.XML.ParserXML extends XML{
private var _object:Object;
private var _i:Number;
private var sc:SmartCallback;
public function ParserXML() {
init.apply(this, arguments);
}
public function init (obj:Object,_sc:SmartCallback) {
_i = new Number(0);
_object = obj;
sc = _sc;
ignoreWhite = true;
}
public function onLoad (success:Boolean) {
if(success) {
_i = 0;
parse(firstChild,_object);
}else{
trace("error parsing");
sc.run(false);
}
}
private function parse (xml2parse:Object,obj:Object) {
var currentNode:Object = xml2parse;
var iterator:Object;
_i++;
if (currentNode.hasChildNodes()) {
currentNode = currentNode.firstChild;
do {
var ref:Array;
var k:Number;
if (obj[currentNode.nodeName] == undefined) {
obj[currentNode.nodeName] = new Object();
ref = obj[currentNode.nodeName];
ref.attributes = currentNode.attributes;
if(currentNode.firstChild.nodeType == 3) {
ref.data = currentNode.firstChild.nodeValue;
continue;
}
} else {
if (obj[currentNode.nodeName].__proto__ != Array.prototype) {
var tmp:Object = obj[currentNode.nodeName];
obj[currentNode.nodeName] = new Array();
k = obj[currentNode.nodeName].push(tmp);
}else{
k = obj[currentNode.nodeName].push();
}
ref = obj[currentNode.nodeName][k] = new Array();
}
parse(currentNode,ref);
}while(currentNode = currentNode.nextSibling);
}
if(!Boolean(--_i)) sc.run(true);
}
}
El código para la clase XML2Object es:
import com.code4net.XML.ParserXML;
import com.code4net.system.SmartCallback;
class com.code4net.XML.XML2Object {
public var parsedObj:Object;
public var archiveToLoad:String;
public var onLoad:Function;
private var myParser:ParserXML;
public function XML2Object() {
init.apply(this,arguments);
}
private function init() {
parsedObj = new Object();
myParser = new ParserXML();
}
public function load (archive:String) {
archiveToLoad = archive;
var sc:SmartCallback = new SmartCallback(this,_onLoad);
myParser.init(parsedObj,sc);
myParser.load(archiveToLoad);
}
private function _onLoad (success) {
delete myParser;
onLoad.call(this,success);
}
}
Y por último el código SmartCallback:
class com.code4net.system.SmartCallback {
private var scope:Object;
private var method:Function;
private var argArray:Array;
function SmartCallback(sc:Object,m:Function,arg:Array) {
argArray = new Array();
scope = sc;
method = m;
if (arg != undefined) argArray = arg;
}
function run(arg:Object) {
if (arg != undefined) argArray = argArray.concat(arg);
method.apply(scope,argArray);
}
}
En fin, que estoy seguro que lo del MTcodeBeutifier os gusta, pero leer tanto código así es un coñazo. Así que aquí tenéis el archivo con las clases y un .fla de ejemplo.
Para ver el resultado del parseo tendréis que utilizar el Debugger de Flash, en _level0 encontraréis la propiedad myReader, que a su ve contiene el objeto parsedObj que es el resultado del parseo.