Парсинг локально подключаемого xml файла объемом более 17 мб
Вложений: 1
Здравствуйте!
Пишу функции для справочника в формате chm. Задача - локально подключить и отпарсить xml файл объёмом 17-25 мб (следовательно выполнить это на движке IE6). В результате получил огромную нагрузку на процессор (если решитесь запустить index.html на слабом компе, то запаситесь терпением на время обработки. Chm работает быстрее и выведет данные, примерно, через 5-10 сек). Помогите найти более логичное и быстрее работающее решение задачи чем то, что я своял: Теперь всё по порядку. xml имеет следующую структуру: Код:
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">С подключением xml я справился следующим образом (эксперимента ради реализовал поддержку и для Firefox с помощью объекта XMLHttpRequest): //см. рекомендации на w3schools.com
// 1. делаем запрос к БД, т.е. загружаем xml
function createDocument()
{
//Temporary DOM object.
var xmlDoc;
//Create the DOM object for IE
if (window.ActiveXObject)
{
var versions =
[
"Msxml2.DOMDocument.6.0",
"Msxml2.DOMDocument.3.0"
];
for (var i = 0; i < versions.length; i++)
{
try
{
xmlDoc = new ActiveXObject(versions[i]);
xmlDoc.load("data_01.xml");
return xmlDoc;
}
catch (error)
{
//do nothing here
}
}
}
//Cтроим DOM для Firefox
else if (window.XMLHttpRequest)
{// локально работае везде кроме Chrome и Opera 11
xmlDoc=new XMLHttpRequest();
xmlDoc.open("GET","data_01.xml",false);
xmlDoc.send();
xmlDoc=xmlDoc.responseXML;
return xmlDoc;
}
return null;
}
Далее, парсим xml в двухмерный массив arr[i][j]
var xmlDoc = createDocument();
// 2. парсим тело полученного документа в массив извлекая текстовые данные посредством свойств firstChild.data
var arr = [],
xmlRows = xmlDoc.getElementsByTagName('Row');
// log
log("Загружено элементов Row: "+xmlRows.length+". ");
for(var i=0; i<xmlRows.length; i++) {
var xmlCols = xmlRows[i].getElementsByTagName('Col');
arr[i] = [];
for(var j=0; j<xmlCols.length; j++) {
arr[i][j] = xmlCols[j].firstChild.data;
// log
// log("Загружено элементов Col: "+arr[i][j].length+". ");
}
}
// Возвращаем массив Arr
return arr;
Потом вывожу данные построчно в динамически формируемый список. На этом, пока, всё. В дальнейшем предполагается разбить полученный массив arr на множество двухмерных массивов, данные которых будут выводиться на экран в зависимости от действий пользователей. Вот весь рабочий код:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Chm справочник на xml базе данных</title>
<body>
<div id="logDiv" style="position: relative; clear:both; margin: 0px auto; width: 334px; height: 100px; overflow: auto; white-space: pre-wrap; border: 1px solid black; left: 9px; margin-top: 0px;"></div>
<center><input type="button" value="Очистить журнал" onclick="clearLog();"/></center>
<script type="text/javascript">
function log(str) {
document.getElementById("logDiv").innerHTML += str+"\n";
}
function clearLog() {
document.getElementById("logDiv").innerHTML = "";
}
// Чтобы не засорять пространство глобальных переменных все делаем в безымянной функции
var DBArray = (function() {
// 1. делаем запрос к БД, т.е. загружаем xml
function createDocument()
{
//Temporary DOM object.
var xmlDoc;
//Create the DOM object for IE
if (window.ActiveXObject)
{
var versions =
[
"Msxml2.DOMDocument.6.0",
"Msxml2.DOMDocument.3.0"
];
for (var i = 0; i < versions.length; i++)
{
try
{
xmlDoc = new ActiveXObject(versions[i]);
xmlDoc.load("data_01.xml");
return xmlDoc;
}
catch (error)
{
//do nothing here
}
}
}
//Cтроим DOM для Firefox
else if (window.XMLHttpRequest)
{// локально работае везде кроме Chrome и Opera 11
xmlDoc=new XMLHttpRequest();
xmlDoc.open("GET","data_01.xml",false);
xmlDoc.send();
xmlDoc=xmlDoc.responseXML;
return xmlDoc;
}
return null;
}
var xmlDoc = createDocument();
// 2. парсим тело полученного документа в массив
var arr = [],
xmlRows = xmlDoc.getElementsByTagName('Row');
// log
log("Загружено элементов Row: "+xmlRows.length+". ");
for(var i=0; i<xmlRows.length; i++) {
var xmlCols = xmlRows[i].getElementsByTagName('Col');
arr[i] = [];
for(var j=0; j<xmlCols.length; j++) {
arr[i][j] = xmlCols[j].firstChild.data;
// log
// log("Загружено элементов Col: "+arr[i][j].length+". ");
}
}
// Возвращаем массив Arr
return arr;
})();
// выводим списком текстовое содержимое элементов Col которое отпарcили в массив arr функции DBArray
function createList(data) {
var list = document.createElement("ul");
for (var i = 0; i < data.length; i++) {
var newItem = document.createElement("li");
var newText = document.createTextNode(data[i]);
newItem.appendChild(newText);
list.appendChild(newItem);
}
return list;
}
window.onload = function() {
var list = createList(DBArray);
document.body.appendChild(list);
}
</script>
</body>
</html>
P. S. К сожалению я специализируюсь, в основном, на верстке и пока плохо владею javascript. Прошу простить если моя задача покажется Вам слишком абсурдной, с радостью приму любые советы по решению изложенной проблемы. |
Занятно. А не пробовали делать парсинг с помощью Dom парсера?
ActiveXObject("Microsoft.XMLDOM") в ИЕ, DomParser в остальных? (http://javascript.ru/forum/misc/2090...tml#post121722) Может, будет быстрее? |
Только что обновил сообщение прикрепив ссылку на w3schools.com
DOM parser предполагает наличие тегов в теле документа с функцией, что уже не очень удобно, к тому же все данные обрабатываются в одну текстовую строку, у меня никак не удалось запихнуть их в в духмерный массив:
<script>
txt="<note>";
txt=txt+"<from>Jani</from>";
txt=txt+"<from>Anne</from>";
txt=txt+"<from>Jolie</from>";
txt=txt+"<from>Susanne</from>";
txt=txt+"<from>Marie</from>";
txt=txt+"<to>Tove</to>";
txt=txt+"<from>Marie</from>";
txt=txt+"<from>Anne</from>";
txt=txt+"<from>Jolie</from>";
txt=txt+"<from>Анна</from>";
txt=txt+"<from>Вера</from>";
txt=txt+"</note>";
txt="<note>";
txt=txt+"<from>Jolie</from>";
txt=txt+"<from>Susanne</from>";
txt=txt+"<from>Marie</from>";
txt=txt+"<to>Tove</to>";
txt=txt+"<from>Marie</from>";
txt=txt+"<from>Anne</from>";
txt=txt+"<from>Jolie</from>";
txt=txt+"<from>Marie</from>";
txt=txt+"<from>Инна</from>";
txt=txt+"</note>";
txt="</root>";
var xmlDoc;
if (window.DOMParser)
{
parser=new DOMParser();
xmlDoc=parser.parseFromString(txt,"text/xml");
}
else // Internet Explorer
{
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async="false";
xmlDoc.loadXML(txt);
}
var arr = [],
xmlRows = xmlDoc.getElementsByTagName('note');
for(var i=0; i<xmlRows.length; i++) {
var xmlCols = xmlRows[i].getElementsByTagName('from');
arr[i] = [];
for(var j=0; j<xmlCols.length; j++) {
arr[i][j] = xmlCols[j].firstChild.data;
}
}
document.getElementById("from").innerHTML=arr;
</script>
|
Слышал, но не проверял, что обратные цыклы работают быстрее.
Т.е. i меняется не от 0 к length, а от length к 0 и проверяется при этом во втором условии i. Т.е. как-то так: for (var i=xmlCols.length; i; i--) Но даже если делать с прямым циклом, то length лучше посчитать заранее (у вас в каждой итерации цикла data.length пересчитывается вроде как): for (var i = 0, l = data.length; i < l; i++) |
Цитата:
http://jsperf.com/caching-array-length/12 |
Цитата:
|
Спасибо за совет! Значене length конечно можно заранее объявить, но вся вся нагрузка на проц. ложится при разборе arr[i][j] = xmlCols[j].firstChild.data, т. е. когда извлекаются текстовые данные.
|
Цитата:
|
Да какая разница какие циклы быстрее, сам цикл никогда не будет слабым звеном, если только вы не проверяете, какие циклы быстрее.
|
А обязательно использовать XML ???
Может получится хранить инфу в json ???? Тогда и преобразовывать из массива в массив не надо, храни сразу так как планируешь использовать. |
Цитата:
|
Цитата:
|
Можно в промежутке XSL трансформацию сделать в JSON )
|
Конечно, если бы написать VBA надстройку к EXCEL, которая импортировала бы готовый файл в формате json ... Но программист, написавший форму для заполнения БД под excel, пока, отказался это делать. Если я смогу четко описать структуру БД в json, то может он и возьмется за работу.
|
Есть еще вариант, запускать преобразования пачками с таймаутами. Тогда браузер не замрет на 5 секунд, но и время разбора будет большим, ну и данные будут доступны не сразу, а постепенно...
|
Цитата:
|
Цитата:
На что я обратил для себя внимание:
У меня в Opera 11 результаты подтвердили статистику: for in 252 ±1.10% 99% slower for check index 2,664 ±0.25% 89% slower using cached length outside test 13,012 ±0.29% 44% slower - самые медленные способы организации цикла в Opera 11 |
Сравнение производительности циклов в разных браузерах это конечно хорошо, но как лучше переписать функции в моём примере для производительной работы в IE6. Проверял убирая функцию вывода списка, выяснил, что тормоза, в первую очередь не в data.length, а в arr[i][j] = xmlCols[j].firstChild.data. Исправьте меня если не прав.
|
Цитата:
|
Цитата:
|
Цитата:
Хотя, всё это слишком мудрено логичнее, не умножая сущностей, отпарсить все данные сразу поставив пользователю красивую заставку на время парсинга))) |
Важнее сейчас следующее: подскажите как заменить конструкцию с firstChild.data ведь документ с пустыми тегами типа <Col></Col> не проходит обработку, или как-нибудь создавать пустую строку чтобы firstChild.data находил объект? Замена <Col></Col> на <Col>-</Col> не выход из положения!
|
А вот еще что можно оптимизировать. Создание новых нодов и потом appendChild - достаточно тяжелые операции. Гораздо легче создать строку, и потом вставить ее через innerHTML.
// Вместо
function createList(data) {
var list = document.createElement("ul");
for (var i = 0; i < data.length; i++) {
var newItem = document.createElement("li");
var newText = document.createTextNode(data[i]);
newItem.appendChild(newText);
list.appendChild(newItem);
}
return list;
}
// Делаем
function createList(data) {
var list = document.createElement("ul"),
cont = '',
i, l;
for (i = 0, l = data.length; i < l; i++) {
cont += '<li>' + data[i] + '</li>';
}
list.innerHTML = cont;
return list;
}
|
Вам уже советовали использовать JSON вместо XML, я присоединяюсь к этому совету. Приблезительно как будет выглядеть файл:
[ [ "text1",
"text2",
"img/img1.png",
"img/img2.png"],
[ "text3",
"text4",
"img/img3.png",
"img/img4.png"],
[ "text5",
"text6",
"img/img5.png",
"img/img6.png"] ]
Замечу что этот массив полностью аналогичен тому массиву который Вы пытаетесь получить с XML. Есть еще идея, если Вы планируете использовать данный файл только в браузерах то Вы можете формировать изначально правильный JavaScript документ и подсоединять его к документу через теги: <script type="text/javascript" src="data-table.js"></script> Вот как примерно будет выглядеть документ:
var dataTable = [ [ 'text1',
'text2',
'img/img1.png',
'img/img2.png'],
[ 'text3',
'text4',
'img/img3.png',
'img/img4.png'],
[ 'text5',
'text6',
'img/img5.png',
'img/img6.png'] ];
tableLoad();
Замечу что этот метод кросбраузерный и он минимально нагружает процессор. Данный скрипт нужно подключать в самом конце документа и функция tableLoad(); должна уже быть определена где-то выше. Срабатывание этой функции означает что база полностью загружена и можно с ней работать. |
Спасибо, Magneto!
Главный вопрос: будет ли работать Json в CHM файле, т. е. на движке IE6? Я ведь пишу функции для CHM-справочника. |
| Часовой пояс GMT +3, время: 20:19. |