PDA

Просмотр полной версии : XML в переменную


olejan1991
07.03.2013, 00:10
Изучаю AS3 по Колину Муку и вот дошел до главы работы с XML раздела про загрузку данных XML из внешнего источника.
Вот переделал код из примера под себя, вроде все работает:
Основной класс VirtualMap.as
package
{
import flash.display.Sprite;

import map.*;

public class VirtualMap extends Sprite
{
private var XMLtypeCell:XMLLoader;

public function VirtualMap()
{
XMLtypeCell = new XMLLoader();
}
}

}

Класс загрузки XML XMLLoader.as
package map
{
import flash.events.*;
import flash.net.*;

// Загрузка XML-данных из внешнего источника
public class XMLLoader
{
// Переменная, в которой будут храниться загруженные XML-данные
private var dataMap:XML;
// Объект, используемый для загрузки XML-данных
private var urlLoader:URLLoader;


public function XMLLoader()
{
// Указываем местоположения внешнего источника XML-данных
var urlRequest:URLRequest = new URLRequest("XML/map0.xml");
// Создаем объект, который умеет загружать внешние текстовые данные
urlLoader = new URLLoader();
// Регистрируем обработчик, чтобы событие об окончании загрузки XML-данных
urlLoader.addEventListener(Event.COMPLETE, completeLoad);
// Загружаем XML-данные
urlLoader.load(urlRequest);

}

public function completeLoad(e:Event):void
{
// Присваиваем данные XML переменной
dataMap = new XML(urlLoader.data);
trace(dataMap);
}


}
}
И вот, собственно, в чем вопрос: переменную мы получили с данными, а вот как ее использовать в другом классе? например получить из нее необходимый атрибут или узел из класса VirtualMap?

Wolsh
07.03.2013, 00:31
История стара, как мир.
Вы сами то, перед тем как писать этот свой XMLLoader, задумывались, чем он будет круче дефолтного URLLoader? Только тем, что будет сам в себе хранить жестко забитую ссылку на файл для скачивания?
Завидное преимущество...

vitamin4er
07.03.2013, 00:34
Ну, переменная должна быть публичной...(если не хотите создавать экземпляр класса - то еще и статической)...и собственно если классы находятся в одном пакете, то просто пишете ИмяКласса.Переменная.

olejan1991
07.03.2013, 00:38
История стара, как мир.
Вы сами то, перед тем как писать этот свой XMLLoader, задумывались, чем он будет круче дефолтного URLLoader? Только тем, что будет сам в себе хранить жестко забитую ссылку на файл для скачивания?
Завидное преимущество...
Тогда я просто не могу понять как взять данные из внешнего XML файла?

Добавлено через 1 минуту
Ну, переменная должна быть публичной...(если не хотите создавать экземпляр класса - то еще и статической)...и собственно если классы находятся в одном пакете, то просто пишете ИмяКласса.Переменная.
Так не получается, она принимает значение NULL

Добавлено через 10 минут
чтоб записать данные XML в переменную нужно только к URLLoader цеплять обработчик?

Wolsh
07.03.2013, 00:52
По вопросу — ну конечно, по событию окончания загрузки Вам надо известить "другой класс" о том, что файл загрузился и можно забирать данные. То есть, снова отправить событие типа Event.COMPLETE (Вы конечно можете сами придумать событие, раз пошла такая пьянка, какое-нибудь XMLLoader.XML_LOAD_COMPLETE) прямо из обработчика Event.COMPLETE ))).
Иначе ведь другому классу не узнать, что файл загрузился и данные доступны?
Отсюда следует еще один момент — этот "другой класс" должен как-то подписаться на событие от XMLLoader. Но вот же незадача — XMLLoader начинает загрузку прямо в своем конструкторе. А подписаться то мы можем только ПОСЛЕ создания экземпляра, то есть, когда конструктор отработает. А значит, файл может загрузиться (например, из кэша), и событие будет отправлено — еще ДО того, как мы на него подпишемся. Оказывается, вот почему у класса URLLoader усть метод load(), а не просто передача URLRequest прямо в конструктор!
И вот так, следуя обычной логике, Вы получите абсолютно тот же самый URLLoader, с абсолютно тем же "интерфейсом" и примерно тем же синтаксисом и порядком работы. Не думайте, что в Adobe сидят злые дядьки и думают только о том, как усложнить Вам жизнь.
Что можно сделать по-другому?
Можно использовать коллбек. То есть, создавая экземпляр XMLLoader, отдавать в конструктор некую функцию, которая должна выполниться, когда файл загрузится. В конструкторе сохранить ссылку на эту функцию в приватной переменной, а в обработчике завершения загрузки вызвать ее, возможно даже передав параметром загруженные данные. Но, честное слово, не надо огород городить. Зачем Вам это? Целый класс, дублирующий URLLoader, только для хранения одной строковой переменной — адреса файла.

olejan1991
07.03.2013, 00:57
Давайте забудем про мой нелепый класс. Этот огород был для попытки загрузить данные из внешнего файла XML в переменную для последующей работы с этими данными. Подскажите как правильно загрузить данные из внешнего файла в переменную, т. к. во многих обучающих материалах, которые я перерыл приведены примеры с созданием класса типа моего?

Wolsh
07.03.2013, 01:03
Подскажите как правильно загрузить данные из внешнего файла в переменнуюА в чем проблема? Ваш trace(dataMap); разве не выводит полученные данные? Выходит, Вы их загрузили и присвоили переменной.
Сформулируйте вопрос. Тем более, что я привел целых ТРИ ответа на него.

olejan1991
07.03.2013, 01:10
Мне нужно, чтобы содержимое переменной dataMap с данными Xml я мог получить в любом другом классе, а мой dataMap доступен только в функции completeLoad, которая вызывается после удачной загрузки файла.

Wolsh
07.03.2013, 01:11
1. Дописать Вашу обертку URLLoader'a. Вынести делегирование метода load() из конструктора, в обработчике загрузки диспатчить событие от имени уже XMLLoader'a. Данные сохранить в публичной переменной (до того, как диспатчить событие). Получите второй URLLoader, только и всего.
2. Не писать никакой XMLLoader, а просто загрузить данные URLLoader'ом там, где они нужны. Использовать так же, как сейчас трейсите — в обработчике загрузки.
3. Использовать в XMLLoader коллбек-функцию от "заказчика" загрузки.

in4core
07.03.2013, 01:15
package com.in4core.edenSite.utils
{
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
/**
* ...
* @author in4core progression lab
*/
public class XMLLoader extends EventDispatcher
{
private var _urlLoader:URLLoader = new URLLoader();
private var _data:XML;

public function XMLLoader()
{

}

public function load(url:String):void
{
this._urlLoader.addEventListener(Event.COMPLETE , onComplete);
this._urlLoader.load(new URLRequest(url));
}

private function onComplete(e:Event):void
{
this._data = new XML(this._urlLoader.data);
this.dispatchEvent(new Event(Event.COMPLETE));
}

public function getContent():XML
{
return this._data;
}
}

}
Самый наипростейший лоадер. чего сложного то?

olejan1991
07.03.2013, 01:17
Вот я хочу использовать предложенный 2й вариант, но я не пойму как использовать URLLoader, читаю мануал адоба и не могу понять...

in4core
07.03.2013, 01:18
Я вам дал пример. Думаю разберетесь

olejan1991
07.03.2013, 01:21
package com.in4core.edenSite.utils
{
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
/**
* ...
* @author in4core progression lab
*/
public class XMLLoader extends EventDispatcher
{
private var _urlLoader:URLLoader = new URLLoader();
private var _data:XML;

public function XMLLoader()
{

}

public function load(url:String):void
{
this._urlLoader.addEventListener(Event.COMPLETE , onComplete);
this._urlLoader.load(new URLRequest(url));
}

private function onComplete(e:Event):void
{
this._data = new XML(this._urlLoader.data);
// Зачем этот момент?
this.dispatchEvent(new Event(Event.COMPLETE));
}

public function getContent():XML
{
return this._data;
}
}

}

Wolsh
07.03.2013, 01:23
Да. Вот in4core привел код первого варианта — обертки.
Как видите, используя такой класс, Вы будете писать в точности все то же самое, что написано в нем)))
private var _xmlLoader:XMLLoader = new XMLLoader();
private var _data:XML;
this._xmlLoader.addEventListener(Event.COMPLETE , onComplete);
this._xmlLoader.load("file.xml");

private function onComplete(e:Event):void
{
this._data = this._xmlLoader.data;
// какой-то код работы с данными дальше;
}Лично я не понимаю, зачем? Если точно так же можно использовать готовый URLLoader...

olejan1991
07.03.2013, 01:29
Ув Wolsh, теперь я окончательно запутался(
Не могли бы Вы, если это Вас не затруднит написать как нужно делать?
Ситуация: есть класс
package
{
import flash.display.Sprite;

import map.*;

public class VirtualMap extends Sprite
{

public function VirtualMap()
{

}
}


как тут загрузить данные в переменную и пользоваться ими?

Wolsh
07.03.2013, 01:35
// Зачем этот момент?
Ясно. Вы считаете, что как только Вы написали load(), файл уже загружен и можно брать из него данные.
Я не думаю, что даже через 50 лет загрузка файлов из сети будет происходить быстрее наносекунды.

Вы не можете обратиться к файлу сразу же после команды load();
Вы должны дождаться, пока файл загрузится.
Это требует времени.
В этом смысл Событий. Не известно, когда они произойдут, через какое время. Программа должна ждать, пока не случится Событие, раз данные будут доступны только ПОСЛЕ этого события. Для этого пишется обработчик, который и будет вызван только тогда, когда произойдет его событие.
Но код, который Вы написали после load(), выполнится не тогда, когда произойдет событие. Он выполнится СРАЗУ после load(); А сразу после load(); нет никакого файла, никаких данных. Они в пути. А у Вас null.

Добавлено через 4 минуты
как тут загрузить данные в переменную и пользоваться ими?Да просто перепишите сюда все из своего XMLLoader. И в обработчике начинайте использовать данные.

olejan1991
07.03.2013, 02:14
и вынести работу с XML файлом в отдельный класс, как я понял, не получится и не нужно?

in4core
07.03.2013, 02:26
и вынести работу с XML файлом в отдельный класс, как я понял, не получится и не нужно
Нужно или нет - решать вам. А вынести работу с XML - дело не хитрое. Выносите. Например

package com.in4core.edenSite.models
{
import flash.events.EventDispatcher;

/**
* ...
* @author in4core lab
*/
public final class GlobalModel extends EventDispatcher
{
private var _menu:XMLList = null;
private var _trackListXML:XML = null;
private var _preferencesList:XML = null;
private var _sections:XML = null;
private var _playerConfig:XML = null;
private var _copyText:String = null;

public function GlobalModel()
{
super();
}

public function get trackListXML():XML
{
return _trackListXML;
}

public function get menu():XMLList
{
return _menu;
}

public function get preferencesList():XML
{
return _preferencesList;
}

public function get sections():XML
{
return _sections;
}

public function get playerConfig():XML
{
return _playerConfig;
}

public function get copyText():String
{
return _copyText;
}


/////////////////////////////////////////////////////////////////
//4
//4 LOGIC
//4
/////////////////////////////////////////////////////////////////

public function parseGlobalData(data:String):void
{
const gl:XML = new XML(data);

_trackListXML = new XML ( gl..songs );
_playerConfig = new XML ( gl..soundPlayer);
_preferencesList = new XML ( gl..preferences );
_sections = new XML ( gl..sections );
_menu = new XMLList ( gl..headers );

_copyText = gl..copyText;

}
}

}

Wolsh
07.03.2013, 08:29
in4core, я думаю топикстартер подразумевал класс, который загрузит xml и любой желающий сможет в любой момент запросить у него данные. Как бы ничего неординарного, за исключением одного: данные можно запросить только ПОСЛЕ окончания загрузки, а не сразу после создания экземпляра этого класса, а этот момент как раз топикстартеру и непонятен.

olejan1991, если для создания каких-то объектов "дальше" по коду Вам нужны данные из xml-файла, то сначала Вы должны его загрузить. Мало создать загрузчик и сказать "грузи", необходимо дождаться окончания загрузки.
Это значит, что создавать эти другие объекты дальше Вам можно только после загрузки. "После" означает "в обработчике события окончания загрузки". Только там у Вас есть необходимые данные. И по-другому никак, если xml загружается извне.
Это одна сторона вопроса. Другая — как передавать данные из одного объекта в другой. Этот вопрос не связан ни с XML ни с загрузкой данных извне. Вам стоило разобраться с ним без загрузки извне, чтобы не перегревать мозг. Но этот вопрос слишком обширен и слишком часто здесь обсуждался, чтобы в сотый раз писать то же самое. Этих способов огромное количество, и нет никакого "единственного верного". Все зависит от вашего проекта, его архитектуры и задачи.

olejan1991
07.03.2013, 11:32
Ну без загрузки извне понятно, в функции пишем return нужныйОбъект и все, работаем с ним через вызов этой функции и все, а вот с файлом извне как раз и не понятно как вернуть нужное значение из-за наличия, как раз, этих событий и Event в качестве параметра функции, которая запускается по окончании загрузки, ведь у нее есть этот обязательный параметр и обратаиться к ней за объектом без атрибута я не могу, но ведь объект сидит в ней.

Добавлено через 6 часов 27 минут
Я все понял, спасибо за помощь

olejan1991
16.03.2013, 14:17
package
{
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.text.TextField;

import map.*;

public class VirtualMap2 extends Sprite
{
// Переменная, в которой будут храниться загруженные XML-данные
private var dataMap:XML;
// Объект, используемый для загрузки XML-данных
private var urlLoader:URLLoader;

public function VirtualMap2()
{
// Указываем местоположения внешнего источника XML-данных
var urlRequest:URLRequest = new URLRequest("XML/map0.xml");
// Создаем объект, который умеет загружать внешние текстовые данные
urlLoader = new URLLoader();
// Регистрируем обработчик, чтобы событие об окончании загрузки XML-данных
urlLoader.addEventListener(Event.COMPLETE, completeLoad);
// Загружаем XML-данные
urlLoader.load(urlRequest);



}

public function completeLoad(e:Event):void
{
// Присваиваем данные XML переменной
dataMap = new XML(urlLoader.data);

// Показываем сетку
var cell:VirtualMapView = new VirtualMapView();
cell.displayCell(dataMap);
/////////////////////////////////////////////////////////////////////////////////////////////////
//Здесь мы присваиваем переменной данные из файла XML
cell.setXML(dataMap);
//Здесь мы должны получить данные присвоенные переменной, но мы получаем null
cell.getXML();
/////////////////////////////////////////////////////////////////////////////////////////////////
addChild(cell);

}

}
}

package map
{
import flash.display.*;

public class VirtualMapView extends Sprite
{

private var dd:XML;

public function VirtualMapView()
{

}

public function setXML(newDD:XML):void
{
// присваиваем переменной те самые данные из файла
var dd:XML = newDD;
// тут они есть
trace(dd);
}

public function getXML():XML
{
//это get метод должен использоваться далее в классе, а именно данные,которые он возвращает
return dd;
}
}