The Morfik XML-RPC package allows Morfik developers to integrate their web applications with web servers that expose xml-rpc functions. Browser-side xml-rpc functions can be consumed using the Morfik XML-RPC package.


package-view.png
package-down.png


Getting started

To use the Morfik XML-RPC package simply add the package to the project by utilizing the “Used Packages” command on the project ribbon (see Figure 1 Used Packages Command), or simply drag the package file onto the application main client area.


security-fig1.png
Figure 1: Used Packages Command


First Things First

Before delving into the package, take a look at the Xml-Rpc website and the documentation for the Xml-Rpc specification. When you examine the type of xml values that can be utilized in xml-rpc calls, you can see the standard allows for Boolean, string, double, dateTime, base64, struct and array data types. The struct type, which contains any number of members each of which contain a name-value pair, is encapsulated in the TXmlRpcStruct class and the array type, which contains a single data member that can have any number of value members, in the standard TArray class. The value properties of arrays and structs may also be arrays or structs.

The two other most important classes for the browser side are the TXmlRpcClient and TXmlRpcRequest classes. We examine a couple of ways to use these classes below.

A Quick Walkthrough – Sample 1

We will generate an interface to the LiveJournal Login xml-rpc API which you can find described here. (Note that there are no wsdl files associated with rpc-xml services as there are with SOAP services.) The input parameter and the result are both of type struct so we will be using the TXmlRpcStruct class in the code:

FX Code

Procedure Form1.Button1Click(Event: TDOMEvent);
Var
    Client  : TXmlRpcClient;
    Request : TXmlRpcRequest;
    RStruct : TXmlRpcStruct;
Begin
    Client := TXmlRpcClient.Create('www.livejournal.com', '/interface/xmlrpc');
    Request := Client.CreateRequest('LJ.XMLRPC.login');
    RStruct := TXmlRpcStruct.Create;
    RStruct.SetField('username', 'YourUserName');
    RStruct.SetField('password', 'YourPassword');
    RStruct.SetField('ver', 1);
    Request.AddStruct(RStruct);
    Request.Send(GetMethodPointer(Self, @ResponseHandler), GetMethodPointer(Self, @ErrorHandler))
End;


When a TXmlRpcClient is created, you need to specify two parameters: a URI for the server and a refinement path to the service itself. The TXmlRpcRequest is created with a passed parameter specifying the method to be called on the server. Notice the use of the SetField method to assign values to the elements of the struct, and the call to AddStruct to include the struct in the request.

The Send method takes two method pointer parameters: one to be called for a successful request and a one for an unsuccessful request. Both methods have the same signature:

FX Code

Procedure Form1.Handler(Data : TXmlRpcStruct);


Here is some sample code for each of the response handlers:

FX Code

Procedure mfk_TestXmlRpc.ErrorHandler(Data : TXmlRpcStruct);
Begin;
    ResponseLabel.Caption := 'Error: ' + String(Data.Value('message'));
End;
Function Form1.GenerateHTML(Data : MetaType) : String;
Var
    I          : Integer;
    ParamNames : TArray;
    Text       : String;
    Tc         : TXmlRpcStruct;
Begin
    If TypeOf(Data) <> 'object' Then
    Begin
        Result := '<div>' + TString(Data).replace(#13, '<br>') + '</div>';
        Exit;
    End;
    Text := '<ul>';
    Tc := TXmlRpcStruct.Create(Data);
    ParamNames := Tc.Keys;
    For I := 0 To ParamNames.length - 1 Do
    Begin
         Tc := TXmlRpcStruct.Create(TArray(Data)[ParamNames[I]]);
         If Tc.TypeName = 'object' Then
             Text := Text + '<li>' + String(ParamNames[I]) + '<br/>' +
                     GenerateHTML(TArray(Data)[ParamNames[I]]) + '</li>'
         Else
             Text := Text + '<li>' + String(ParamNames[I]) + ': ' +
                     String(TArray(Data)[ParamNames[I]]) + '</li>';
    End;
    Result := Text + '</ul>';
End;
Procedure Form1.ResponseHandler(Data : TXmlRpcStruct);
Begin
    ResponseLabel.Caption := GenerateHTML(Data);
End;


A couple of things to note here are 1) the TXmlRpcStruct Value function returns a value of type MetaType (similar to the Delphi variant type) which must be typecast to be useful and 2) a TXmlRpcStruct can be created using an existing struct passed into the Create method as a parameter, which helps in handling values that are themselves structs.

A Quick Walkthrough – Sample 2

In this sample we are going to encapsulate the xml-rpc functionality in another class which will be used for barcode lookup through the [www.upcdatabase.com Internet UPC Database]. The responses from the xml-rpc function calls contain an array of fields that are detailed in the TUpcData class seen below.

After adding mfk_xrpClient to a new browser module, create the following classes:

FX Code

TUpcData = Class(TObject)
  Private
    fFound             : Boolean;
    fUPC               : String;
    fEAN               : String;
    fDescription       : String;
    fMessage           : String;
    . . .
  Public
    Constructor Create(Data : TJObject);
    Property Found              : Boolean Read fFound;
    Property UPC                : String  Read fUPC;
    Property EAN                : String  Read fEAN;
    Property Description        : String  Read fDescription;
    Property Msg                : String  Read fMessage;
    . . .
End;

Notice that the TUpcClient class below is descended from the TXmlRpcClient class.

FX Code

TUpcClient = Class(TXmlRpcClient)
  Private
    Procedure CommonRequest(Method : String; Code : String; Callback : Pointer);
  Public
    Constructor Create;
    Procedure LookupUPC(Code : String; Callback : Pointer);
    Procedure LookupEAN(Code : String; Callback : Pointer);
    . . .
End;


Because the response from the Rpc call is an array, the new TUpcData method takes the Data parameter and casts it to an array before extracting the field values as can be seen in the code below (in the implementation section):

FX Code

Type
    UpcCallback = Procedure (Data: TUpcData);
 
Constructor TUpcData.Create(Data : TJObject);
Var
    Arr : TArray;
Begin
    Arr := TArray(Data);
    fFound             := Arr['found'];
    fUPC               := Arr['upc'];
    fEAN               := Arr['ean'];
    fDescription       := Arr['description'];
    fMessage           := Arr['message'];
    . . .
End;


The Create method should look familiar to you from the code in the last section.

FX Code

Constructor TUpcClient.Create;
Begin
    Inherited Create('www.upcdatabase.com', '/rpc');
End;


This method calls the correct xml-rpc service on the server and sets the callback.

FX Code

Procedure TUpcClient.CommonRequest(Method : String; Code : String; Callback : Pointer);
Var
    Request : TXmlRpcRequest;
Begin
    Request := CreateRequest(Method);
    Request.AddString(Code);
    Request.Send(Callback, Callback);
End;


This method calls the xml-rpc function to look up a UPC code and processes the result in the nested _Parse function which determines how to callback to the original function based upon the type of response. The originating methods are discussed later.

FX Code

Procedure TUpcClient.LookupUPC(Code : String; Callback : Pointer);
    Procedure _Parse(Data : TJObject);
    Begin
        If TypeOf(Data) = 'string' Then
            UpcCallback(Callback)(TUpcData(Data))
        Else If Boolean(TArray(Data)['error']) = True Then
            UpcCallback(Callback)(TUpcData(Data))
        Else
            UpcCallback(Callback)(TUpcData.Create(Data));
    End;
Begin
    CommonRequest('lookupUPC', Code, @_Parse);
End;


To use the classes discussed above, you need code similar to that below. The TUpcData response returned is typecast to an array to check for an error response and if no error is found a display label is formatted from the values returned from the TRpcData.Create call.

FX Code

Var
    Client  : TUpcClient;
Procedure Form1.Handler(Data : TUpcData);
Var
    Msg : String;
Begin
    Msg := '<ul>';
    If Boolean(TArray(Data)['error']) = True Then
        Msg := 'Error: ' + String(TArray(Data)['message'])
    Else
    If Data.Description <> Nil Then
    Begin
        Msg := Msg + '<li>' + Data.Description + '</li>';
        Msg := Msg + '<li>' + Data.IssuerCountry + '</li>';
        Msg := Msg + '<li>' + Data.Size + '</li>';
        Msg := Msg + '</ul>';
    End
    Else
        Msg := String(Data);
    ResponseLabel.Caption := Msg;
End;
Procedure Form1.Button1Click(Event: TDOMEvent);
Begin
    Client.LookupUPC(TextEdit1.Text, GetMethodPointer(Self, Handler));
End;
Initialization
    Client := TUpcClient.Create;
End.


See Also

Back to top