I am attempting to write a generic messaging passing system for Delphi and .NET. The system allows messages to be defined as plain objects and message handlers are defined as anonymous methods that act on those objects.
The objects are converted to JSON and passed between applications running on the same machine. Each application maintains a list of handlers that understand specific message types.
My class has a number of parameterized registration methods. Some take a single type parameter. Some take a pair of parameters where one represents a request object and the other a response object. Here is what the Request/Response handler registration looks like:
procedure TTelegraph.RegisterRequestHandler<TRequest, TResponse>
(requestTypeToHandle: string; handler: TFunc<TRequest, TResponse>);
begin
FRequestHandlers.Add(requestTypeToHandle,
TRequestHandler<TRequest, TResponse>.Create(handler,
TRequest,
TResponse));
end;
FRequestHandlers
is a TDictionary<string,TRequestHandler>
. The registration method is called like so:
FTelegraph.RegisterRequestHandler<TTestRequest, TTestResponse>('My Request',
function(x: TTestRequest): TTestResponse
begin
Result := TTestResponse.Create;
Result.Number := x.Number;
Result.Message := Format('Received: %s', [x.Message]);
end);
The generic TRequestHandler<T1,T2>
is a DTO that wraps the handler along with the types TRequest
and TResponse
. It inherits from the non-generic TRequestHandler
. I'm not sure if there's a better way to go about this but it was the only way I could think of to store multiple unrelated types in a single collection.
This all seems to work fine. The problem is when a request message is received. The C# code to handle request messages looks like this:
private void ProcessRequestTelegram(Telegram request)
{
var requestType = _RequestHandlers[request.MessageType].RequestType;
var typedRequest = JsonConvert.DeserializeObject(request.Payload, requestType);
var result = _RequestHandlers[request.MessageType].Handler.DynamicInvoke(typedRequest);
var jsonPayload = JsonConvert.SerializeObject(result);
var response = new Telegram()
{
Payload = jsonPayload,
CorrelationID = request.CorrelationID,
Template = TelegramTemplateEnum.Response,
SenderWindowHandle = _LocalWindowHandle.ToInt32(),
RecipientWindowHandle = _MessageRecipient.ToInt32(),
MessageType = request.MessageType
};
var jsonResponse = JsonConvert.SerializeObject(response);
var resultCode = _Messaging.SendMessage(_MessageRecipient, jsonResponse);
CheckIfSendMessageErrorOccurred(resultCode);
}
In the code above RequestType
is of the Type
type.
But for the life of me I can't come up with the Delphi equivalent. I get as far as attempting to deserialize the request.Payload and I'm stuck at how to pass the JSON parser the type to convert the payload into. I've tried various ways of storing TRequest
and TResponse
in the RequestType and ResponseType properties of TRequestHandler
: TTypeInfo
, TRTTIType
and currently TClass
. But nothing seems to give me anything useful to pass to TJson.JsonToObject
. It has a generic overload that takes a type parameter for the return type but apparently you can't use a TClass
as a type parameter.
You cannot use TJson.JsonToObject<T>
as at the time of calling this you don't have a generic T. Look into the code of this method and you see that T is being passed to TJSONUnMarshal.CreateObject
. So in your case you should store the TClass
of TRequest
and TResponse
and write your own version of TJson.JsonToObject
that gets passed a TClass
.
Another way would be to create your instance first and then pass it to the non generic TJson.JsonToObject
which takes the ClassType
of the passed instance and passes it to TJSONUnMarshal.CreateObject
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With