В прошлый раз была разминка. Прочитали несколько узлов — запомнили и успокоились. Сегодня задачка будет по-сложнее — разбор элементов Entry в Google Data Feed. В начале небольшой экскурс в мануалы по Google Data Protocol v.2.0 в часть, касающуюся проблемной области.
Итак, элементы Entry — это по сути хранилище всевозможных данных для работы со всеми известными API, использующими в своей работе Google Data Protocol. В этих элементах может содержаться всё, что угодно — от контактной информации до наборов данных Google Analytics, FeedBurner и пр. Однако есть четыре предопределенных структуры для entry (определяются как kinds) — это:
- Contact kind — набор данных контакта
- Profile Kind — набор данных по профилю пользователя
- Event kind — набор данных для события
- Message kind — набор данных для сообщения
В каждой из этих структур выделены элементы: обязательные, необязательные и дополнительные.
Причём обязательных элементов намного меньше, чем всех других. Все элементы, непосредственно относящиеся к GData («родные» для протокола) определяются как:
gd:ххх
где ххх название элемента. В этуже схему включаются перечислители, т.е. те узлы XML, которые содержат только чётко определенные строковые аттрибуты, например элемент gd:eventStatus может содержать в атибуте Value следующие значения:
http://schemas.google.com/g/2005#event.canceled
http://schemas.google.com/g/2005#event.confirmed
http://schemas.google.com/g/2005#event.tentative
Короче, немного поковырявшись в «родных» элементах протокола можно составить список из 49 различных gd-элементов. Каждый компонент при этом может содержать как текстовую информацию, так и набор аттрибутов в т.ч. перечисляемого типа, integer, boolean и т.д. «Гадость» заключается в том, что отдельные элементы, например gd:feedLink может содержать в себе точно такой же Feedб кокой разбирается в текущий момент, а этот фид опять же может пестрить элементами entry и т.д. и т.п. Короче сходу так просто и не въедешь в тему откуда ноги произрастают у данных. Собственно, отчасти поэтому, отчасти по другой причине и начал разбирать XML в TreeView.
Помимо всего прочего протокол не запрещает размещать внутри Entry и другие элементы API, например элементы gCal:xxx — определяющие данные для Календаря Google и т.д. Как вариант, можно было бы оставить все как есть, т.е. «выдирать» из фида необходимую информацию, работать с ней и на этом успокоится. Но, мы не будем полагаться на случай (с). Итак, что «умеет» на текущий момент модуль для работы с GData.
Первое: разбирает Feed и выделяет основные элементы фида, отдельно сохраняя все элементы Entry. При этом можно провести обратную процедуру — передать набор элементов в класс и собрать XML-документ для отправки на сервер.
Второе: в части работы с Entry. Работа ведется следующим образом:
1. В каждом элементе Entry определяются как и в Feed корневые элементы, относящиеся непосредственно к узлу, например автор записи, даты публикации и обновления и т.д.
2. Все оставшиеся элементы фильтруются и выводится список элементов gd-элементов, имеющий следующий вид:
type TGDElement = record ElementType : TgdEnum; XMLNode: IXMLNode; end; type PGDElement = ^TGDElement; type TGDElemntList = class(TList) private procedure SetRecord(index: Integer; Ptr: PGDElement); function GetRecord(index: Integer): PGDElement; public constructor Create; procedure Clear; destructor Destroy; override; property GDElement[i: Integer]: PGDElement read GetRecord write SetRecord; ... end;
3. Оставшиеся элементы пока в расчёт не беруться, т.к. у них опять же будут свои структуры, атрибуты и т.д.
4. Любой элемент из списка TGDElemntList можно обработать соответсвующие функцией и уже из IXMLNode получить структуру Delphi (record, class), с которой можно делать всё, что угодно: править, дополнять и т.д. Если не нужна структура — не проблема — можно работать непосредственно с узлом IXMLNode.
Например, так происходит обработка одного из узлов в entry:
function gdAdditionalName(aXMLNode: IXMLNode): TgdAdditionalNameStruct; begin if GetGDNodeType(aXMLNode.NodeName) <> ord(egdAdditionalName) then raise Exception.Create(Format(rcErrCompNodes,[cGDTagNames[ord(egdAdditionalName)]])); try if aXMLNode.Attributes['yomi'] <> null then Result.yomi := aXMLNode.Attributes['yomi']; Result.Text := aXMLNode.Text; except raise Exception.Create(Format(rcErrPrepareNode, [aXMLNode.NodeName])); end; end;
Здесь мы берем узел gd:AdditionalName и представляем его в виде структуры:
type TgdAdditionalNameStruct = record yomi: string; //атрибут узла, означающий псевдоним пользователя Text: string; //текстовая часть узла - имя end;
Аналогичным образом идёт обработка и более сложных узлов, например, содержащих внутри себя ещё ряд узлов gd:xxx.
Для конечного пользователя, которому лень смотреть исходник опять же всё умещается в несколько строк кода. Возвращаясь к примеру выше получение структуры TgdAdditionalNameStruct в вызывающей программе будет может быть таким:
var Node: IXMLNode; Struct: TgdAdditionalNameStruct; begin ... Struct:=gdAdditionalName(Node) ... end;
Или используя любой цикл перечислить все узлы списка и перебрать все структуры, но это, думаю будет немного лишним.
В настоящий момент дописываю работы с gd-элементами в фиде. Слудующий шаг — обратная процедура: по предоставленным данным собрать фид и скинуть на сервер. Ну и, конечноже, более плотная работа с Google Celendar API.