#1 2013-11-06 16:42:55

jvillasantegomez
Member
From: Ciudad Habana. Cuba.
Registered: 2013-10-21
Posts: 54

How to create a custom json wrapper

Ok

Mormot seems like a nice solution at first when you have a large codebase in delphi for evolving that codebase to a restfull representation, but sometimes i can't even find an easy way of doing things.

Here is my question: How can i create a custom json wrapper and return that from an interface based service??

For example, i want that all my methods in an interface based service to return stuff like this

A successful response:
{
  "code":200,
  "status":"success",
  "data": "some data from the service"
}

An error response:
{
  "code":500,
  "status":"error",
  "developerMessage":"description of the problem for the app developer",
  "userMessage": "Pass this message to the app user",
  "morInfo": "some url to get more info"
}

This is pretty normal when using php or any other server side technology, but, how can i make this kind of wrapped response to work with mormot?

Offline

#2 2013-11-06 16:48:47

jvillasantegomez
Member
From: Ciudad Habana. Cuba.
Registered: 2013-10-21
Posts: 54

Re: How to create a custom json wrapper

Here is what I thinking:

TRespond = class
  private
    fData: RawUTF8;
    fMessage: RawUTF8;
  published
    property Data: RawUTF8 read fData write fData;
    property Message: RawUTF8 read fMessagewrite fMessage;
  end;

Then on any of my services:

function TService.GetById(id: RawUTF8): RawJSON;
var
  repository: TRepository;
  data: RawUTF8;
  respond: TRespond;
begin
  repository := TRepository.Create(fProps);
  data := repository.GetById(id);

  respond := TRespond.Create;
  respond.fData := data;
  respond.fMessage := 'some message';
  Result := ObjectToJson(respond);
end;

But sadly it doesn't work. Also, how could i get a handle to any errors for a custom json error response. I think it would be a good idea to describe on the docs the request/response cycle of an interface based service in mormot.

any suggestions???

Last edited by jvillasantegomez (2013-11-06 18:52:26)

Offline

#3 2013-11-06 19:20:56

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: How to create a custom json wrapper

AFAIR the request/response cycle of an interface based service is documented in the SAD pdf (ensure you get the latest 1.18 revision).

RawJSON is not the solution here.
There will be a '{"result":[..]}' default format.

But if you define TServiceCustomAnswer as function result, you have at hand the returned content with any format.
By default, you can then set your raw JSON in result.Content:

function TService.GetById(id: RawUTF8): TServiceCustomAnswer;
var
  repository: TRepository;
  data: RawUTF8;
  respond: TRespond;
begin
  repository := TRepository.Create(fProps);
  data := repository.GetById(id);
  /// put your resulting JSON in result.Content

  respond := TRespond.Create;
  respond.fData := data;
  respond.fMessage := 'some message';
  Result.content := ObjectToJson(respond);
  respond.Free; // do not forget to release memory!
end;

Offline

#4 2013-11-06 20:24:22

jvillasantegomez
Member
From: Ciudad Habana. Cuba.
Registered: 2013-10-21
Posts: 54

Re: How to create a custom json wrapper

It dosn't work

I defined this class:

TResponse = class
  public
    fData, fMensaje: RawUTF8;
  published
    property Data: RawUTF8 read fData write fData;
    property Mensaje: RawUTF8 read fMensaje write fMensaje;
  end;

Then in my service i did this:

begin
  repository := TRepository.Create(fProps);
  data := repository.GetById(id);  // This returns data according to Result := stmt.FetchAllAsJSON(true);

  response := TResponse.Create;
  response.Data := data;
  response.Mensaje := 'some message';
  Result.Content := ObjectToJson(response);
  response.Free; // do not forget to release memory!
end;

After that here's the json i get back:

{"result":["￰AAJ7fQ=="],"id":25443627}

Some unreadeable json when i'm expecting some result set from the database call in response.Data and the message i set in response.Mensaje.
Of course, I defined the service with a return type of TServiceCustomAnswer.

Last edited by jvillasantegomez (2013-11-06 20:25:17)

Offline

#5 2013-11-06 20:42:32

jvillasantegomez
Member
From: Ciudad Habana. Cuba.
Registered: 2013-10-21
Posts: 54

Re: How to create a custom json wrapper

Now, If I add to my services this line:

Result.Header := JSON_CONTENT_TYPE_HEADER;

then here's the result i'm getting:

{}

Yep, an empty json... what's going on here??

Offline

#6 2013-11-06 21:28:01

jvillasantegomez
Member
From: Ciudad Habana. Cuba.
Registered: 2013-10-21
Posts: 54

Re: How to create a custom json wrapper

Well, I've found a solution to this problem

It seems that the custom object must be declared between {$M+} ... {$M-} to have RTTI information at serialization time. Here's the declaration of the TResponse custom object for it to work:

{$M+}
  TResponse = class
  private
    fData, fMensaje: RawUTF8;
  published
    property Data: RawUTF8 read fData write fData;
    property Mensaje: RawUTF8 read fMensaje write fMensaje;
  end;
{$M-}

With that i can get the json with javascript, but i have to do a JSON.parse(...) for it to work, with the default it worked out of the box. Anyways, in the jquery success callback you can easily do this:

success: function(data, textStatus, jqXHR) {
  var m = JSON.parse(data.Mensaje);
  var d = JSON.parse(data.Data);
  // here m equals 'some message' and d equals the result of the db query...
}

I have lost the id passed to the client, but since i'm using a browser (javascript) as the client i guess it is not a big deal, as far as i know sicClientDriven is just used when you have a Delphi client for your mormot server.

Now I'm wondering about the performance hit of this kind of response, I'm thinking about make this kind of wrapped response the default for all my services, does it has an impact on performance this kind of Object to JSON serialization for every request??

Last edited by jvillasantegomez (2013-11-06 21:34:42)

Offline

#7 2013-11-07 05:21:59

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: How to create a custom json wrapper

If you use a JS client, the best for performance is to use the default format.
It is valid JSON content, ready to be parsed by the browser:

{"result":[{"Real":-18,"Imaginary":-27}],"id":0}

I will soon add optional return of data as a JSON object instead of an array:

{"result":{"Real":-18,"Imaginary":-27},"id":0}

(this it is needed for upcoming JS support on server side)

If you want the best performance for returning any JSON content, you can use directly TTextWriter or the following function if a JSON object with simple key/value pairs is enough for you:

/// encode the supplied data as an UTF-8 valid JSON object content
// - data must be supplied two by two, as Name,Value pairs, e.g.
// ! JSONEncode(['name','John','year',1972]) = '{"name":"John","year":1972}'
// - note that cardinal values should be type-casted to Int64() (otherwise
// the integer mapped value will be transmitted, therefore wrongly)
function JSONEncode(const NameValuePairs: array of const): RawUTF8;

But in practice, ObjectToJSON() would be very fast (no comparison with the official JSON library), only a bit slower than the function above.

Offline

#8 2013-11-07 13:37:46

jvillasantegomez
Member
From: Ciudad Habana. Cuba.
Registered: 2013-10-21
Posts: 54

Re: How to create a custom json wrapper

Thks for your reply

I will end up using

/// encode the supplied data as an UTF-8 valid JSON object content
// - data must be supplied two by two, as Name,Value pairs, e.g.
// ! JSONEncode(['name','John','year',1972]) = '{"name":"John","year":1972}'
// - note that cardinal values should be type-casted to Int64() (otherwise
// the integer mapped value will be transmitted, therefore wrongly)
function JSONEncode(const NameValuePairs: array of const): RawUTF8;

It seems to be very fast and good enough for what i'm trying to do.

Offline

#9 2013-11-07 14:21:05

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: How to create a custom json wrapper

I just added an option to return the interface-based services content as a JSON object, instead of an array.
See http://synopse.info/fossil/info/baf1521dc9

It may help you.

Offline

#10 2013-11-07 17:54:36

jvillasantegomez
Member
From: Ciudad Habana. Cuba.
Registered: 2013-10-21
Posts: 54

Re: How to create a custom json wrapper

How would i use this new functionality??

Last edited by jvillasantegomez (2013-11-12 14:55:04)

Offline

Board footer

Powered by FluxBB