Les types de données SOAP et les types de données 1000

De Wiki1000

Sommaire

Les types de données SOAP et Ligne 1000

L’exemple précédent utilisait un service très simple échangeant une chaine de caractère, les types utilisables dans les WS peuvent être plus compliqués et s’appuient sur les types définis par les Schémas XML

Les principaux types simples

SOAP Ligne 1000 Commentaire
string String
int Integer
decimal Currency Le type Decimal peut comporter jusqu’à 128 décimales, la ligne 1000 ne permet que 4 décimales
float, double Double
dateTime TDateTime
date Date
time Time
boolean Boolean
base64Binary String Une chaine encodée contenant les données binaires

Les types SOAP sont reconnus comme des types de données natifs par 1000

Les types binaires

SOAP permet d’échanger des éléments binaires encodé soit en base 64 (base64Binary) soit en hexadécimal (hexBinary).

Pour permettre de manipuler ces données binaires des propriétés base64Binary et hexBinary ont été ajouté au type de données binaires du Framework (TfwPicture, TfwBinary, TfwMemo), et de nouveaux objets techniques ont été introduis :

C’est un type générique permettant de contenir des données binaires.

Exemple d’utilisation :

// Procedure SendClaim;
// 
var inst:TACwsiattachement;
    body:TACClaimDetailType;
    content: TBinaryContent;
begin
  // Objet gestionnaire de binaire
  content := TBinaryContent.Create;
 
  // Chargement de l’objet avec une image
 content.LoadFromFile('d:\Mes documents\Mes images\white house.jpg');
 
  // Descriptif de l’image attendu par le service
 
  body := ClassManager.CreateInstance('TACClaimDetailType');
  body.Name := 'White house';
 
  //
  inst := ClassManager.CreateInstance('TACwsiattachement');
  inst.sendClaim(body,content.base64Binary);
end;

Exemple :

Cet exemple montre comment appelé un service exécutant l’écho d’un paramètre binaire :

Le service :

Type
  InteropService = Class(TServiceLocal)
  Procedure echoBase64(const input:base64Binary; out output:base64Binary); 
 end;
 
Procedure InteropService.echoBase64(const input:base64Binary; out output:base64Binary);
begin
  output := input;
end;

Le proxy local du service :

Type
  ICinteropservice = Class(TServiceDistant)
  public
    Procedure echoBase64(const input:base64Binary; out output:base64Binary);
  end;
 
Procedure ICinteropservice.echoBase64(const input:base64Binary; out output:base64Binary);
begin
 //Remote procedure, do not code
end;

L’appel du service en utilisant des chaînes de caractères :

//Procedure echoBase64;
var inst:ICinteropService;
    sin:string;
    sout:base64binary;
    contin,contout:TBinaryContent;
begin
  inst := ClassManager.CreateInstance('ICinteropService');
 
  // Une chaine a transmettre
  sin := 'coucou';
 
  // Converti la chaine en utilisant un BinaryContent
  // 
  contin := TBinaryContent.Create;
  contin.AsString := sin;
 
  // appel du service
  // sout contient la réponse encodée
  inst.echoBase64(contin.base64Binary,sout);
 
  // converti la réponse 
  //
  contout := TBinaryContent.Create;
  contout.base64Binary := sout;
 
  // compare le résultat
  //
  if sin<>contout.AsString
     then showMessage('echoBase64 failed')
     else showMessage('echoBase64 OK');
end;

L’appel du service en utilisant des TStringList

//Procedure echoBase64_2;
var inst:ICinteropService;
    lsin,lsout:TStringList;
    sout: base64binary;
begin
  inst := ClassManager.CreateInstance('ICinteropService');
  lsin := TStringList.Create;
 
  lsin.LoadFromFile('c:\myText-1.txt');
 
  inst.echoBase64(lsin.base64binary,sout);
  lsout := TStringlist.Create;
  lsout.base64Binary := sout;
  lsout.SaveToFile('c:\myText-2.txt');
end;

Même exemple en utilisant une compression :

//Procedure echoBase64_3;
var inst:ICinteropService;
    lsin,lsout:TStringList;
    sout: base64Binary;
begin
  inst := ClassManager.CreateInstance('ICinteropService');
  lsin := TStringList.Create;
  lsin.LoadFromFile('c:\myText-1.txt');
 
  inst.echoBase64(lsin.getAsBase64Binary(b64ZlibNoSig),sout);
 
  lsout := TStringlist.Create;
  lsout.setAsBase64Binary(sout,b64ZLibNoSig);
  lsout.SaveToFile('c:\myText-3.txt');
end;

Les énumérés

Le protocole SOAP permet de définir des énumérations de valeurs pour préciser les valeurs possibles d’un paramètre de message.

Un énuméré SOAP correspond à un énuméré Ligne 1000.

Par exemple pour définir un service effectuant l’écho d’un énuméré :

//Défini un énuméré.
EchoNum = (cstEcho1, cstEcho2) ;
 
//Procedure echoEnum(input:EchoEnum; out output:EchoEnum);
begin
  output := input;
end;

Pour appeler ce service, (le service a été importé en utilisant un préfixe de paquet IC) :

ICEchoNum = (cstEcho1,cstEcho2) ;
 
// Procedure echoEnum;
//
var inst:ICinteropService; sin,sout:ICEchoEnum;
begin
  sin := cstEcho1;
  inst := ClassManager.CreateInstance('ICinteropService');
  inst.echoEnum(cstEcho1,sout);
  if sin<>sout
     then showMessage('echoEnum failed')
     else showMessage('echoEnum OK');
end;

Les valeurs énumérées sont transmises par leur constantes et non par leur index, l’appel précédent génère le message suivant :

<?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">
<soap:Body>
<echoEnum xmlns="http://www.sage.com/fr/line1000/InterpServicePackage">
<input>cstEcho1</input>
</echoEnum>
</soap:Body>
</soap:Envelope>

Les types complexes (structures)

Le protocole SOAP permet d’échanger des types complexes sous forme de structure.

Une structure SOAP correspond à une classe Ligne 1000.

Appel d’un service effectuant l’écho d’une structure :

//Procedure echoStruct;
//
var inst:DSService; structin,structout:DSSOAPStruct;
begin
  // La structure est une classe 1000
  structin := ClassManager.CreateInstance('DSSOAPStruct');
  structin.varString := 'coucou';
  structin.varInt := 1;
  structin.varFloat := 1.0;
 
  // appel du service
  inst := ClassManager.CreateInstance('DSService');
 
  // l’instance en retour est créée par le framework
  inst.echoStruct(structin,structout);
 
  showMessage('Result:'+structout.varString);
end;

Les extensions de type complexe.

Une extension de type complexe permet d’enrichir un type complexe préalablement défini, la correspondance en Ligne 1000 est une classe héritant de la classe enrichie.

Info-20px.png Note : Lors de la publication d’un service toutes les classes dérivées d’une classe publiée sont publiées.

Les tableaux et références.

Le protocole SOAP permet d’échanger des ensembles de données sous forme de tableaux. L’implémentation de 1000 distingue les tableaux de type simple des tableaux de structure.

Scénarii Correspondance ligne 1000
Tableau de type simple (t1) passé en paramètre Tableau ouvert du type simple (t1) dans les paramètres de l’opération

const p : Array of t1 ;

Tableau de structure (s1) en paramètre Liste d’objet de la classe représentant la structure (s1) dans les paramètres de l’opération

const p : s1List ;

Tableau de structure (s1) dans une structure (s2) Rôle liste vers la structure (s1) du tableau dans la classe de la structure (s2)
Type complexe (s1) référençant un type complexe (s2) Rôle référence vers la structure (s1) dans la classe de la structure (s2)
Tableau de type simple (t1) dans une structure (s2). Rôle liste vers une classe wrapper du type simple (t1) dans la classe de la structure (s2)

Tableau de type simple passé en paramètre.

Exemple:

Procedure echoArrayOfString(const input:Array of string; out output:Array of string);
var idx:Integer;
begin
  // Implémentation du service
  // Copie le tableau élément par élément.
  //
  for idx:=0 to length(input)-1 do
   output[idx] := input[idx];
End ;

Pour appeler ce service :

Procedure echoArrayOfString ;
var inst:ICinteropService; sin,sout:Array of string;
    idx:Integer; s1,s2:string;
begin
  inst := ICinteropService.Create;
 
  // Initialise le tableau par une constante
  sin := ['Ah','que','coucou'];
 
  // Appel du service, sout sera initialisée 
  // par la couche soap
  inst.echoArrayOfString(sin,sout);
 
 
  // Verification
  // 
  s1 := '';
 
  for idx:=0 to length(sin)-1 do
   s1 := s1+sin[idx];
 
  s2 := '';
  for idx:=0 to length(sout)-1 do
   s2 := s2+sout[idx];
 
  if s1<>s2
     then showMessage('echoArrayString failed :'+s1+':'+s2)
     else showMessage('echoArrayString OK');
end;

Tableau de type complexe passé en paramètre.

Un type complexe étant une classe un tableau de type complexe est une liste d’objet de cette classe

Par exemple pour échanger une liste de SOAPStruct

Type
  InteropStruct1 = Class(TitObject)
  public
    InteropStruct2List: CompositionList of InteropStruct2;
    InteropStruct2Ref: InteropStruct2;
    InteropStruct3Ref: InteropStruct3;
    oidInteropStruct2Ref: string;
    oidInteropStruct3Ref: string;
    unCode1: string;
  end;
 
  InteropStruct2 = Class(TitObject)
  public
    InteropStruct1Ref: InteropStruct1;
    oidInteropStruct1Ref: string;
    unCode2: string;
  end;
 
  InteropStruct3 = Class(TitObject)
  public
    unCode3: string;
  end;
 
  InteropService = Class(Service)
  public
    Procedure echoStruct1Array(const input:InteropStruct1List; out output:InteropStruct1List);
  end;

Pour appeler ce service :

Procedure echoStructArray;
var inst:ICinteropService;
    sin,sout:ICInteropStruct1List;
    s1i,s1o:ICInteropStruct1;
    idx:Integer; s2:ICInteropStruct2;
begin
  inst := ICinteropService.Create;
 
  // Force l’appel distant
  inst.LoopMode := mlmNone;
 
  // Création de la liste d’appel.
  //
  sin := ClassManager.CreateObjectList('ICInteropStruct1');
  //
  s1i := ICInteropStruct1.Create;
  s1i.unCode1 := 'code1';
 
  for idx:=0 to 1 do
   begin
     s2 := ICInteropStruct2.Create;
     s2.unCode2 := 'code1-'+inttostr(idx);
     s1i.InteropStruct2List.AddRef(s2);
   end;
  sin.AddRef(s1i);
 
  // Appel du service
  // la liste sout sera initialisée 
  // par la couche soap
 
  inst.echoStruct1Array(sin,sout);
 
 
  // Vérification
  //
  if sin.Count<>sout.Count then
   begin
     showMessage('echoStruct1Array failed, struct count');
     Exit;
   end;
 
  // Test du premier élément
  s1i := sin.Refs[0];
  s1o := sout.Refs[0];
  if s1i.unCode1<>s1o.unCode1 then
   begin
     showMessage('echoStruct1Array failed, struc1.unCode1');
     Exit;
   end;
  //
  //
  if s1i.InteropStruct2List.Count<>s1o.InteropStruct2List.Count then
   begin
     showMessage('echoStruct1Array failed, struc1.struct2.count');
     Exit;
   end;
 
  // Test de la liste imbriquée
  //
  for idx:=0 to s1i.InteropStruct2List.Count-1 do
   if (s1i.InteropStruct2List.Refs[idx].unCode2<>s1o.InteropStruct2List.Refs[idx].unCode2) then
    begin
     showMessage('echoStruct1Array failed, struc1.struct2[].unCode2');
     Exit;
    end;
 
  showMessage('echoStruct1 OK');
end;

Tableau de structure dans une structure.

Un type complexe SOAP peut contenir des tableaux, 1000 modélise un tableau contenu dans un type complexe par un rôle liste composition.

Voir l’exemple précédent.

Type complexe référençant un élément de type complexe.

Un type complexe SOAP peut référencer un élément de type complexe, 1000 modélise l’élément comme une référence composition référençant le type complexe.

Tableau de type simple dans une structure.

La ligne 1000 ne permet pas de manipuler nativement des tableaux de type simple comme attribut de classes. Pour permettre l’échange de ces tableaux elle définit des classes Wrapper de types simples puis utilise des tableaux sur ces classes.

Exemple :

Déclaration du service :

image20.png

unit InterpServicePackage
interface
Type
  InteropService = Class(TitObject)
  public
    Procedure echoStruct5(const input:InteropStruct5; out output:InteropStruct5);
  end;
 
  InteropStringWrapper = Class(TitObject)
  // cette classe a l’option Wrapper de type simple coché dans ses propriétés soap
  public
    data : string; // les classes wrapper doivent avoir un attribut data
  end;
 
  Interopstruct5 = Class(TitObject)
  public
    unArrayOfString: List of InteropStringWrapper;
    unCode: string;
  end;
 
Implementation
 
{InteropService}
 
Procedure InteropService.echoStruct5(const input:InteropStruct5; out output:InteropStruct5);
begin
  output := input;
end;
 
end.

Lorsque ce service est publié en WSDL, la classe wrapper n’apparait pas et un élément de type tableau est publié :

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.sage.com/fr/line1000/InterpServicePackage" xmlns:tns="http://www.sage.com/fr/line1000/InterpServicePackage">
 <wsdl:types>
  <xsd:schema elementFormDefault="qualified" targetNamespace="http://www.sage.com/fr/line1000/InterpServicePackage" xmlns="http://www.w3.org/2001/XMLSchema">
   <element name="echoStruct5">
    <complexType>
     <sequence>
      <element name="input" type="tns:Interopstruct5" minOccurs="0" maxOccurs="1"/>
     </sequence>
    </complexType>
   </element>
   <complexType name="Interopstruct5">
    <sequence>
     <element name="unArrayOfString" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
     <element name="unCode" type="xsd:string"/>
    </sequence>
   </complexType>
   <element name="echoStruct5Response">
    <complexType>
     <sequence>
      <element name="output" type="tns:Interopstruct5" minOccurs="0" maxOccurs="1"/>
     </sequence>
    </complexType>
   </element>
  </xsd:schema>
 </wsdl:types>
 <wsdl:message name="echoStruct5">
  <wsdl:part name="parameters" element="tns:echoStruct5"/>
 </wsdl:message>
 <wsdl:message name="echoStruct5Response">
  <wsdl:part name="parameters" element="tns:echoStruct5Response"/>
 </wsdl:message>
 <wsdl:portType name="interopservicePortType">
  <wsdl:operation name="echoStruct5">
   <wsdl:input message="tns:echoStruct5" name="echoStruct5"/>
   <wsdl:output message="tns:echoStruct5Response" name="echoStruct5Respond"/>
  </wsdl:operation>
 </wsdl:portType>

L’import du wsdl de ce service génère le modèle suivant :

image21.png

L’élément unArrayOfString, tableau de type simple, génère une classe wrapper et une référence liste sur cette classe.

L’appel de la fonction echStruct5 avec le passage des éléments du tableau :

Procedure TestInteropService.echoStruct5;
var inst:ICinteropService;
    sin,sout:ICInteropStruct5;
    wrp:ICWrapperOfString;
    idx:Integer;
    ss,stag: string;
begin
  inst := ICinteropService.Create;
  sin := ICInteropStruct5.Create;
  sin.unCode := 'code5';
  for idx:=0 to 1 do
   begin
     wrp := ICWrapperOfString.Create;
     wrp.data := 'data-'+inttostr(idx);
     sin.unArrayOfString.AddRef(wrp);
   end;
 
  inst.echoStruct5(sin,sout);
 
  ss := ''; stag := #13;
  for idx:=0 to sout.unArrayOfString.Count-1 do
   begin
     ss := ss+stag+sout.unArrayOfString.refs[idx].data;
   end;
  showMessage('echoStruct5 :'+ss);
end;

Ce qui génère le message suivant :

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<echoStruct5 xmlns="http://www.sage.com/fr/line1000/InterpServicePackage">
<input>
<unArrayOfString>data-0</unArrayOfString>
<unArrayOfString>data-1</unArrayOfString>
<unCode>code5</unCode>
</input>
</echoStruct5>
</soap:Body>
</soap:Envelope>

Publication des rôles des classes utilisées comme type complexe soap.

Lorsqu’ une classe, utilisée dans un service, est publiée dans un descriptif de service (WSDL) elle y inclut tous les rôles référençant une classe du paquet de service. Un rôle référençant une classe à l’extérieur du paquet de service ne sera pas publié.

Web Services (ws)Développement DSM

Outils personnels