Вообще я в последнее время стараюсь избегать использовать в разработке какие-либо сторонние компоненты и библиотеки, исключением было использование Synapse для работы с HTTP/HTTPS — очень уж я привык к использованию этой простой библиотеки, опять же, не дай бог, придется под Lazarus проект переносить — меньше проблем будет.
А тут вдруг пару раз мне посоветовали использовать при работе с XML разработку SimDesign — NativeXML. В качестве аргументов были «малый размер exe-шника», «удобство», «простота использования» и т.д. Решил проверить, попробовал…понравилось :).
NativeXML — это Open Sourse проект, доступен всем и каждому абсолютно бесплатно. Последняя версия 3.06. Конечно, после использования только стандартных средств для обработки XML библиотека NativeXML выглядит несколько необычно, но разобраться с ней достаточно просто. Чем мы сейчас и займемся.
Изучение начнем с установки NativeXML.
Установка и настройка NativeXML
Скачиваем zip-архив с установочным файлом, распаковываем и устанавливаем библиотеку. Если установка проведена по умолчанию, то все файлы NativeXML будут располагаться в директории:
C:/Program Files/NativeXML/
Где Вы можете найти:
- Документацию по библиотеке (на английском) в папке Documentation
- Пять примеров работы с NativeXML
- Папку Source с исходниками
В исходниках Вы обнаружите всего три файла: NativeXML.pas, NativeXmlObjectStorage.pas и NativeXmlAppend.pas.
Первый файл — NativeXML.pas — это исходник, содержащий все необходимые функции и обеты для работы с XML.
NativeXmlObjectStorage.pas…не довелось ещё его использовать «на полную катушку», но судя по описанию — это небольшая надстройка для хранения объектов в виде XML-файлов.
NativeXmlAppend.pas используется в случаях, когда необходимо добавить какой-либо фрагмент XML в конец существующего файла.
Для нас важно сегодня научиться пользоваться основой — NativeXML.pas.
Теперь, после установки библиотеки на свой компьютер Вы можете пойти двумя путями:
- Просто положить в папку с проектом файл NativeXML.pas, подключить его в uses и использовать его методы.
- Указать в настройках Delphi 2010 путь к папке Source.
В целом результат будет один и тот же — вы сможете использовать NativeXML в своем приложении.
Надеюсь, что с установкой и настройкой проблем у Вас не возникнет. Теперь приступим к изучению библиотеки. Во-первых, научимся читать данные из XML-файла.
Чтение данных из XML-файла
В качестве источника, предлагаю использовать любой RSS-канал, например мой. Почему именно RSS?
Во-первых, для получения данных с RSS-канала, нам не потребуется делать лишних «телодвижений», канал доступен, исходник XML получить легко, используя тот же Indy или Synapse.
Во-вторых, в RSS-канале мы можем встретить практически все «тонкие места», которые вызывают трудности при парсинге, например секции CDATA, пространства имен и т.д.
Создадим простенькое приложение, как показано на рисунке:
По указанному URL будем скачивать исходник XML и выводить его в Memo. Затем прочитаем все элементы RSS-канала и:
1. Выведем общее количество постов блога, находящихся в RSS-потоке
2. Заполним ComboBox заголовками постов
3. Прочитаем ключевые слова (теги, категории) постов — они содержатся в CDATA.
4. Выведем дату публикации сообщений.
Подключаем в uses модули:
uses ... NativeXML, httpsend {httpsend - для Synapse};
Теперь получаем исходник XML:
procedure TForm3.Button1Click(Sender: TObject); begin with THTTPSend.Create do begin if HTTPMethod('GET',Edit1.Text) then begin Memo1.Clear; Memo1.Lines.LoadFromStream(Document, TEncoding.UTF8); end; end; end;
Пишем процедуры парсинга XML-файла.
Во-первых, загрузим XML из потока и прочитаем заголовки статей. Для этого объявим две глобальные переменные:
XMLDoc: TNativeXml; //объект XML-документа NodeList: TXmlNodeList;//список узлов
Первая процедура:
procedure TForm3.ParseXML(const XML: TStream); var i:integer; begin XMLDoc:=TNativeXml.Create;//создаем экземпляр класса XMLDoc.LoadFromStream(XML);//загружаем данные из потока if XMLDoc.IsEmpty then raise Exception.Create('Пустой XML! Работа прервана!'); NodeList:=TXmlNodeList.Create; XMLDoc.Root.FindNodes('item',NodeList);//получаем список узлов Item label4.Caption:=IntToStr(NodeList.Count); {парсим каждый узел Item} ComboBox1.Items.Clear; for I := 0 to NodeList.Count - 1 do ComboBox1.Items.Add(NodeList.Items[i].NodeByName('title').ValueAsString); end;
Теперь, при выборе в ComboBox какого-либо элемента мы должны вывести в Edit’ы ключевые слова и дату публикации. Так как ключевики содержаться в элементах CDATA, то следует заметить следующее:
тэги CDATA рассматриваются в NativeXML как дочерние узлы элемента, имеющие тип xeCData.
.
Следовательно, процедуру чтения данных об отдельном элементе RSS-потока можно представить следующим образом:
procedure TForm3.ComboBox1Change(Sender: TObject); var Category: TXMLNodeList; i:integer; begin Edit2.Text:=''; Edit3.Text:=''; with NodeList.Items[ComboBox1.ItemIndex] do begin Category:=TXmlNodeList.Create; Edit3.Text:=NodeByName('pubDate').ValueAsString; NodesByName('category',Category); for I := 0 to Category.Count - 1 do Edit2.Text:=Edit2.Text+Category.Items[i].NodeByElementType(xeCData).ValueAsString+', '; end; end;
Теперь немного поразбираемся с тем, что мы собственно тут наделали. Итак, вначале мы создаем экземпляр класса TNativeXML и загружаем в него данные из потока. Если проводить аналогию со стандартными средствами для работы с XML в Delphi, то TNativeXML — это то же самое, что и TXMLDocument.
Далее мы выводили в ComboBox названия постов и делали это в цикле:
for I := 0 to NodeList.Count - 1 do ComboBox1.Items.Add(NodeList.Items[i].NodeByName('title').ValueAsString);
Здесь мы выбираем отдельный элемент TXMLNode из списка узлов TXmlNodeList. При этом узел мы находим по имени (title). Затем мы читаем содержимое узла как простую строку UTF8String.
Одной из замечательных возможностей NativeXML является то, что мы можем читать содержимое узла и тут же автоматически его приводить к необходимому типу данных. Например так:
NodeList.Items[i].NodeByName('AnyNode').ValueAsInteger
я прочитал содержимое узла и привел его к значение к типу Integer. Аналогичным образом можно работать даже с такими типами как Boolean, DateTime и т.д. — NativeXML позволяет нам сильно не задумываться о том как преобразовать данные узла.
При работе с отдельным элементом RSS-потока мы дополнительно использовали ещё одну возможность NativeXML — выбор узла по его типу:
Category.Items[i].NodeByElementType(xeCData).ValueAsString
Здесь мы выбрали узел из списка, у этого узла выделили узел с типом xeCData и прочитали содержимое этого узла.
Всего в NativeXML используется 14 различных типов узлов, от обычных xeNormal, до таких специфичных как xeEntity (< !ENTITY >). Вы можете использовать эти типы данных для поиска узлов или записи данных в файл.
Похожим образом организована работа NativeXML и с атрибутами узла. Для примера, прочитаем атрибут вот этого узла из RSS:
< guid isPermaLink="false">http://webdelphi.ru/?p=3349< / guid >
Чтение атрибута, имеющего тип boolean:
NodeByName('guid').ReadAttributeBool('isPermaLink')
Также помимо приведения значений атрибутов к типам данных Boolean, Integer, double и т.д. вы можете использовать также приведение к таким типам как TPen или TRect.
Теперь выполним обратныю операцию — запись данных в XML-файл.
Запись данных в XML-файл
Попробуем записать простенький XML-файл, содержащий несколько узлов, например так:
procedure TForm3.Button2Click(Sender: TObject); var XMLDoc: TNativeXml; Node: TXmlNode; begin XMLDoc:=TNativeXml.Create;//создали документ XMLDoc.CreateName('RootNode');//создали корневой узел //создаем дочерний узел Node:=TXmlNode.Create(nil); Node.Name:='FirstNode'; Node.ValueAsString:='Значение узла'; Node.AttributeAdd('attr_integer',12);//добавляем атрибут integer Node.WriteAttributeDateTime('attr_date',Now);//другой способ. записываем дату XMLDoc.Root.NodeAdd(Node); {второй узел: другой вариант записи} XMLDoc.Root.NodeNew('SecondNode').ValueAsBool:=false; XMLDoc.SaveToFile('MyXML.xml'); end;
В итоге получим XML-документ следующего содержания:
Теперь попробуем немного усложнить заачу и записать узел, содержащий секцию CDATA. Сделать это можно, например так:
var WithCdata, NodeCDATA: TXmlNode; ... WithCdata:=TXmlNode.Create(nil); NodeCDATA:=TXmlNode.CreateType(XMLDoc,xeCData); NodeCDATA.ValueAsString:='Это строка в CDATA'; WithCdata.Name:='WithCdata'; WithCdata.NodeAdd(NodeCDATA); XMLDoc.Root.NodeAdd(WithCdata); ...
В итоге наш документ примет следующий вид:
Ну и, наконец пара моментов связанных с изменением кодировки документа, версии xml и т.д. Такие изменения следует проводить после добавления корневого узла. Например так:
XMLDoc:=TNativeXml.Create;//создали документ XMLDoc.CreateName('RootNode');//создали корневой узел //задаем параметры документа XMLDoc.WriteOnDefault:=false; XMLDoc.CommentString:='Комментарий к файлу'; XMLDoc.EncodingString:='UTF-8'; XMLDoc.VersionString:='1.0';
Я задал кодировку документа, версию и дополнительно записал комментарий к файлу. Обратите внимание, что перед этим я заменил свойство:
XMLDoc.WriteOnDefault:=false;
То есть отключил запись документа по умолчанию.
Вот пожалуй и все по поводу первого знакомства с NativeXML в Delphi. Надеюсь, что статья поможет вам начать работу с этой библиотекой и разобраться более детально с её работой.
Все эти самописные компоненты хороши пока не сравниваем возможности и скорость работы. И вот тут получается что стандартный MSXML, есть в любой windows ОС, быстрее (по моим данным в разы) и имеет несравнимо больше возможностей.
А как он относится к большим файлам? К примеру, файл на 34М сложной структуры он нормально распарсит?
vbif, в этой версии — да, парсил и по 50М файлы шустро. В последней версии — не могу сказать, т.к. не использовал её.
А речь идёт как раз о переносимости на другие платформы) Лично мне нравится кросплатформенный libxml, не знаю есть ли реализация на Delphi, но в любом случае можно реализовать варпер…
Ребята помогите пожалуйста!Как прочитать и такую кострукцию!!! я имею ввиду simpleChoice верхний элемент считываю «Демокрит» а другие не получаются подскажите
<itemBody>
<p>Воспитателем Александра Македонского был </p>
<choiceInteraction shuffle=»true» maxChoices=»1″>
<simpleChoice identifier=»3″>Демокрит</simpleChoice>
<simpleChoice identifier=»2″>Гераклит</simpleChoice>
<simpleChoice identifier=»1″>Аристотель</simpleChoice>
<simpleChoice identifier=»0″>Платон</simpleChoice>
</choiceInteraction>
</itemBody>
Надо использовать класс TXMLNodeList и юзать процедурку NodesByName. Ну, а потом — элементарный цикл for..do для вывода значений узлов
вот что получилось NodesByName(‘choiceInteraction’,choiceInteraction);
if FirstList.Items[ComboBox2.ItemIndex].NodeByName(‘choiceInteraction’) <> nil then
begin
memo1.Clear;
for j := 0 to choiceInteraction.Items[0].NodeCount — 1 do
memo1.Lines.Add(VarToStr(Utf8ToAnsi(choiceInteraction.Items[0].Nodes[j].ValueAsString)));
memo2.Clear;
for l := 0 to choiceInteraction.Items[0].NodeCount — 1 do
memo2.Lines.Add(VarToStr(Utf8ToAnsi(choiceInteraction.Items[0].Nodes[l].ReadAttributeString(‘identifier’))));
end;
Спс Vlad!Разобрался!все равно если 5 и больше simpleChoice то выводит в мемо 4???Как тут правильно цикл реализовать подскажите пожалуйсто!
неееее все работает))))Спс
Очень полезный компонент для тех кто собирается сэкономить и купить Delphi XE Starter (я например), так как нету в нет TXMLDocuments.
Не только поэтому, по-моему NativeXML нормально работает в Lazarus
А кто нибудь пробовал заточить NativeXML под Lazarus. Сходу не получилось.
Я использовал NativeXML под Lazarus. Было это давненько (где-то с год назад) и не помню, чтобы возникали какие-то проблемы в работе. Попробуйте найти предыдущий вариант библиотеки — может в новой чего-то недоработали в плане Lazarus’а
Интересно, а под Delphi XE2 запустится спокойно?
надо проверить
Скачал версию 402 и решил попробовать — Node:=TXmlNode.Create(nil); — ошибка.
Нету у TXmlNode такого.
потому что CreateParent там вроде конструктор. у которого два параметра — владелец, и родительский узел.
зы: попробовал тут пару раз использовать, при записи очень шустрый, а при чтении данных че то вообще провал. либо хз. надо разбираться в чем проблема.
бомба для работы с Cdata
херня полная этот nativexml — не работает в xe6