Delphi SOAP client not seeing arrays

Hi,

I have a problem with a SOAP client that I am hoping someone can throw some light on.
I am calling a particular SOAP function which returns a response like the example below:

  <?xml version="1.0" encoding="utf-8" ?> 
- <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  - <soap:Header>
      <wsa:Action>http://www.GlobalBettingExchange.com/ExternalAPI/GetPricesResponse</wsa:Action> 
      <wsa:MessageID>uuid:1dfca0ad-52bb-417e-a414-070d7ef26034</wsa:MessageID> 
      <wsa:RelatesTo>uuid:bd3b2616-35ac-4565-bb29-3dab7f4ea4dd</wsa:RelatesTo> 
      <wsa:To>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:To> 
    - <wsse:Security>
      - <wsu:Timestamp wsu:Id="Timestamp-569abf0c-4e96-4645-8280-0eeaa57f761b">
          <wsu:Created>2011-07-08T11:49:25Z</wsu:Created> 
          <wsu:Expires>2011-07-08T11:54:25Z</wsu:Expires> 
        </wsu:Timestamp>
      </wsse:Security>
    </soap:Header>
  - <soap:Body>
    - <GetPricesResponse xmlns="http://www.GlobalBettingExchange.com/ExternalAPI/">
      - <GetPricesResult>
          <ReturnStatus Code="0" Description="Success" CallId="072fe273-4662-4a91-af1a-2ca902eaf48a" /> 
          <Timestamp>2011-07-08T12:49:25.9326353+01:00</Timestamp> 
        - <MarketPrices Id="2213313" Name="Match Odds" Type="3" IsPlayMarket="false" Status="2" NumberOfWinningSelections="1" StartTime="2011-08-13T15:00:00.0000000+01:00" WithdrawalSequenceNumber="0" DisplayOrder="1" IsEnabledForMultiples="false" IsInRunningAllowed="false" IsManagedWhenInRunning="false" IsCurrentlyInRunning="false" InRunningDelaySeconds="0" TotalMatchedAmount="25">
          - <Selections Id="11861507" Name="Stoke City" Status="2" ResetCount="0" DeductionFactor="0.00">
              <ForSidePrices Price="6" Stake="9.00" /> 
              <ForSidePrices Price="5.2" Stake="212.00" /> 
              <ForSidePrices Price="4.8" Stake="172.00" /> 
              <AgainstSidePrices Price="6.6" Stake="178.18" /> 
              <AgainstSidePrices Price="6.8" Stake="18.11" /> 
              <AgainstSidePrices Price="7.6" Stake="17.38" /> 
            </Selections>
          - <Selections Id="11861508" Name="Chelsea" Status="2" ResetCount="0" DeductionFactor="0.00">
              <ForSidePrices Price="1.68" Stake="35.00" /> 
              <ForSidePrices Price="1.6" Stake="400.00" /> 
              <ForSidePrices Price="1.56" Stake="273.00" /> 
              <AgainstSidePrices Price="1.88" Stake="276.00" /> 
              <AgainstSidePrices Price="1.94" Stake="6.00" /> 
              <AgainstSidePrices Price="1.98" Stake="73.00" /> 
            </Selections>
          - <Selections Id="11861509" Name="Draw" Status="2" ResetCount="0" DeductionFactor="0.00">
              <ForSidePrices Price="3.75" Stake="13.00" /> 
              <ForSidePrices Price="3.6" Stake="200.00" /> 
              <ForSidePrices Price="3.45" Stake="221.00" /> 
              <AgainstSidePrices Price="4.2" Stake="402.00" /> 
              <AgainstSidePrices Price="4.6" Stake="32.30" /> 
              <AgainstSidePrices Price="4.8" Stake="41.45" /> 
            </Selections>
          </MarketPrices>
        </GetPricesResult>
      </GetPricesResponse>
    </soap:Body>
  </soap:Envelope>


The specific problem is that it is seeing the <Selections> as an array but not <ForSidePrices> or <AgainstSidePrices>. Is there a limit to the number of array levels it can handle?
The data structure for MarketPrices is defined as:


  GetPricesResponse = class(BaseResponse)
  private
    FMarketPrices: MarketTypeWithPrices;
  public
    destructor Destroy; override;
  published
    property MarketPrices: MarketTypeWithPrices  read FMarketPrices write FMarketPrices;
  end;


  MarketTypeWithPrices = class(TRemotable)
  private
    FId: Int64;
    FName_: WideString;
    FType_: Smallint;
    FIsPlayMarket: Boolean;
    FStatus: Smallint;
    FNumberOfWinningSelections: Smallint;
    FStartTime: TXSDateTime;
    FWithdrawalSequenceNumber: Smallint;
    FDisplayOrder: Smallint;
    FIsEnabledForMultiples: Boolean;
    FIsInRunningAllowed: Boolean;
    FIsManagedWhenInRunning: Boolean;
    FIsCurrentlyInRunning: Boolean;
    FInRunningDelaySeconds: Integer;
    FReturnCode: Integer;
    FReturnCode_Specified: boolean;
    FTotalMatchedAmount: TXSDecimal;
    FTotalMatchedAmount_Specified: boolean;
    FPlacePayout: TXSDecimal;
    FPlacePayout_Specified: boolean;
    FSelections: Array_Of_SelectionTypeWithPrices;
    FSelections_Specified: boolean;
    procedure SetReturnCode(Index: Integer; const AInteger: Integer);
    function  ReturnCode_Specified(Index: Integer): boolean;
    procedure SetTotalMatchedAmount(Index: Integer; const ATXSDecimal: TXSDecimal);
    function  TotalMatchedAmount_Specified(Index: Integer): boolean;
    procedure SetPlacePayout(Index: Integer; const ATXSDecimal: TXSDecimal);
    function  PlacePayout_Specified(Index: Integer): boolean;
    procedure SetSelections(Index: Integer; const AArray_Of_SelectionTypeWithPrices: Array_Of_SelectionTypeWithPrices);
    function  Selections_Specified(Index: Integer): boolean;
  public
    destructor Destroy; override;
  published
    property Id:                        Int64                             Index (IS_ATTR) read FId write FId;
    property Name_:                     WideString                        Index (IS_ATTR) read FName_ write FName_;
    property Type_:                     Smallint                          Index (IS_ATTR) read FType_ write FType_;
    property IsPlayMarket:              Boolean                           Index (IS_ATTR) read FIsPlayMarket write FIsPlayMarket;
    property Status:                    Smallint                          Index (IS_ATTR) read FStatus write FStatus;
    property NumberOfWinningSelections: Smallint                          Index (IS_ATTR) read FNumberOfWinningSelections write FNumberOfWinningSelections;
    property StartTime:                 TXSDateTime                       Index (IS_ATTR) read FStartTime write FStartTime;
    property WithdrawalSequenceNumber:  Smallint                          Index (IS_ATTR) read FWithdrawalSequenceNumber write FWithdrawalSequenceNumber;
    property DisplayOrder:              Smallint                          Index (IS_ATTR) read FDisplayOrder write FDisplayOrder;
    property IsEnabledForMultiples:     Boolean                           Index (IS_ATTR) read FIsEnabledForMultiples write FIsEnabledForMultiples;
    property IsInRunningAllowed:        Boolean                           Index (IS_ATTR) read FIsInRunningAllowed write FIsInRunningAllowed;
    property IsManagedWhenInRunning:    Boolean                           Index (IS_ATTR) read FIsManagedWhenInRunning write FIsManagedWhenInRunning;
    property IsCurrentlyInRunning:      Boolean                           Index (IS_ATTR) read FIsCurrentlyInRunning write FIsCurrentlyInRunning;
    property InRunningDelaySeconds:     Integer                           Index (IS_ATTR) read FInRunningDelaySeconds write FInRunningDelaySeconds;
    property ReturnCode:                Integer                           Index (IS_ATTR or IS_OPTN) read FReturnCode write SetReturnCode stored ReturnCode_Specified;
    property TotalMatchedAmount:        TXSDecimal                        Index (IS_ATTR or IS_OPTN) read FTotalMatchedAmount write SetTotalMatchedAmount stored TotalMatchedAmount_Specified;
    property PlacePayout:               TXSDecimal                        Index (IS_ATTR or IS_OPTN) read FPlacePayout write SetPlacePayout stored PlacePayout_Specified;
    property Selections:                Array_Of_SelectionTypeWithPrices  Index (IS_OPTN or IS_UNBD) read FSelections write SetSelections stored Selections_Specified;
  end;


  Array_Of_SelectionTypeWithPrices = array of SelectionTypeWithPrices;


  SelectionTypeWithPrices = class(TRemotable)
  private
    FId: Int64;
    FName_: WideString;
    FStatus: Smallint;
    FResetCount: Smallint;
    FDeductionFactor: TXSDecimal;
    FForSidePrices: ArrayOfPricesType;
    FAgainstSidePrices: ArrayOfPricesType;
  public
    destructor Destroy; override;
  published
    property Id:                Int64              Index (IS_ATTR) read FId write FId;
    property Name_:             WideString         Index (IS_ATTR) read FName_ write FName_;
    property Status:            Smallint           Index (IS_ATTR) read FStatus write FStatus;
    property ResetCount:        Smallint           Index (IS_ATTR) read FResetCount write FResetCount;
    property DeductionFactor:   TXSDecimal         Index (IS_ATTR) read FDeductionFactor write FDeductionFactor;
    property ForSidePrices:     ArrayOfPricesType  read FForSidePrices write FForSidePrices;
    property AgainstSidePrices: ArrayOfPricesType  read FAgainstSidePrices write FAgainstSidePrices;
  end;


  ArrayOfPricesType = array of PricesType;

  PricesType = class(TRemotable)
  private
    FPrice: TXSDecimal;
    FStake: TXSDecimal;
  public
    destructor Destroy; override;
  published
    property Price: TXSDecimal  Index (IS_ATTR) read FPrice write FPrice;
    property Stake: TXSDecimal  Index (IS_ATTR) read FStake write FStake;
  end;



With this structure the arrays FForSidePrices and FAgainstSidePrices are always empty. If, however, I change the structure of these elements to single classes rather than arrays:

    FForSidePrices: PricesType;
    FAgainstSidePrices: PricesType;

then these get populated with the first elements in the response. How can I get to see all 3 elements in the response?

I am using Delphi 2005 but have tried the same project in Delphi XE with the same result.

Any help is gratefully received.
Thanks
Kevin
0
Kevin
7/8/2011 1:44:05 PM
embarcadero.delphi.webservices 976 articles. 0 followers. Follow

5 Replies
3368 Views

Similar Articles

[PageSpeed] 44

Hello,


>
> The specific problem is that it is seeing the <Selections> as an array but 
> not <ForSidePrices> or <AgainstSidePrices>. Is there a limit to the number 
> of array levels it can handle?
> The data structure for MarketPrices is defined as:
>

The SOAP runtime uses dynamic arrays for two XML scenarios: collections and 
unbounded elements. The original SOAP encoding (Section-5 Encoding) fell in 
the former category. i.e. something along the lines of:

{code}
    <names>
      <item>Athos</item>
      <item>Porthos</item>
      <item>Aramis</item>
   </names>
{code}

In the case of unbounded elements, there's no parent element. It's just

{code}
    <name>Athos</name>
    <name>Porthos</name>
    <name>Aramis</name>
{code}


To handle the difference, the runtime relies on the IS_UNBD flag.

In the case you brought up, Selections is an unbouded element of 
MarketPrices, and the binding generated correctly represents this:

{code}
MarketTypeWithPrices = class(TRemotable)
    property Selections:  Array_Of_SelectionTypeWithPrices  Index (IS_OPTN 
or IS_UNBD) read FSelections write SetSelections stored 
Selections_Specified;
  end;
{code}


Likewise, 'ForSidePrices' and 'AgainstSidePrices' are unbounded elements of 
<Selections>. But there the IS_UNBD is missing:(

{code}
SelectionTypeWithPrices = class(TRemotable)
    property ForSidePrices:     ArrayOfPricesType  read FForSidePrices write 
FForSidePrices;
    property AgainstSidePrices: ArrayOfPricesType  read FAgainstSidePrices 
write FAgainstSidePrices;
  end;
{code}


>    FForSidePrices: PricesType;
>    FAgainstSidePrices: PricesType;
>
> then these get populated with the first elements in the response. How can 
> I get to see all 3 elements in the response?
>

This does not surprise me. To get all the elements you'll need to 
"Index(IS_UNBD)" on these properties


> I am using Delphi 2005 but have tried the same project in Delphi XE with 
> the same result.
>

This was indeed a problem in Delphi 2005 but a patch was released 
(http://cc.embarcadero.com/item/24535). But XE should handle this 
correctly... ????


> Any help is gratefully received.
>

The problem is that the code generated by the importer is not flagging 
unbounded correctly. Can you point me to the WSDL? The WSDL importer of both 
XE and the one from the patch should correctly generate IS_UNBD. If not, 
that's the problem and while you can modify the code by hand, I'd rather we 
get to the bottom of the issue as there might be other unbounded elements 
that you'll run into later.

Can you confirm which importer was used? D2005's, XE's or the one from the 
patch? Thank you.

Cheers,

Bruneau
0
Jean
7/13/2011 12:32:59 AM
Hi Bruneau

Thanks for your reply and apologies for not getting back to you sooner.

The WSDL in question can be found here:

http://api.betdaq.com/v2.0/API.wsdl

I can also confirm that the same imported code is generated for the class SelectionTypeWithPrices by both the patched importer from http://cc.embarcadero.com/item/24535 and the new importer from Delphi XE. I agree that if we can end up with an importer that will produce the expected code then that would be the preferred outcome. I can get around most problems with some manual editing but there are still some functions I cannot use - although I have not yet tried your suggestion of inserting the IS_UNBD fl
ag.

Cheers
Kevin
0
Kevin
7/15/2011 5:12:21 PM
Hello,

Ah! This is related to the unbounded compositor nodes brought up in the 
other thread 
(https://forums.embarcadero.com/message.jspa?messageID=367682&tstart=1). 
Neither the XE nor the patch importer handles unbounded compositor nodes. I 
found a workaround that I researched after XE shipped and posted here:

  https://forums.embarcadero.com/message.jspa?messageID=325355&tstart=0

With that workaround applied, I do see the importer generating the code that 
inform the runtime that these elements/properties are unbounded, as in:

{code}
SelectionTypeWithPrices = class(TRemotable)
  published
    property ForSidePrices:     Array_Of_PricesType  Index (IS_UNBD) read 
FForSidePrices write FForSidePrices;
    property AgainstSidePrices: Array_Of_PricesType  Index (IS_UNBD) read 
FAgainstSidePrices write FAgainstSidePrices;
  end;
{code}

The patch and XE importer will support elements that are marked 
maxOccurs="unbounded" but not element who are in compositor nodes marked 
maxOccurs="unbounded".

I hope the above helps! Grab the importer I emailed you (and posted here 
https://forums.embarcadero.com/thread.jspa?threadID=56585&tstart=0) and it 
should properly detect elements in unbounded compositor nodes.

Cheers,

Bruneau
0
Jean
7/15/2011 6:08:02 PM
Hi Bruneau,

Thanks for your reply. I need to complete some very urgent work before I can return to this issue. But if I recall when I tried your emailed importer it did indeed appear to create better imported code, but I was unable to compile it under Delphi 2005 which was not unexpected. When I tried a test application under Delphi XE I came up against different problems. I could not get any string data back, these elements were always blank. Also the getPrices function generates the error:

Exception: This name may not contain the '[' character:

-->[<--ArrayItemName="OrderHandle"]


I have not yet had time to investigate this further.
I will have another look at this issue in a week or two.

Thanks again for your help and suggestions so far.
Kevin
0
Kevin
7/16/2011 4:03:03 PM
Kevin, given how old this thread is I don't suppose you'll ever see this but .............

How did you get the ExternalApiHeader into the _di_SecureService using Delphi?.

Hope you know what I'm talking about there.
0
Tom
9/2/2012 6:10:01 PM
Reply: