В прошлый раз я рассматривал процедуру получения комментариев из блога WordPress с использованием XML-RPC.
Сегодня продолжим разбираться с вопросами использование структур XML-RPC в Delphi и немного "приукрасим" нашу Delphi-функцию по чтению комментариев.
Как вы помните, любая структура (struct) в XML-RPC имеет следующее содержание:
- <struct>
- <member>
<name>название</name>
- <value><int>значение</int></value>
</member>
то есть каждый элемент имеет свое название и значение. Первое, что приходит на ум в плане интерпретации этого кода в Delphi - создать динамический массив записей (record). Например таких:
1
2
3
4
5
6
7
8
9
|
type
TSimpleType = (tsInt, tsI4, tsString, tsDouble, tsDateTime, tsBase64, tsBoolean);
type
TStructElement = packed record
Name : string;
SType: TSimpleType;
Value: string;
end; |
Таким образом, мы всегда можем определить какой тип данных содержит член структуры и правильно его записать в XML-документ.
Пока остановимся на этом варианте. Функция добавления структуры в уже созданный XML-документ может быть такой:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
procedure TBlog.SetStructure(Struct: TStructArray; Document: PXMLDocument);
var i:integer;
Root,Member: IXMLNode;
begin
if (Length(Struct)=0) or(Document^.IsEmptyDoc) then Exit;
Root:=Document^.DocumentElement.ChildNodes.FindNode('params').AddChild('param').AddChild('struct');
for i:= 0 to Length(Struct) - 1 do
begin
member:=Root.AddChild('member');
member.AddChild('name').NodeValue:=Struct[i].Name;
case Struct[i].SType of
tsInt,tsI4:member.AddChild('value').AddChild('int').NodeValue:=Struct[i].Value;
tsString: member.AddChild('value').AddChild('string').NodeValue:=Struct[i].Value;
tsDouble: member.AddChild('value').AddChild('double').NodeValue:=Struct[i].Value;
tsDateTime:member.AddChild('value').AddChild('dateTime.iso8601').NodeValue:=Struct[i].Value;
tsBase64: member.AddChild('value').AddChild('base64').NodeValue:=Struct[i].Value;
tsBoolean: member.AddChild('value').AddChild('boolean').NodeValue:=Struct[i].Value;
end;
end;
end; |
Признаться, был позыв сделать запись содержащую поле типа Variant и избавиться от TSimpleType, но что-то я засомневался по поводу верной интерпретации полей типа boolean. Не будет ли в случае задание булевой переменной в виде 0-1 программа записывать в документ поле структуры с типом int?
Чтобы избежать подобных недоразумений пришлось использовать лишнее поле и на основании его записывать данные в XML-документ.
Теперь вернемся к нашей процедуре чтения комментариев. В прошлый раз мы остановились на том, что использовали в качестве одного из параметров отдельной записи record, описывающей поле filter в запросе. еперь процедуру можно немного видоизменить и включить во входные параметры наш новый тип данных.
У меня, после недолгих преобразований, получилась следующая процедура для чтения комментариев из блога WordPress:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
function TBlog.GetComments(const CommentStruct: TStructArray): TComments;
var i,j:integer;
Doc: IXMLDocument;
Values: IDOMNodeList;//все тэги value массива
Members: IDOMNodeList;//все тэги member элемента value
List: TStringList;
begin
Doc:=GetDocument('wp.getComments');
SetParam(tsInt,'1',@Doc);
SetParam(tsString,FUserName,@Doc);
SetParam(tsString,FPassword,@Doc);
SetStructure(CommentStruct,@Doc);
SendQuery(@Doc,FURL);
Values:=Doc.DOMDocument.getElementsByTagName('data').item[0].childNodes;
SetLength(Result,Values.length);
for i:= 0 to Values.length-1 do
begin
Members:=Values[i].firstChild.childNodes;//получили все members для 1 value
for j:=0 to Members.length - 1 do
begin
with Result[i]do
begin
case j of
0:Result[i].date_created_gmt:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
1:Result[i].user_id:=StrToInt((Members[j].lastChild.firstChild as IDOMNodeEx).text);
2:Result[i].comment_id:=StrToInt((Members[j].lastChild.firstChild as IDOMNodeEx).text);
3:Result[i].parent:=StrToInt((Members[j].lastChild.firstChild as IDOMNodeEx).text);
4:Result[i].status:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
5:Result[i].content:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
6:Result[i].link:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
7:Result[i].post_id:=StrToInt((Members[j].lastChild.firstChild as IDOMNodeEx).text);
8:Result[i].post_title:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
9:Result[i].author:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
10:Result[i].author_url:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
11:Result[i].author_email:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
12:Result[i].comment_type:=(Members[j].lastChild.firstChild as IDOMNodeEx).text;
end;
end;
end;
end;
end; |
Напомню, что тип TComments - это динамический массив записей вида:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
type
TComment = packed record
date_created_gmt: string;
user_id: integer;
comment_id: integer;
parent: integer;
status: string;
content: string;
link :string;
post_id: integer;
post_title: string;
author: string;
author_url: string;
author_email: string;
author_ip : string;
comment_type : string;
end; |
Двигаемся дальше. С одной стороны все вроде бы замечательно - создали переменную типа TStructArray, передали в качестве параметр процедуры и получили ответ. Но с другой стороны - что делать если в запросе должна быть структура на 10 полей? 20? Это ж замаешься для каждого поля указывать название, тип, значение. Я решил эту проблему следующим образом (пока черновой вариант). Создаем функцию для создания struct на определенный запрос. В нашем случае это wp.GetComments:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
function TBlog.GetCommentStructure(post_id: integer; status: string; number,
offset: integer): TStructArray;
begin
Result:=nil;
if post_id>-1 then
begin
Setlength(Result,length(Result)+1);
Result[length(Result)-1].Name:='post_id';
Result[length(Result)-1].SType:=tsInt;
Result[length(Result)-1].Value:=IntToStr(post_id);
end;
if length(status)>0 then
begin
Setlength(Result,length(Result)+1);
Result[length(Result)-1].Name:='status';
Result[length(Result)-1].SType:=tsString;
Result[length(Result)-1].Value:=status;
end;
if number>-1 then
begin
Setlength(Result,length(Result)+1);
Result[length(Result)-1].Name:='number';
Result[length(Result)-1].SType:=tsInt;
Result[length(Result)-1].Value:=IntToStr(number);
end;
if offset>-1 then
begin
Setlength(Result,length(Result)+1);
Result[length(Result)-1].Name:='offset';
Result[length(Result)-1].SType:=tsInt;
Result[length(Result)-1].Value:=IntToStr(offset);
end;
end; |
и уже результат этой функции используем для получения комментариев:
1
2
3
4
|
function TBlog.LoadComments(const Post_id: integer; Numder, Offset: integer; Status: string): TComments;
begin
Result:=GetComments(GetCommentStructure(Post_id,Status,Numder,Offset));
end; |
При этом, если задать параметрам типа integer значения -1, а в параметре статус передать пустую строку, то функция вернет комментарии из блога по умолчанию, т.е. последние 10 одобренных.