Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Подземелье Магов
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  22:28[Войти] | [Зарегистрироваться]

Создание DTD для объекта

Андрей Чудин
дата публикации 01-10-2001 12:29

Создание DTD для объекта

За созданием кода для сериализации и десериализации объектов в Delphi логично перейти к рассмотрению вопроса о возможности генерации соответствующего DTD для сохраняемых в XML классов. DTD понадобится нам, если мы захотим провести проверку XML документа на корректность и допустимость с помощью одного из XML анализаторов. Работа с анализатором MSXML рассмотрена в статье Загрузка и анализ документа XML. на авторском сайте.

Автоматическое создание DTD очень простая задача. У нас все для этого есть. Необходимо рекурсивно пройтись по всем свойствам объекта и сгенерировать модели содержания для каждого тега. При сериализации в XML мы не использовали атрибутов, а значит мы не сможем в DTD установить контроль над содержанием конкретных элементов. Остается только определить модель содержания для XML, т.е. вложенность тегов в друг друга. Хотя стандарт DTD устаревает и следует переходить к использованию схем, будет полезным обеспечить возможность создания DTD для наших объектов.

Создадим процедуру GenerateDTD(), которая обеспечит запись формируемого DTD для заданного объекта Component в заданный поток Stream. Она создает список DTDList, в котором будут накапливаться атрибуты DTD, после чего передает всю черновую работу процедуре GenerateDTDInternal().

{ 
  Процедура генерации DTD для заданного объекта в 
  соответсвии с published интерфейсом его класса. 
  Вход: 
    Component - объект 
  Выход: 
    текст DTD в поток Stream 
} 
procedure GenerateDTD(Component: TObject; Stream: TStream); 
var 
  DTDList: TStringList; 
begin 
  DTDList := TStringList.Create; 
  try 
    GenerateDTDInternal(Component, DTDList, Stream, Component.ClassName); 
  finally 
    DTDList.Free; 
  end; 
end; 


Следующий код просматривает свойства объекта, составляет их список, а затем формирует из этого модель содержания для элемента. Для свойств классовых типов используется рекурсия. Поскольку при сериализации объекта мы не использовали атрибутов, то определений для них создавать нет необходимости.

Для всех неклассовых типов модель содержания это - (#PCDATA). К примеру, свойство объекта Tag: integer превращается в .

Отдельно подходим к коллекциям. Для них необходимо указать на множественность дочернего тега элемента коллекции. Например, для свойства TMyCollection модель содержания может выглядеть так: .

{ 
  Внутренняя рекурсивная процедура генерации DTD для заданного объекта. 
  Вход: 
    Component - объект 
    DTDList - список уже определенных элементов DTD 
              для предотвращения повторений. 
  Выход: 
    текст DTD в поток Stream 
} 
procedure GenerateDTDInternal(Component: TObject; DTDList: TStrings; 
		Stream: TStream; const ComponentTagName: string); 
var 
  PropInfo: PPropInfo; 
  TypeInf, PropTypeInf: PTypeInfo; 
  EnumInfo: PTypeInfo; 
  TypeData: PTypeData; 
  i, j: integer; 
  AName, PropName, sPropValue, s, TagContent: string; 
  PropList: PPropList; 
  NumProps: word; 
  PropObject: TObject; 
const 
  PCDATA = '#PCDATA'; 
  procedure addElement(const ElementName: string; Data: string); 
  var s: string; 
  begin 
    if DTDList.IndexOf(ElementName) <> -1 then exit; 
    DTDList.Add(ElementName); 
    s := 'if Data = '' then Data := PCDATA; 
    s := s + '(' + Data + ')>'#13#10; 
    Stream.Write(PChar(s)[0], length(s)); 
  end; 
begin 
  { Playing with RTTI } 
  TypeInf := Component.ClassInfo; 
  AName := TypeInf^.Name; 
  TypeData := GetTypeData(TypeInf); 
  NumProps := TypeData^.PropCount; 
 
 
  GetMem(PropList, NumProps*sizeof(pointer)); 
  try 
    { Получаем список свойств } 
    GetPropInfos(TypeInf, PropList); 
    TagContent := ''; 
 
    for i := 0 to NumProps-1 do 
    begin 
      PropName := PropList^[i]^.Name; 
 
      PropTypeInf := PropList^[i]^.PropType^; 
      PropInfo := PropList^[i]; 
 
      { Пропустить не поддерживаемые типы } 
      if not (PropTypeInf^.Kind in [tkDynArray, tkArray, 
	  	tkRecord, tkInterface, tkMethod]) then 
      begin 
        if TagContent <> '' then TagContent := TagContent + '|'; 
        TagContent := TagContent + PropName; 
      end; 
 
 
      case PropTypeInf^.Kind of 
        tkInteger, tkChar, tkFloat, tkString, 
        tkWChar, tkLString, tkWString, tkVariant, tkEnumeration, tkSet: 
        begin 
          { Перевод в DTD. Для данных типов модель содержания - #PCDATA } 
          addElement(PropName, PCDATA); 
        end; 
        { код был бы полезен при использовании атрибутов 
        tkEnumeration: 
        begin 
          TypeData:= GetTypeData(GetTypeData(PropTypeInf)^.BaseType^); 
          s := ''; 
          for j := TypeData^.MinValue to TypeData^.MaxValue do 
          begin 
            if s <> '' then s := s + '|'; 
            s := s + GetEnumName(PropTypeInf, j); 
          end; 
          addElement(PropName, s); 
        end; 
        } 
        tkClass: { Для классовых типов рекурсивная обработка } 
        begin 
          PropObject := GetObjectProp(Component, PropInfo); 
          if Assigned(PropObject)then 
          begin 
            { Для дочерних свойств-классов - рекурсивный вызов } 
            if (PropObject is TPersistent) then 
              GenerateDTDInternal(PropObject, DTDList, Stream, PropName); 
          end; 
        end; 
 
      end; 
    end; 
 
    { Индивидуальный подход к некоторым классам } 
    { Для коллекций необходимо включить в модель содержания тип элемента } 
    if (Component is TCollection) then 
    begin 
      if TagContent <> '' then TagContent := TagContent + '|'; 
      TagContent := TagContent + 
	   (Component as TCollection).ItemClass.ClassName + '*'; 
    end; 
 
    { Добавляем модель содержания для элемента } 
    addElement(ComponentTagName, TagContent); 
  finally 
    FreeMem(PropList, NumProps*sizeof(pointer)); 
  end; 
 
end; 

Закомментированный код нам не нужен, но он не удален, т.к. он демонстрирует получение списка возможных значений для перечисления (Enumeration) и набора (Set). Это может понадобиться, если появится необходимость генерировать свойства в виде атрибутов XML тегов и, соответственно, DTD для возможных значений этих атрибутов.

Продолжение:


Смотрите также материалы по темам:
[TObject] [TPersistent] [TCollection] [TStrings] [TStringList] [TStream] [XML]

 Обсуждение материала нет сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования