当前位置 : 主页 > 编程语言 > delphi >

delphi – 使用RTTI创建的SOAP请求未完全解析为XML

来源:互联网 收集:自由互联 发布时间:2021-06-23
在我们现在正在创建的新应用程序中,我们有很多SOAP请求(现在很容易超过50个不同的请求).为了尽可能抽象地创建请求,我们添加了一个名为TRequestMessageParser的抽象类来委托构造soap请求
在我们现在正在创建的新应用程序中,我们有很多SOAP请求(现在很容易超过50个不同的请求).为了尽可能抽象地创建请求,我们添加了一个名为TRequestMessageParser的抽象类来委托构造soap请求.

此抽象类接收参数列表,并具有SetParameterValues方法,以使用新RTTI填充相应的SOAP请求.它创建并填充给定请求的对象参数,数组参数和其他复杂结构.然后,我们创建与WSDL导入器生成的特定请求类型相关联的派生类.这些派生类只做两件事:

>实例化请求.
>调用SetParameterValues.

现在,这工作正常(或似乎).创建请求,如果您调试它,您可以看到参数中指定的所有属性都已设置,无论它们是序数类型,实例还是动态数组.

将请求解析为XML文本时会出现问题.发生这种情况时,永远不会设置动态数组属性.我们可以通过使用我们分配给服务包装器的THTTPRIO的OnBeforeExecute事件处理程序来确认这一点.不会抛出任何错误或异常.简单地忽略动态数组属性.

如果我们手动创建请求,即专门创建和设置每个对象,数组和属性,那么请求(看起来与RTTI相同)会被正确解析为XML文本.

很明显,当我们使用RTTI创建请求时,我们必须做错事,尽管尽管调试和谷歌搜索转换错误我们无法找到它是什么.

您将在下面找到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
  //context is initialized in constructor
  for parameter in ParameterList do
  begin

    if parameter.IsComplexType then //true if it has > 1 subparameter (object or array)
    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
    //ordinal types
    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
        //TODO: probably not necessary as SOAP request have no enumerations so far
        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;

  //we add the instance to the parent object
  requestProperty := context.GetType(parentObject.ClassType).GetProperty(parameter.Code);
  requestProperty.SetValue(parentObject, requestPropertyInstance);

  //we assign the parameters corresponding to the instance
  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
  //we retrive rtti information for the property
  parentType := context.GetType(parentObject.ClassInfo);
  objectProperty := parentType.GetProperty(parameter.Code);
  DynArrayType := (objectProperty.PropertyType as TRttiDynamicArrayType);

  //we retrieve a reference to the property as TValue
  newArrayValue := objectProperty.GetValue(parentObject);

  //we get and set the dynamic array length
  arrayLength := parameter.Subparameters.Count;
  DynArraySetLength(PPointer(newArrayValue.GetReferenceToRawData)^, newArrayValue.TypeInfo, 1, @arrayLength);

  //we retrieve the array element type
  DynArrElementType := DynArrayType.ElementType;

  //if it is an object we create the corresponding instances
  if DynArrElementType.IsInstance then
  begin
    for i := 0 to ArrayLength - 1 do
      AddObjectElementToDynamicArray(newArrayValue, i, DynArrElementType, parameter.Subparameters[i]);
  end
  //if it is an ordinal element we assign the value
  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');


  //until now we have a copy of the dynamic array so we reassign it to the property
  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,一个Value和一个子参数列表(一个通用的TObjectList),所有这些都可以通过read属性访问.我需要看到它我也可以添加它的代码.

我们正在使用Delphi XE5来创建应用程序.如果有人能给我们至少一个关于我们做错事的领导,那就太好了!

正如@J …提出的问题是(不知何故)动态数组在发送请求时超出了范围.

为了解决这个问题,我们在创建请求之后但在将其发送到服务之前,为请求的每个动态数组属性分配了相同动态数组的副本.复制在发送请求之前完成,如下所示:

Foo := TFooRequestMessageParser.getRequest;
//for each dynamic array property get a copy. This applies also to subproperties
Foo.DynArrayProperty := Copy(Foo.DynArrayProperty);
fooService.SendRequest(foo);

另一种可能性是做@J …也建议,即手动增加引用计数以避免动态数组被释放.这可能是一种更明智,更快捷的方法,但现在我们将坚持我们的解决方案.

网友评论