:: MVP ::
|
|
:: RSS ::
|
|
|
С неприятной ситуацией в IXMLDocument можно столкнуться, работая с пространством имен – xmlns. А неприятная она
своим недетерминированным поведением, ведь поддерживать код, который на разный машинах выдает разный результат,
практически невозможно. Рассмотрим ситуацию на простом xml следующего вида:
Для создания такого XML напишем небольшой код:
procedure TForm1.Button1Click(Sender: TObject);
var
Document: IXMLDocument;
Root, Node1, Node2: IXMLNode;
begin
Document := TXMLDocument.Create(nil);
Document.Active := True;
Document.Version := '1.0';
Document.Encoding := 'UTF-8';
Document.NodeIndentStr := ' ';
Document.Options := Document.Options + [doNodeAutoIndent];
Root := Document.AddChild('root');
Node1 := Root.AddChild('title1');
Node1.Text := 'child1';
Node2 := Root.AddChild('title2');
Node2.Text := 'child2';
ShowMessage(Document.Node.XML);
end;
|
Этот код можно немного упростить:
procedure TForm1.Button1Click(Sender: TObject);
var
Document: IXMLDocument;
Root, Node1, Node2: IXMLNode;
begin
Document := NewXMLDocument;
Document.Encoding := 'UTF-8';
Document.NodeIndentStr := ' ';
Document.Options := Document.Options + [doNodeAutoIndent];
Root := Document.AddChild('root');
Node1 := Root.AddChild('title1');
Node1.Text := 'child1';
Node2 := Root.AddChild('title2');
Node2.Text := 'child2';
ShowMessage(Document.Node.XML);
end;
|
На этом этапе нет никаких проблем, так как пространство имен еще не определено (вернее оно
пустое по умолчанию). Но если теперь мы добавим пространство имен корневому узлу
procedure TForm1.Button1Click(Sender: TObject);
{...}
begin
{...}
Root := Document.AddChild('root');
Root.Attributes['xmlns'] := 'http://www.w3.org/2005/Atom';
{...}
end;
|
Увидим, что в узлах title{...} появился пустой атрибут xmlns
Вероятной причиной такого поведения является то, что newNode (title) исходит из документа без
объявленного пространства имен, но oldNode (root) находится в документе с пространством имен.
В этой ситуации узел переносит свое пустое пространство имен в документ, и оно отображается явно.
Приняв это во внимание, попробуем решить проблему, для этого попробуем добавить пространство имен
ко всем узлам, порожденным от корневого.
procedure TForm1.Button1Click(Sender: TObject);
var
Document: IXMLDocument;
Root, Node1, Node2: IXMLNode;
begin
Document := NewXMLDocument;
Document.Encoding := 'UTF-8';
Document.NodeIndentStr := ' ';
Document.Options := Document.Options + [doNodeAutoIndent];
Root := Document.AddChild('root');
Root.Attributes['xmlns'] := 'http://www.w3.org/2005/Atom';
Node1 := Root.AddChild('title1', Root.NamespaceURI);
Node1.Text := 'child1';
Node2 := Root.AddChild('title2', Root.NamespaceURI);
Node2.Text := 'child2';
ShowMessage(Document.Node.XML);
end;
|
Но это не помогает. Причина этого становится понятна, если посмотреть на значение Root.NamespaceURI
под отладчиком (или вывести его в ShowMessage) – оно пустое. Сделаем вывод – пространство имен
невозможно определить путем установки атрибута с именем xmlns. Зададим пространство имен при создании
дочерних узлов явно, через DeclareNamespace:
procedure TForm1.Button1Click(Sender: TObject);
{...}
begin
{...}
Root := Document.AddChild('root');
Root.DeclareNamespace('', 'http://www.w3.org/2005/Atom');
Node1 := Root.AddChild('title1', Root.NamespaceURI);
Node1.Text := 'child1';
Node2 := Root.AddChild('title2', Root.NamespaceURI);
Node2.Text := 'child2';
{...}
end;
|
И снова ничего не изменилось, а это уже выглядит странно, ведь пространство имен для Root указано явно
вызовом метода DeclareNamespace. Для того чтобы код заработал в таком виде, пространство имен у дочерних
узлов нужно указывать явно (в виде строки), что на мой взгляд не совсем правильно.
procedure TForm1.Button1Click(Sender: TObject);
var
Document: IXMLDocument;
Root, Node1, Node2: IXMLNode;
begin
Document := NewXMLDocument;
Document.Encoding := 'UTF-8';
Document.NodeIndentStr := ' ';
Document.Options := Document.Options + [doNodeAutoIndent];
Root := Document.AddChild('root');
Root.DeclareNamespace('', 'http://www.w3.org/2005/Atom');
Node1 := Root.AddChild('title1', 'http://www.w3.org/2005/Atom');
Node1.Text := 'child1';
Node2 := Root.AddChild('title2', 'http://www.w3.org/2005/Atom');
Node2.Text := 'child2';
ShowMessage(Document.Node.XML);
end;
|
Теперь мы получим ожидаемый результат
Проблему с установкой namespace решает создание корневого узла с помощью вызова метода GetDocBinding
(в этом случае значение параметра NamespaceURI будет определено):
procedure TForm1.Button1Click(Sender: TObject);
var
Document: IXMLDocument;
Root, Node1, Node2: IXMLNode;
begin
Document := NewXMLDocument;
Root := Document.GetDocBinding('root', TXMLNode, 'http://www.w3.org/2005/Atom') as IXMLNode;
Document.Encoding := 'UTF-8';
Document.NodeIndentStr := ' ';
Document.Options := Document.Options + [doNodeAutoIndent];
Node1 := Root.AddChild('title1');
Node1.Text := 'child1';
Node2 := Root.AddChild('title2');
Node2.Text := 'child2';
ShowMessage(Document.Node.XML);
end;
|
Используя метод GetDocBinding можно создавать дочерние узлы от самого документа, а не от корневого
узла, результат будет тем, который нам нужен:
procedure TForm1.Button1Click(Sender: TObject);
var
Document: IXMLDocument;
Root, Node1, Node2: IXMLNode;
begin
Document := NewXMLDocument;
Document.Encoding := 'UTF-8';
Document.NodeIndentStr := ' ';
Document.Options := Document.Options + [doNodeAutoIndent];
Root := Document.GetDocBinding('root', TXMLNode, 'http://www.w3.org/2005/Atom') as IXMLNode;
Node1 := Document.CreateElement('title1', Root.NamespaceURI);
Node1.Text := 'child1';
Document.DocumentElement.ChildNodes.Add(Node1);
Node2 := Document.CreateElement('title2', Root.NamespaceURI);
Node2.Text := 'child2';
Document.DocumentElement.ChildNodes.Add(Node2);
ShowMessage(Document.Node.XML);
end;
|
Проблема с xmlns решена, а использование GetDocBinding кажется единственно правильным решением
для генерации корректного xml.
|
При использовании материала - ссылка на сайт обязательна
|
|