Problem with the serialization of a boolean value in delphi

Hi All,

I'm having this wee problem whereby a Boolean value is not being serialized correctly (to lowercase true or false - following the xml standard) for an object that inherits from TRemotable - it always capitalises that first letter of each. I've tried overriding the ObjectToSoap method of the TRemotable class - but this would mean having to iterate through each node to determine if it contains that value "True" or "False", and lowercasing it - a time consuming process, is there an easier way? Any help would be greatly appreciated.

Thanks in advance...
rowan_masseyAsked:
Who is Participating?

Improve company productivity with a Business Account.Sign Up

x
 
2266180Connect With a Mentor Commented:
you are implying that there is a very serious bug with serializing/unserializing a boolean type property in something that has been around for years. I can understand a bug in case of some weird dynamic arrays or custom variants, but not with a delphi base type, but something that a lot of people have used for a long time is highly unlikely.
I will first go with a problem in your code rather than in the soap code :) I'm just sceptical, that's all ;)

btw, the order of the serialization is the order in which the properties appear in the a-z ordering of the property list.

so the second property serialized/unserialized is DateOpened. what type is TXSDateTime? is it a class? might explain why you are getting a class there.
the 3rd is the DoNotReprice:boolean which should go as an enumeration. are you even getting there?
0
 
MerijnBSr. Software EngineerCommented:
how do you convert the boolean to string, or is this done automatically by TRemotable?
0
 
rowan_masseyAuthor Commented:
It all seems to be done by TRemotable - I'm trying to figure out where this is happening, I assumed that it would be in the ObjectToSoap function of the TRemotable class (Seems to be the most logical place for this to happen).
0
What Kind of Coding Program is Right for You?

There are many ways to learn to code these days. From coding bootcamps like Flatiron School to online courses to totally free beginner resources. The best way to learn to code depends on many factors, but the most important one is you. See what course is best for you.

 
MerijnBSr. Software EngineerCommented:
you would expect it to be there, if you step through the code, can't you see the string being build up?
0
 
rowan_masseyAuthor Commented:
Hi MerijinB,

I followed the stack trace - and eventually got to this bit of code that should get executed - but doesn't seem to, you'll notice that at the very end that it should convert the boolean type to lowercase, which again doesn't seem to (When I look at the XML that I save the Boolean value is "True" - I hate delphi). The attribute of my class is definately of type boolean...DOOM!

function TSOAPDomConv.ConvertEnumToSoap(Info: PTypeInfo;
  P: Pointer; NumIndirect: Integer): InvString;
var
  Value: Pointer;
  I: Integer;
begin
  Value := P;
  for I := 0 to NumIndirect - 2 do
    Value := Pointer(PInteger(Value)^);
  if NumIndirect = 0 then
    Result := GetEnumName(Info, Byte(Value))
  else
    Result := GetEnumName(Info, PByte(Value)^);
  { NOTE: No need to use SameTypeInfo here since C++ has proper case }
  if Info = TypeInfo(System.Boolean) then
    Result := Lowercase(Result);
end;
0
 
MerijnBSr. Software EngineerCommented:
I don't understand, is this piece executed or not?

If so, if you put a breakpoint here, can you see what's going wrong (or at least _where_ it is going wrong?)
0
 
rowan_masseyAuthor Commented:
Hi Merijin,
This code seems to be one of delphi's units...any time I try and add the directory it's in to my projects search path (in a vain attempt to debug it) it keeps telling this:

[Pascal Fatal Error] Rio.pas(136): F2051 Unit WebServExp was compiled with a different version of IntfInfo.TIntfMetaData

Scalping couldn't be as painful as this...
0
 
MerijnBSr. Software EngineerCommented:
if you enable "use debug dcu's" and just put a breakpoint there, won't it break?
0
 
2266180Commented:
I can tell you one thing: RTTI is sometimes a pain

my guess is that somewhere there is another type definition of boolean and your code uses that definition instead of system.boolean.

so, keep your mouse over that boolean declaration adn see where it goes, OR, rename it to system.boolean to be safe.

that shouild work just fine now.
0
 
rowan_masseyAuthor Commented:
Gents,

Just when I though I had it sorted...Thanks MerijnB, the use debug dcu's worked a treat - the problem is in the following bit of code - my kind value is coming up as tkClass, something which I wasn't expecting (I expected tkEnumeration) so it calls the ConvertObjectToSOAP procedure (attached below as well) - all of which leads me to nowhere I don't mind saying - there is nothing here that iterates through anything to determine if the node is boolean - DOOM! (again I appreciate the help...and patience)

procedure TSOAPDomConv.ConvertNativeDataToSoap(RootNode, Node: IXMLNode;
                const Name: InvString; Info: PTypeInfo; P: Pointer; NumIndirect: Integer);
var
  ElemNode: IXMLNode;
  TypeName: InvString;
  URI, S: InvString;
  IsNull: Boolean;
  I: Integer;
  IsScalar: Boolean;
begin
  case Info.Kind of
    tkClass:
      ConvertObjectToSOAP(Name, P, RootNode, Node, NumIndirect);
    tkDynArray:
      ConvertNativeArrayToSoap(RootNode, Node, Name, Info, P, NumIndirect);
    tkSet,
    tkMethod,
    tkArray,
    tkRecord,
    tkInterface:
      raise ESOAPDomConvertError.CreateFmt(SDataTypeNotSupported, [KindNameArray[Info.Kind]]);

    tkVariant:
    begin
      ConvertVariantToSoap(RootNode, Node, Name, Info, P, NumIndirect, NULL, False);
    end;
    else
    begin
      if Info.Kind = tkEnumeration then
      begin
        if not RemClassRegistry.InfoToURI(Info, URI, TypeName, IsScalar) then
          raise ESOAPDomConvertError.CreateFmt(SRemTypeNotRegistered, [Info.Name]);
        S := ConvertEnumToSoap(Info, P, NumIndirect);
        ElemNode := CreateScalarNodeXS(RootNode, Node, Name, URI, TypeName, S);
      end else
      begin
        if NumIndirect > 1 then
          for I := 0 to NumIndirect - 2 do
            P := Pointer(PInteger(P)^);
        TypeTranslator.CastNativeToSoap(Info, S, P, IsNull);
        if IsNull then
          CreateNULLNode(RootNode,ElemNode, Name)
        else
        begin
          if not RemTypeRegistry.TypeInfoToXSD(Info, URI, TypeName) then
            raise ESOAPDomConvertError.CreateFmt(SRemTypeNotRegistered, [Info.Name]);
          ElemNode := CreateScalarNodeXS(RootNode, Node, Name, URI, TypeName, S);
        end;
      end
    end;
  end;
end;

procedure TSOAPDomConv.ConvertObjectToSOAP(const Name: InvString;
  ObjP: Pointer; RootNode, Node: IXMLNOde; NumIndirect: Integer);
var
  ElemNode: IXMLNOde;
  I: Integer;
  ID: string;
  URI, TypeName: WideString;
  P: Pointer;
  Instance: TObject;
  MultiRef: Boolean;
begin
  P := ObjP;
  for I := 0 to NumIndirect - 1 do
    P := Pointer(PInteger(P)^);
  Instance := P;

  if Assigned(Instance) and not Instance.InheritsFrom(TRemotable) then
    raise ESOAPDomConvertError.CreateFmt(SUnsuportedClassType, [Instance.ClassName]);

  if not Assigned(Instance) then
    CreateNULLNode(RootNode, Node, Name)
  else
  begin
    { Retrieve URI of Type }
    if not RemClassRegistry.ClassToURI(Instance.ClassType, URI, TypeName) then
      raise ESOAPDomConvertError.CreateFmt(SRemTypeNotRegistered, [Instance.ClassName]);
    MultiRef := MultiRefObject(Instance.ClassType);
    { NOTE: SOAP Attachments will enter this path as they are never multirefed }
    if not MultiRef then
    begin
      if IsObjectWriting(Instance) then
        raise ESOAPDomConvertError.CreateFmt(SNoSerializeGraphs, [Instance.ClassName]);
      AddObjectAsWriting(Instance);
      { NOTE: Prefixing nodes can cause problems with some
              SOAP implementations. However, not doing so causes problems
              too ?? }
      CreateObjectNode(Instance, RootNode, Node, Name, URI, [ocoDontPrefixNode]);
      RemoveObjectAsWriting(Instance);
    end
    else
    begin
      ID := FindMultiRefNodeByInstance(Instance);
      { NOTE: Passing 'True' to prefix here can cause problems with some
              SOAP implementations. However, removing it can cause problems
              too ?? }
      if ID = '' then
        { NOTE: The ref'ed node must be of the TypeName - not the referring node name }
        ID := CreateObjectNode(Instance, RootNode, Node, TypeName, URI, []);
      ElemNode := Node.AddChild(Name, '' {No Namespace prefix});
      ElemNode.Attributes[SXMLHREF] := SHREFPre + ID;
    end;
  end;
end;
0
 
MerijnBSr. Software EngineerCommented:
I think you'll have to dig a little further

Step into the call to CreateObjectNode() and see if you find more there
0
 
2266180Commented:
I'd rather see your object declaration and also the answer to my suggestion.
and with the object declaration, mention which fields are the ones that appear to have problems at serialization/deserialization
0
 
rowan_masseyAuthor Commented:
Hi Lads,

Ciuly, below is an example of the class (Test) that I'm using (this is how it is called: webservice.CheckPrice(TestObject);) - the problem, as I stated above is that "Kind" in ConvertNativeDataToSoap is tkClass. What other than boolean could I change those types to?

MerijnB, I had gone it CreateObjectNode which does the following:
function TSOAPDomConv.CreateObjectNode(Instance: TObject; RootNode, ParentNode: IXMLNode;
                                       const Name, URI: InvString;
                                       ObjConvOpts: TObjectConvertOptions): InvString;
begin
  { Allow TRemotable_xxxx classes to perform custom serialization }
  if Assigned(Instance) and Instance.InheritsFrom(TRemotable) then
    TRemotable(Instance).ObjectToSOAP(RootNode, ParentNode, Self, Name, URI, ObjConvOpts, Result)
  else
    ObjInstanceToSOAP(Instance, RootNode, ParentNode, Name,
                      URI, ObjConvOpts, Result);
end;
The ObjectToSoap function calls the ObjConverter.ObjInstanceToSOAP which is defined in the ObjConverter Interface...

(**********************MY TEST CLASS*******************************)
Test = class(TRemotable)
  private
    FAccountId: WideString;
    FOrderQuoteId: WideString;
    FIsQuote: Boolean;
    FDateOpened: TXSDateTime;
    FStatus: WideString;
    FQuantity: TXSDecimal;
    FQuotedPrice: TXSDecimal;
    FDoNotReprice: Boolean;
  public
    destructor Destroy; override;
  published
    property AccountId: WideString read FAccountId write FAccountId;
    property OrderQuoteId: WideString read FOrderQuoteId write FOrderQuoteId;
    property IsQuote: Boolean read FIsQuote write FIsQuote;
    property DateOpened: TXSDateTime read FDateOpened write FDateOpened;
    property Status: WideString read FStatus write FStatus;
    property Quantity: TXSDecimal read FQuantity write FQuantity;
    property QuotedPrice: TXSDecimal read FQuotedPrice write FQuotedPrice;
    property DoNotReprice: Boolean read FDoNotReprice write FDoNotReprice;
  end;
0
 
MerijnBSr. Software EngineerCommented:
> The ObjectToSoap function calls the ObjConverter.ObjInstanceToSOAP which is defined in the ObjConverter Interface...

than ObjInstanceToSOAP is probably where the problem lies
0
 
MerijnBConnect With a Mentor Sr. Software EngineerCommented:
I'm hoping to guide rowan_massey to the place where things go wrong.
I don't expect he'll find the actual problem there, but I do hope that he finds a trigger to the place in his own code ;)
0
 
rowan_masseyAuthor Commented:
Lads,
You both hit the nail on the head...I'm a spa! I was overriding the ObjectToSoap in a derived class of TRemotable and Test was derived from this class. What I had in the overrided ObjectToSoap was the following:
Result := inherited ObjectToSoap(RootNode, ParentNode, ObjConverter, Name, URI, ObjConvOpts+ocoDontSerializeProps, RefID);

The ocoDontSerializeProps doesn't "Serialise the properties" well well, how nice (I know, I know). The values are still getting written as "True" and "False" - but I suspect that that could be because of the convertion to string....I don't suppose any of you know any XML sniffer tool that would be quite handy?
0
 
2266180Commented:
xml sniffer? no. ethernet sniffer, yes. and a quite good one too (and free): ethereal http://www.ethereal.com/
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.