Создание средства просмотра XML с помощью JScript – создание мощных скриптов в Exsead XML

Метод чтения источника данных XML из файла или AJAX и последующего представления его в Internet Explorer в виде GUI (графический интерфейс пользователя).

•    Скачать исходники - 19.01 KB

Введение

В данной статье на основе Exsead создается парсер JScript DOM и GUI просмотра XML.

Краткая информация о Exsead Etc.

Основные принципы устройства Exsead изложены здесь. Чтобы данная статья принесла максимум пользы, следует прочитать статью об использовании Internet Explorer в качестве GUI скриптов, находящуюся тут. Код здесь также использует систему для чтения двоичных файлов JScript, подробно описанную тут. Дополнительные сведения об используемом в статье AJAX здесь.

Внутри примера кода есть XMLViewer.js. Данный скрипт использует принцип модель-представление контроллера для построения простого, но эффективного средства просмотра данных XML. Также добавлен еще более простой пример MVC (из предыдущей статьи по использованию Internet Explorer в качестве GUI) под названием MVCExample.js. Задача любого скрипта, так разбирающего XML, - извлекать необработанные данные, хранящиеся в XML, и превращать их в применимые данные. Сами по себе данные XML полностью статичны и бесполезны; они должны быть разобраны в программу, прежде чем заработают. Именно это и делает XMLViewer.js.

XMLViewer.js – очень длинный скрипт, так как он выполняет несколько разных функций, чтобы достичь своей цели.

Однако общая структура очень простая; его части представляют собой следующее:
•    ProcessArguments: Эта функция обрабатывает аргументы, переданные скрипту, а при их отсутствии выбирает действие по умолчанию.
•    DisplayXML: Отправляет наглядное представление XML в графический интерфейс пользователя Internet Exporter.
•    XMLClean: Простая утилита, экранирующий знаки &tl; в тексте.
•    ProcessNode: XML хранится в виде дерева узлов, эту функцию использует DisplayXML для прогона всего дерева, создания HTML-представления структуры XML по ходу.
•    GUIWindow: Это объектная функция - т.е. функция, создающая объект. Создаваемый ею объект является оберткой вокруг экземпляра Internet Explorer. Метод DisplayXML использует класс для отправки наглядного представления (HTML) XML в Internet Explorer.
•    BinaryFile: Объектная функция, создающая объекты BinaryFile, позволяющие JScript читать и записывать файлы в двоичном, а не текстовом виде. Применяется для считывания файлов XML.
•    ConcatStringArray: Очень удобная утилита, получающая массив строк и возвращающая строку, созданную путем их объединения. Это намного быстрее, чем соединять множество строк, когда строки становятся длинными.

Process Arguments

Когда скрипту передаются аргументы, что происходит при перетаскивании файла XML на иконку скрипта, данная функция читает файл как двоичный файл и затем передает его содержимое в DisplayXML. Если аргументы отсутствуют, то Process Arguments использует AJAX для извлечения потока данных XML из канала ATOM Центр умников и передает возвращенный XML в DisplayXML.

Часть Чтение аргументов/файла:
=====================================
        for(var i=0;i<WScript.arguments.count();++i)
        {
            var chunks;
            var fn=WScript.arguments.item(i);
            var bf1=new BinaryFile(fn);
            var xml=bf1.ReadAll();
            DisplayXML(xml);
        }

Часть AJAX:
======================
        var ajax=WScript.CreateObject('Microsoft.XMLHTTP');

        // Пытаемся получить файл 32 раза, затем прекращаем попытки
        for(var i=0;i<32;++i)
        {
            try
            {
                // Заносим сюда ссылку на веб-сервис
                // Последний аргумент со значением «ложь» заставляет скрипт
                // ждать ответа
                ajax.open
                   ('GET','http://nerds-central.blogspot.com/feeds/posts/default',false);

                // Устанавливаем заголовок запроса, чтобы соединение разрывалось сразу,
                // чтобы предотвратить блокировку ресурсов сервера
                ajax.setRequestHeader
                (
                    'Connection',
                    'close'
                );

                // Отправляем запрос и ждем
                ajax.send();

                // Сервер вернул ошибку
                if(!ajax.status==200) throw('Got Wrong Status:'+ajax.status);
                break;
            }
            catch(e)
            {
                for(var a in e)
                {
                    WScript.Echo(a + '=' + e[a]);
                }
                // Заметьте, что использование () вокруг математических выражений заставляет
                // численные расчеты выполняться, а оператор + превращает результат в строку
                WScript.echo('Не удалось получить канал atom из Nerds-Central:
                            осталось попыток='+(32-i));
            }
        }

        // Если цикл не завершается в результате отсчета
        // отобразить XML
        if(i!=32)DisplayXML(ajax.responseText);

DisplayXML

Получая кусок XML в виде строки, данная функция разбирает его с помощью парсера DOM от Microsoft. Затем он находит часть «Документ в DOM» (DOM - объектная модель документа, содержащая в себе другие части, наряду с самим документом).

var xmlDOM = new ActiveXObject("Microsoft.XMLDOM");
xmlDOM.loadXML(xml);
var doc=xmlDOM.documentElement;

Получив элемент «документ», функция создает массив для хранения всего выводимого HTML. Выбран такой подход, так как новые куски HTML могут успешно помещаться в конец массива, а затем весь массив преобразуется в строку в последний момент.

Чтобы понять следующую часть задачи, надо понять, как принцип DOM формирует XML. Как и большинство современных программных принципов, он гораздо проще, чем кажется. Надо понимать два главных принципа при работе с XML:
1.    Все является узлом.
2.    Игнорируйте все, о чем не надо беспокоиться.

Узлы являются простыми контейнерами. Они могут содержать другие узлы, или могут содержать текст. Поэтому такой кусок XML <myParent><myChild>Hello World</myChild></myParent> хранится в DOM в виде 3 узлов. Первый узел содержит имя узла myParent. Он имеет одного потомка, содержащего имя узла myChild. Узел myChild также имеет одного потомка, являющегося безымянным. Но он содержит текст и имеет значение узла Hello World.

Так можно отличать узлы, содержащие текст, и узлы, содержащие другие узлы (или способные содержать другие узлы, но являющиеся пустыми). Каждый узел имеет nodeType. Типы узла 3 и 4 содержат текст.

Мы почти разобрались с узлами! Последний необходимый для этой статьи вопрос - понятие 'атрибуты'. Атрибуты - элементы ключа="значение(я)", существующие в XML. Например, <myParent gender="female">Dorris</myParent> может быть фрагментом XML, указывающим, что чью-то мать звали Dorris. Атрибуты являются альтернативой использованию более сложных структур XML типа следующей:<myParent><tname>Dorris</name><gender>female</gender></myParent>. Если бы вы распечатали все обсуждения в интернете относительно того, когда атрибуты должны или не должны использоваться, вероятно, вы бы уничтожили джунгли Амазонки. Поэтому пока согласитесь, что атрибуты можно использовать!

Элемент «документ DOM» сам является узлом. Все документы XML должны иметь один внешний узел, потомками которого являются все остальные узлы. ProcessNode получает узел и генерирует HTML-представление этого узла и всех его потомков. Поэтому DisplayXML передает элемент «документ» (неизвестно, почему он не называется «узел «документ») в ProcessNode.

ProcessNode

Хотите испугаться? ProcessNode является «рекурсивным спускающимся обработчиком». Звучит очень сложно, ошеломляюще и пугающе... Но на самом деле это весьма просто. Объяснение выглядит так: любой узел имеет значение или потомка. Имеется функция, обрабатывающая родительский узел. Родительский узел является таким же, как и дочерний узел. Поэтому для обработки родительского узла и его потомков применяется одна и та же функция.

Простейший способ добиться этого – заставить функцию, обрабатывающую родителя, вызывать саму себя для каждого из потомков. Функция, вызывающая сама себя, называется рекурсивной. Функция, использующая рекурсию, чтобы спускаться по отношению типа родитель-потомок, называется рекурсивной спускающейся. Наконец, она обрабатывает узлы по ходу, поэтому называется рекурсивным спускающимся обработчиком.

// Данная версия содержит все, но код обработки XML удален
// Смотрите полную версию в zip-архиве
function ProcessNode(node,outArr)
{
    // Это текстовый узел? Текстовые узлы имеют тип 3 или 4
    if(node.nodeType<3 || node.nodeType>4)
    {
        // !!! – Не текстовый узел - !!!

        // Получаем имя узла
        ... node.nodeName ...

        // Получаем атрибуты
        var atts=node.attributes;

        // Обрабатываем атрибуты, если они есть
        if(atts.length>0)
        {
            for(var i=0;i<atts.length;++i)
            {
                var aNode=atts.item(i);
                // Атрибуты также являются узлами! Они имеют имя и значение имя=значение
                ... aNode.nodeName ...
                ... aNode.nodeValue ...
            }
        }

        // Обрабатываем потомков, если они есть
        if(node.hasChildNodes())
        {
            var newNode=node.firstChild;
            while(newNode!=null)
            {
                // Кусок рекурсии!
                ProcessNode(newNode,outArr);

                newNode=newNode.nextSibling;
            }
        }
    }
    else
    {
        // !!! – Текстовый узел - !!!
        ... node.nodeValue ...
    }
}

Наконец наглядное представление XML в виде HTML отправляется в GUI

Он создается с помощью следующего фрагмента кода:

// Получаем выходные данные
var out=ConcatStringArray(outArr);

// Создаем GUI
var gui= new GUIWindow();

gui.SetHTML(out);
var doc=gui.GetDocument();
var styleSheet;
if(doc.styleSheets.length==0)
{
    styleSheet=doc.createStyleSheet();
}
else
{
    styleSheet=doc.styleSheets[0];
}
styleSheet.addRule('body','color: black;');
styleSheet.addRule('body','font-family: Arial, helvetic, sans-serif;');
styleSheet.addRule('span.nodeName','font-weight: 700;');
styleSheet.addRule('ul.attributeList','color: #008;');
styleSheet.addRule('li.nodeValue','color: #080;');
styleSheet.addRule('*.missing','color: #888;');

gui.SetVisible(true);

Приходится усвоить много вещей, но в результате получается мощный скрипт!