Jak zinterpretować lub dodać dane w SOAP WSDL?

0

Proszę o pomoc. Jak do tak zdefiniowanej struktury danych wpisać dane w Delphi.
Zaimportowałem do delhi strukturę z XML'a. Delphi przygotował plik (jak niżej).
Kiedyś wydawało mi się że rozumiem deklaracje dynamiczne i rekordowe, ale teraz zwątpiłem.
Proszę niech mi ktoś to wyjaśni na jakimś przykładzie, jak do struktury "inventory" wpisać dane Details.

const
  IS_UNBD = $0002;
  IS_UNQL = $0008;
  IS_REF  = $0080;

type
  Inventory            = class;         
  Details              = class;
//------
 LocationCode    =  type WideString;      
  PartNum         =  type WideString;      
  Quantity        =  type Int64;      
  Array_Of_Details = array of Details;
//------
 Inventory = class(TRemotable)
  private
    FLocationCode: LocationCode;
    FDetails: Array_Of_Details;
  public
    constructor Create; override;
    destructor Destroy; override;
  published
    property LocationCode: LocationCode      Index (IS_UNQL) read FLocationCode write FLocationCode;
    property Details:      Array_Of_Details  Index (IS_UNBD or IS_UNQL) read FDetails write FDetails;
  end;

  Array_Of_PartNum = array of PartNum;          
  Array_Of_Quantity = array of Quantity;        
//------
 Details = class(TRemotable)
  private
    FPartNum: Array_Of_PartNum;
    FQuantity: Array_Of_Quantity;
  published
    property PartNum:  Array_Of_PartNum   Index (IS_UNBD or IS_UNQL) read FPartNum write FPartNum;
    property Quantity: Array_Of_Quantity  Index (IS_UNBD or IS_UNQL) read FQuantity write FQuantity;
  end;

 Dealer = interface(IInvokable)
  ['{7B3311C4-F348-3641-E9AA-xxxxxxxxxxx}']

    // Cannot unwrap: 
    //     - Input element wrapper name does not match operation's name   --**co to oznacza** 
    procedure WCF_Operation(const part: Inventory); stdcall;

Co oznacza w kodzie ten wpis:
// Cannot unwrap:
// - Input element wrapper name does not match operation's name
Został dodany w trakcie konwersji XML'a na unit deplhi.

Przy pomocy "Inventory" chce przekazać stan magazynu (kod magazynu, w tablicy Details numer części i ilość).

Z góry dziękuję za pomoc.
Rafal.

0

To nie są żadne deklaracje dynamiczne i rekordowe, tylko najzwyklejsze klasy i interfejsy; Ten kod się w ogóle kompiluje, czy podczas kompilacji lecą błędy..? o.O

0

Kod się kompiluje.

a to: Array_Of_PartNum = array of PartNum;
nie jest tablica dynamiczna ?

0

No właśnie to jest macierz dynamiczna, której najpierw trzeba nadać rozmiar procedurą SetLength, zanim zacznie się z niej korzystać;

A tak nawiasem - nazewnictwo w podanym kodzie jest tragiczne.

0

"Details" jest częścią struktury "Inventory" -nie umiem wypełnić danymi tej struktury.
Tyle mi się udało:

var bInv:Inventory;
     bDet:Details;

begin
 bInv :=Inventory.Create;
 bDet:=Details.Create;
  bDet.PartNum:=VarArrayOf(['230831001','2CDG']);
  bDet.Quantity:=VarArrayOf([4,1]);

 bInv.LocationCode:='MAG1';

bInv.Details :=bDET;  //--to nie dziala !!!!! jak to przypisac

PS.:Nazewnictwo faktycznie jest tragiczne, ale to jest produkt DELPHI WDSL Importer na podstawie XML'a wygenerowanego przez SOAP.

0

@RTS - powtarzam po raz kolejny: Inventory to nie jest struktura, tylko klasa!

Piszesz, że ten kod jest generowany - masz możliwość jego modyfikacji, czy musisz korzystać z tego, co jest podane w pierwszym Twoim poście? Nie podobają mi się deklaracje właściwości, łączących z polami macierzowymi w klasie Inventory; Dosyć to dziwny zapis, pomimo tego, że się kompiluje nieco odbiega od znanej mi metody, opisanej w dokumentacji Delphi; Nie ma tam takiej deklaracji jak używasz (z wykorzystaniem keywordu Index) i sam nie wiem czym Twoja metoda różni się od tej standardowej; Chyba tylko tym, że do pola możesz podać (wpisać) całą macierz;

Wiele razy stosowałem pola macierzowe w klasach i zawsze do ich obsługi (dodawania, usuwania, modyfikowania) wykorzystywałem odpowiedzialne za to metody; Właściwość łącząca z polem macierzowym była dodawana, ale nie była typu macierzowego, tylko zwracała pojedynczy element macierzy; Dawała także możliwość modyfikacji danych tego pojedynczego elementu, ale nie łączyła bezpośrednio z polem, tylko wykorzystywany był akcesor (do pobrania elementu) i mutator (do jego modyfikacji); Takie dwie prywatne metody pozwalały na dostęp do elementów macierzy na podstawie indeksu, a publiczne metody pozwalały na manipulowanie listą - dodawanie nowych i usuwanie elementów, czyszczenie listy, sortowanie itd.; Dzięki temu łatwo się z klasy korzystało i dawała ona duże możliwości; Poniżej przykładowa deklaracja takiej właściwości i zestawu metod do obsługi pola macierzowego:

type
  TFooArray = array of AnsiString;

type
  TFooClass = class(TObject)
  private
    FFooArray: TFooArray;
    FCount: Integer;
  private
    function GetElement(AIndex: Integer): AnsiString;
    procedure SetElement(AIndex: Integer; AValue: AnsiString);
  public
    constructor Create();
    destructor Destroy(); override;
  public
    procedure Add(AValue: AnsiString);
    procedure Insert(AIndex: Integer; AValue: AnsiString);
    procedure Remove(AIndex: Integer);
    procedure Clear();
  public
    property FooArray[AIndex: Integer]: AnsiString read GetElement write SetElement;
  end;

Jak widzisz właściwość FooArray daje dostęp do pojedynczego elementu pola FFooArray, gdzie pobrania elementu macierzy dokonuje akcesor GetElement, a modyfikacji elementu mutator SetElement; Do manipulacji rozmiarem listy służą metody Add, Insert, Remove i Clear, które ustalają rozmiar pola FFooArray; Aby uczynić właściwość FooArray domyślną, wystarczy dodać na końcu keyword Default:

property FooArray[AIndex: Integer]: AnsiString read GetElement write SetElement; default;

Dzięki temu nie trzeba będzie podawać nazwy właściwości - wystarczy indeks elementu w kwadratowych nawiasach;


Podsumowując - Twoje właściwości co prawda dają możliwość dostępu i manipulacji polami, ale poniższa linijka jest składniowo błędna:

bInv.Details := bDET;

dlatego że właściwość Details klasy Inventory jest typu macierzowego, a Ty próbujesz podać pojedynczy element; Musisz więc albo przekazać macierz elementów, albo podać indeks elementu, do którego chcesz wpisać obiekt bDET; Gdyby klasa Inventory wyglądała podobnie jak ta przykładowa podana wyżej, mógłbyś wykorzystwać właściwość Details podając indeks elementu, do którego chcesz obiekt wpisać; A jeśli chciałbyć dodać nowe elementy do macierzy, to tak jak jest u mnie - skorzystać z publicznej metody Add; Nawet musiałbyś, bo coś przecież musi zwiększyć rozmiar macierzy i dodać instancję klasy do pola.

0

Spróbuj tak:

  SetLength(bInv.Details, 1);
  bInv.Details[0] :=bDET;

Wydaje mi się jednak, że to i tak nie da ci za wiele bo problem będzie też z PartNum i Quantity czyli

  SetLength(bDet.PartNum, 2);
  bDet.PartNum[0]:='230831001';
  bDet.PartNum[1]:='2CDG';

  SetLength(bDet.Quantity, 2);
  bDet.Quantity[0]:=4;
  bDet.Quantity[1]:=1;

a jeśli działa bez tego to wcześniejszy kawałek powienien wyglądać mniej więcej tak:

  bInv.Details :=VarArrayOf([bDET]);
0

Niestety już to ćwiczyłem:

SetLength(bInv.Details, 1); // tutaj występuje błąd: [DCC Error] u_soap_test.pas(120): E2197 Constant object cannot be passed as var parameter
bInv.Details[0] :=bDET;
SetLength(bInv.Details[0], 1);// a tutaj taki: [DCC Error] u_soap_test.pas(120): E2008 Incompatible types
bInv.Details :=VarArrayOf([bDET]); // tutaj występuje błąd: [DCC Error] u_soap_test.pas(119): E2010 Incompatible types: 'Variant' and 'Details'

Natomiast przypisanie wartości PartNum i Quantity nie jest problemem działa świetnie:

bdet:=Details.Create;
bdet.PartNum:=VarArrayOf(['230831001','2CDG']);
bdet.Quantity:=VarArrayOf([4,1]);

Szkopuł tkwi w tym że dane musze przekazac procedura:

Dealer = interface(IInvokable)
  procedure WCF_Operation(const part: Inventory); stdcall;
end;

zmodyfikowanie komentarzy w kodzie - Furious Programming

0

@Furious Programming - mogę edytować ten kod, ale tak jak mówiłem to jest struktura narzucona przez serwer SOAP (nie mam pojęcia do jakiego stopnia jest on elastyczny). Po raz pierwszy zetknąłem się z problemem napisania klienta SOAP.
Poszukuję praktycznego rozwiązania, ponieważ wszystkie pomysły jakie miałem żeby te dane wysłać do serwera jak na razie nie wypaliły.
Tak wygląda paczka którą w XML'u ten serwer akceptuje:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns0:Inventory xmlns:ns0="http://Dealer.Inventory.Schemas.Inventory">
         <LocationCode>83200</LocationCode>
         <Details>
            <PartNum>226638001</PartNum>
            <Quantity>1</Quantity>
         </Details>
         <Details>
            <PartNum>230831001</PartNum>
            <Quantity>2</Quantity>
         </Details>
      </ns0:Inventory>
   </soap:Body>
</soap:Envelope>

Zapisy w Delphi mają przystawać do tej struktury.

1

w var MyDetails: Array_Of_Details; a dalej

  SetLength(MyDetails, 1);
  MyDetails[0] := bDET;

  bInv.Details := MyDetails;
1

Nie, nie, nie - rozmiar pola macierzowego musisz ustalać wewnątrz konstruktora, destruktora lub metod klasy! Ty próbujesz to zrobić nie dość, że poza ciałem klasy, to jeszcze zamiast na polu (do którego i tak nie masz dostępu) to na właściwości;

Natomiast przypisanie wartości PartNum i Quantity nie jest problemem działa świetnie:

Tak, tyle że tamte pola przyjmują macierz typów prostych, a Details to macierz instancji klas (obiektów), a to co innego, dlatego masz niezgodność typów;

Szkopuł tkwi w tym że dane musze przekazac procedura:

WCF_Operation to nie jest procedura, tylko metoda... Poza tym niepotrzebnie przekazywany jest argument Part przez stałą, bo Inventory jest pointerem na klasę;

W takim razie spróbuj tak, jak pokazał @szopenfx - do właściwości przypisz uzupełnioną macierz typu Array_Of_Details, powinno przejść.

0

Serdeczne dziękuję za pomoc.
ZADZIAŁAŁO :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1