WSDL exposed from RO-server, additional xs-attributes

Hello,

I have built my RO-server, where its services are primarily consumed via BinMessage. So, there is 3rd party application, which needs consume my services via SOAP protocol. Due to SOAP message content validation, 3rd party application have to know if elements inside of message are required or not. Usually, according to common XML schema conventions, this is done with “minOccurs/maxOccurs” pair of xs-attributes, but I have not found a way how to include such information in WSDL document, which is exposed from my RO-server.

Lets have a piece of RODL:

<Struct name="MyStruct">
  <Elements>
    <Element name="MyRequiredField" DataType="Integer">
    <Element name="MyOptionalField" DataType="Integer">
  </Elements>
</Struct>

Inside of WSDL, this structure is exposed as follows:

<xs:complexType name="MyStruct">
  <xs:sequence>
    <xs:element name="MyRequiredField" type="xs:int" /> 
    <xs:element name="MyOptionalField" type="xs:int" /> 
  </xs:sequence>
</xs:complexType>

In this scenario, 3rd party application has no chance to discover that “MyOptionalField” is really optional, not required, value for this element could not be provided. So, I need to change WSDL document in this way:

<xs:complexType name="MyStruct">
  <xs:sequence>
    <xs:element name="MyRequiredField" type="xs:int" /> 
    <xs:element name="MyOptionalField" type="xs:int" minOccurs="0" maxOccurs="1" /> 
  </xs:sequence>
</xs:complexType>

Any ideas how to do this?

Thanks, regards from Prague
Jaroslav

Hello

Which platform is used for your server app - .NET or Delphi?

Hello,

my server is built using RemObjects for Delphi (Delphi 2010 Professional, actually).

As I have observed, xml entities in RODL document can be equiped with persistent additional attributes, collected under <CustomAttributes> xml tag. These attributes are read when RODL is streamed in (TXMLToRODL.ReadAttributes) and stored in TRODLEntity.Attributes. I think it could be considered as reasonable solution to store additional xs-attributes right here, like this:

<Struct name="MyStruct">
  <Elements>
    <Element name="MyRequiredField" DataType="Integer">
    <Element name="MyOptionalField" DataType="Integer">
      <CustomAttributes>
        <minOccurs Value="0" />
        <maxOccurs Value="1" />
      </CustomAttributes>
  </Elements>
</Struct>

Subsequently, TRODLEntity.Attributes can be easily used as additional xs-attributes when WSDL document is generated, using TRODLToWSDL converter class. In this sense I made a small fix in uRODLToWSDL.pas unit, see this:

procedure TRODLToWSDL.WriteStruct(const aLibrary: TRODLLibrary; aStruct: TRODLStruct);
var
  i : integer;
  lLax: Boolean;
  lRet: TRODLStruct;

  // FIX: new auxiliary local function
  function CollectCustomAttributes(const AEntity: TRODLTypedEntity): String;
  var
    I: Integer;
  begin
    Result:='';
    with AEntity.Attributes do
      for I:=0 to Count-1 do begin
        if I > 0 then
          Result:=Result+' ';
        Result:=Result+Format('%s="%s"',[Names[I],ValueFromIndex[I]]);
      end;
    if Result<>'' then
      Result:=' '+Result;
  end;

begin
..
for i := 0 to aStruct.Count-1 do
  with aStruct.Items[i] do begin
    lRet := aLibrary.FindStruct(DataType);
    if (lRet <> nil) and (lRet.Count = 1) and (lRet.Attributes.Values['Anonymous'] = '1') then begin
      if lRet.Attributes.Values['Nillable'] = '1' then
        Write(Format('<xs:element name="%s" type="%s" nillable="1">', [Unprefix(Name), ExtSOAPDataType(aLibrary, lRet.Items[0].DataType)]), 15)
      else
        Write(Format('<xs:element name="%s" type="%s" minOccurs="0" maxOccurs="1">', [Unprefix(Name), ExtSOAPDataType(aLibrary, lRet.Items[0].DataType)]), 15);
    end else begin
      // FIX: attach xs-attributes to the end of element declaration
      Write(Format('<xs:element name="%s" type="%s"%s>', [Unprefix(Name), ExtSOAPDataType(aLibrary, DataType),
        CollectCustomAttributes(aStruct.Items[i])]), 15);
    end;
    WriteAnnotation(Documentation, 15); {Giovanni}
    Write('</xs:element>',15);
  end;
..
end;

This small fix satisfied my needs, elements exposed in WSDL document now contains xs-attributes, as they are introduced in RODL document.

Or… is threre any other, more sophisticated way how to do this?

Thanks, regards from Prague
Jaroslav

Hello

Thanks for your answer (and your code). However please note the following:
Your struct has 2 fields. They CANNOT be optional because an Integer is a value type and cannot be set to null.

So if a 3rd party app will forget to send the value of MyOptionalField you’ll get either 0 or an exception (depending on concrete serialized being used).

<xs:complexType name="MyStruct">
  <xs:sequence>
    <xs:element name="MyRequiredField" type="xs:int" /> 
    <xs:element name="MyOptionalField" type="xs:int" minOccurs="0" maxOccurs="1" /> 
  </xs:sequence>
</xs:complexType>

cannot be equal to

<Struct name="MyStruct">
  <Elements>
    <Element name="MyRequiredField" DataType="Integer">
    <Element name="MyOptionalField" DataType="Integer">
  </Elements>
</Struct>

What you need to do is to get the WSDL (w/o your adjustments in the RO code), change it as needed (ie add all needed attributes) and then re-import it in Service Builder. You’ll see that value type fields require wrappers to become optional ones.

Regards

Hello, thanks for response,

from my POV… my server is RODL-centric application, built around set of definitions made with RO service builder, stored in RODL file/resource. That is why Im expecting that ALL settings (both important & optional) can be made at the same time when Im editing my RODL file… without need to generate/modify/re-import auxiliary WSDL document. Lets count a steps: save RODL as WSDL, open WSDL using external editor, find and supplement missing xs-attributes at all relevant locations, save WSDL, run RO service builder, re-import WSDL… it seems so pointless, painful, noneffective… :sweat:

Ability to define & modify such attributes should be available directly into RO service builder, or… maybe too much courageous idea: why not use standard WSDL document as container to store service definitions, instead of RODL? I think that WSDL can catch & describe services in the same way, maybe even better as RODL do, because it is worldwide standard…

So, dont take it as serious issue, just as notice, remark, conceptual talking point…

Regards from Prague,
Jaroslav

Hello

This happens because you want to expose attributes in WSDL which don’t correspond to the actual RODL service definition. F.e. an anonymous wrapper structure is required to represent a nullable Int but you try to do this via adjusting the generated WSDL, effectively making WSDL lying about data formats being accepted by the server.

Because working with RODL is light-years simpler that with WSDL. Latter one is way too over-complicated, at least in the areas that would be needed to replace RODL. I know it because I wrote the tool that converts WSDL to RODL :wink:

Main issue with WSDL is that sometimes it allows to use literally 5-6 ways to define the same thing.

Regards

Hello, thanks for reply. again…

Ok, lets have a situation: I have RO service builder running, trying to define one simple structure, called MyComplexStruct, which contains two integer fields: 1/ MyRequiredField - we assume it is required, so no other adjustment is needed, 2/ MyOptionalField - we know ahead this is optional field, to avoid lying about data format firstly we declare auxiliary structure named Integer_Optional with one integer field named Value. See this RODL example:

  <?xml version="1.0" encoding="utf-8" ?> 
- <Library Name="MyLibrary" UID="{5571D070-34D4-4AE7-9FE9-8D1776888159}" Version="3.0">
  <Services /> 
- <Structs>
- <Struct Name="Integer_Optional" UID="{98FB20A9-17C5-498B-8DD8-F5FD234A952C}" AutoCreateParams="1">
- <Elements>
  <Element Name="Value" DataType="Integer" /> 
  </Elements>
  </Struct>
- <Struct Name="MyComplexStruct" UID="{74F5D323-1E2B-4D87-9335-F1DE70B2AA9E}" AutoCreateParams="1">
- <Elements>
  <Element Name="MyRequiredField" DataType="Integer" /> 
  <Element Name="MyOptionalField" DataType="Integer_Optional" /> 
  </Elements>
  </Struct>
  </Structs>
  <Enums /> 
  <Arrays /> 
  </Library>

So, hurray… we are finished! Or not…? Lets examine corresponding WSDL, generated from such RODL:

  <?xml version="1.0" encoding="UTF-8" ?> 
- <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema" name="MyLibrary" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" targetNamespace="http://tempuri.org/" xmlns:tns="http://tempuri.org/">
- <wsdl:types>
- <xs:schema targetNamespace="http://tempuri.org/" elementFormDefault="qualified">
- <xs:complexType name="Integer_Optional">
- <xs:sequence>
  <xs:element name="Value" type="xs:int" /> 
  </xs:sequence>
  </xs:complexType>
- <xs:complexType name="MyComplexStruct">
- <xs:sequence>
  <xs:element name="MyRequiredField" type="xs:int" /> 
  <xs:element name="MyOptionalField" type="tns:Integer_Optional" /> 
  </xs:sequence>
  </xs:complexType>
  </xs:schema>
  </wsdl:types>
  </wsdl:definitions

Hmm, damned! Where are “minOccurs/maxOccurs” xs-attributes hidden?? Nowhere, they are missing. Ok, what next? As expected - save WSDL, manually change xs-attributes for MyOptionalField element, save it and re-import again. Done… RODL content displayed in visual editor seems to be same as before. Well, take look at the content in XML form… voila! - there is new <CustomAttributes> collection, nested beneath the Integer_Optional structure definition.

  <?xml version="1.0" encoding="utf-8" ?> 
- <Library Name="MyLibrary" UID="{5571D070-34D4-4AE7-9FE9-8D1776888159}" Version="3.0">
  <Services /> 
- <Structs>
- <Struct Name="Integer_Optional" UID="{98FB20A9-17C5-498B-8DD8-F5FD234A952C}" AutoCreateParams="1">
- <CustomAttributes>
  <simpletype Value="Integer" /> 
  <anonymous Value="1" /> 
  <issimpletypeextension Value="1" /> 
  </CustomAttributes>
- <Elements>
  <Element Name="Value" DataType="Integer" /> 
  </Elements>
  </Struct>
- <Struct Name="MyComplexStruct" UID="{74F5D323-1E2B-4D87-9335-F1DE70B2AA9E}" AutoCreateParams="1">
- <Elements>
  <Element Name="MyRequiredField" DataType="Integer" /> 
  <Element Name="MyOptionalField" DataType="Integer_Optional" /> 
  </Elements>
  </Struct>
  </Structs>
  <Enums /> 
  <Arrays /> 
  </Library>

…and this is the point I’m complaining about… why such kind of RODL modification isn’t directly available in RO service builder? Why I need to make complicated WSDL export/modification/re-import to reach something so simple? Im “The Service Designer” so I want to have opportunity to declare element attributes directly, during design stage, without useless additional steps… I want to say “This is structure covers simple type with optional occurence, in case of SOAP clients notify them via WSDL that all elements of this type are OPTIONAL”

Hope I explain it clear… regards from Prague
Jaroslav

Hello

Ah, seems I misunderstood you, sorry. Latest builds of Service Builder already allow to set custom attribute values:

Regards

Hello,

great news, really… “one small step for man, giant leap for developers…:grinning:. In which release will be this feature available?

Just a notice… its quite easy to declare custom attributes manually, directly in RODL XML document, but… RODL internal structure is not documented well (except a few short articles in RemObjects wiki), so developer without detailed knowledge has no chance to make it correctly.

Thanks for conversation, regards from Prague
Jaroslav