In the new application that we are creating now, we have a lot SOAP requests(now more than 50 different). To abstract away the creation of queries as much as possible, we have added an abstract class named TRequestMessageParserto delegate the construction of soap queries.
This abstract class receives a list of parameters and has a method SetParameterValuesto populate the corresponding one SOAP requestwith a new one RTTI. It creates and populates object parameters, array parameters and other complex structures of a given request. Then we create derived classes, attached to the specific request type, generated WSDL importer. These derived classes do only two things:
- fulfill the request.
- a challenge
SetParameterValues.
Now it works great (or seems). The request is created, and if you debug it, you can see that all the properties specified in the parameters are set regardless of whether they are ordinal types, instances or dynamic arrays.
The problem occurs when the request is parsed on XML text. When will this happen dynamic array properties are never set. We could confirm this using the event handler OnBeforeExecute THTTPRIOthat we assigned to the service shell. An error or exception is not thrown. The properties of a dynamic array are simply ignored.
If we create the request manually, i.e. we create and install each object, array and property specifically, then the request (which seems to be the same as RTTI) is correctly processed by XML-text.
, - , RTTI, , Google , , .
TRequestMessageParser:
TRequestMessageParser<REQ: TRemotable> = class
protected
FRequest : REQ;
<snip rest of declaration>
procedure TRequestMessageParser<REQ>.SetParameterValues(Parameters: TObjectList<TRequestParameter>);
begin
SetParameterValues(FRequest, Parameters);
end;
procedure TRequestMessageParser<REQ>.SetParameterValues(parentObject: TObject; ParameterList : TObjectList<TRequestParameter>);
var
parameter : TRequestParameter;
requestPropertyRttiType : TRttiType;
requestProperty : TRttiProperty;
booleanValue : boolean;
begin
for parameter in ParameterList do
begin
if parameter.IsComplexType then
begin
requestProperty := context.GetType(parentObject.ClassType).GetProperty(parameter.Code);
requestPropertyRttiType := requestProperty.PropertyType;
case requestPropertyRttiType.TypeKind of
tkClass: ManageObjectProperty(parentObject, requestPropertyRttiType, parameter);
tkDynArray: ManageDynamicArrayProperty(parentObject, parameter);
else
raise Exception.Create('Unsupported type for requests.');
end;
end
else
begin
requestProperty := context.GetType(parentObject.ClassType).GetProperty(parameter.Code);
if requestProperty.PropertyType.TypeKind = tkEnumeration then
begin
if (requestProperty.PropertyType as TRttiEnumerationType).UnderlyingType.Handle = System.TypeInfo(Boolean) then
begin
booleanValue := parameter.Value;
requestProperty.SetValue(parentObject, TValue.From(booleanValue));
end
else
requestProperty.SetValue(parentObject, TValue.FromVariant(parameter.Value));
end
else
requestProperty.SetValue(parentObject, TValue.FromVariant(parameter.Value));
end;
end;
end;
procedure TRequestMessageParser<REQ>.ManageObjectProperty(parentObject: TObject; requestPropertyRttiType : TRttiType; parameter : TRequestParameter);
var
requestPropertyInstance : TObject;
requestProperty : TRttiProperty;
begin
requestPropertyInstance := requestPropertyRttiType.AsInstance.MetaclassType.Create;
requestProperty := context.GetType(parentObject.ClassType).GetProperty(parameter.Code);
requestProperty.SetValue(parentObject, requestPropertyInstance);
SetParameterValues(requestPropertyInstance, parameter.Subparameters);
end;
procedure TRequestMessageParser<REQ>.ManageDynamicArrayProperty(parentObject: TObject; parameter : TRequestParameter);
var
parentType : trttiType;
objectProperty : TRttiProperty;
DynArrayType: TRttiDynamicArrayType;
DynArrElementType: TRttiType;
newArrayValue : TValue;
parentObjectArrayValue : TValue;
ArrayLength : LongInt;
i : integer;
begin
parentType := context.GetType(parentObject.ClassInfo);
objectProperty := parentType.GetProperty(parameter.Code);
DynArrayType := (objectProperty.PropertyType as TRttiDynamicArrayType);
newArrayValue := objectProperty.GetValue(parentObject);
arrayLength := parameter.Subparameters.Count;
DynArraySetLength(PPointer(newArrayValue.GetReferenceToRawData)^, newArrayValue.TypeInfo, 1, @arrayLength);
DynArrElementType := DynArrayType.ElementType;
if DynArrElementType.IsInstance then
begin
for i := 0 to ArrayLength - 1 do
AddObjectElementToDynamicArray(newArrayValue, i, DynArrElementType, parameter.Subparameters[i]);
end
else if DynArrElementType.IsOrdinal then
begin
for i := 0 to ArrayLength - 1 do
newArrayValue.SetArrayElement(i, TValue.FromVariant(parameter.Subparameters[i].Value));
end
else
raise Exception.Create('Unsupported');
TValue.MakeWithoutCopy(newArrayValue.GetReferenceToRawData, DynArrayType.Handle, parentObjectArrayValue);
objectProperty.SetValue(parentObject, parentObjectArrayValue);
end;
procedure TRequestMessageParser<REQ>.AddObjectElementToDynamicArray(DynamicArray : TValue; position: integer; DynamicArrayElementType: TRttiType; objectElementParameter: TRequestParameter);
var
ElementValue : TValue;
objectSubparameter : TRequestParameter;
begin
ElementValue := DynamicArrayElementType.GetMethod('Create').Invoke(DynamicArrayElementType.AsInstance.MetaclassType, []);
SetParameterValues(ElementValue.AsObject, objectElementParameter.Subparameters);
DynamicArray.SetArrayElement(position, ElementValue);
end;
TRequestParameter - , Code, a Value a list of subparameters ( TObjectList), read. Id, , .
Delphi XE5 . - , , !