В прошлый раз я рассматривал процедуру получения комментариев из блога WordPress с использованием XML-RPC.
Сегодня продолжим разбираться с вопросами использование структур XML-RPC в Delphi и немного «приукрасим» нашу Delphi-функцию по чтению комментариев.
Как вы помните, любая структура (struct) в XML-RPC имеет следующее содержание:
- <struct> - <member> <name>название</name> - <value><int>значение</int></value> </member>
то есть каждый элемент имеет свое название и значение. Первое, что приходит на ум в плане интерпретации этого кода в Delphi — создать динамический массив записей (record). Например таких:
type TSimpleType = (tsInt, tsI4, tsString, tsDouble, tsDateTime, tsBase64, tsBoolean); type TStructElement = packed record Name : string; SType: TSimpleType; Value: string; end;
Таким образом, мы всегда можем определить какой тип данных содержит член структуры и правильно его записать в XML-документ.
Пока остановимся на этом варианте. Функция добавления структуры в уже созданный XML-документ может быть такой:
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:
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 — это динамический массив записей вида:
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:
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;
и уже результат этой функции используем для получения комментариев:
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 одобренных.