Element attribute from different namespace

87 views
Skip to first unread message

PH

unread,
Apr 27, 2016, 1:46:48 AM4/27/16
to WST-LIST
Hey! I have a web service requiring a header with an attribute from different namespace than the element, original WSDL is from http://x-road.ee/valid/xroad6.wsdl where some headers from xroad have objectType attribute from xroad-identifiers. The WSDL importer tool doesn't seem to import the headers so I manually added some inherited THeaderBlocks and TSimpleContentHeaderBlocks to the ICallContext.

Specifically, I have an element from namespace foo and it has an (enumerable) attribute from namespace bar such that:
<foo:client bar:type="SUBSYSTEM">

Is there any way to define such a header element for the request?

-PH

Inoussa OUEDRAOGO

unread,
Apr 27, 2016, 3:55:42 AM4/27/16
to wst-...@googlegroups.com
2016-04-27 5:46 UTC, PH <sund...@gmail.com>:
> Hey! I have a web service requiring a header with an attribute from
> different namespace than the element, original WSDL is from
> http://x-road.ee/valid/xroad6.wsdl where some headers from xroad have
> objectType attribute from xroad-identifiers. The WSDL importer tool doesn't
>
> seem to import the headers so I manually added some inherited THeaderBlocks

Quick solution : download the schema "http://x-road.eu/xsd/xroad.xsd"
and parse it with ws_helper; ws_helper can also parse XSD files. Then
you could use the declared types as the others.

The WSDL does donwload parse imported schema; but for now it does so
only for schema files located on local file system.


--
Inoussa O.

PH

unread,
May 9, 2016, 2:52:39 AM5/9/16
to WST-LIST
Thanks for the tip! Now downloaded all the schemas and edited the locations to local files downloaded into the same folder. There's some problem with importing the randomValues-type: The class and the property are named randomValues, while the property declaration has randomValues_Type as the identifier, which is not declared anywhere. I renamed the generated class to randomValues_Type and now it compiles, I'll see if I get this working now :)

-PH

PH

unread,
May 20, 2016, 8:58:17 AM5/20/16
to WST-LIST
With few changes, WST is running in Delphi XE8 now. wst_global.inc needed {$IFDEF VER290} in the same style as previous {$IFDEF VER260} for XE5 (presumably similar for all other versions of XE6, XE7, XE10). indy_http_protocol.pas had TIdNetscapeCookie which I guess is same as TIdCookie now (Indy10), and all the ReferencedObject.GetCookieIndex(0,AName) have their parameters swapped to (AName,0). Tested the example user_service_intf.wsdl by reading it into SoapUI, had to remove the <document><GUID...>-element because SoapUI didn't like it. Mocking the services in SoapUI, I had to change base_soap_formatter.pas so that I prepend 'urn:' in front of the ANameSpace if ANameSpace is not an URL. After this, I got the example client working nicely with files generated by ws_helper.

Now, I tested the https://raw.githubusercontent.com/petkivim/x-road-adapter-example/master/src/src/main/resources/example.xroad-6.4.wsdl WSDL if I could get a working client up. Downloaded all .xsd:s to local file system and changed schemaLocation="xroad.xsd" so that ws_helper could parse them. The parsed files have for example the id-header presented as
id = string;
without any header classes, is this expected? For each of the simple header classes, should I create a bunch of TSimpleContentHeader-derived classes to add them in to requests? But I did manage to get ws_helper to parse client- and service-header elements as proper types now, only I need to change some of the generated namespaces for them. For example client in xroad.pas is of type XRoadClientIdentifierType, which was parsed into identifiers.pas where it is registered in typeRegistry in the wrong namespace. Is my only current option just to manually go through all the registered namespaces and change them to proper ones?

-PH

PH

unread,
May 23, 2016, 6:06:20 AM5/23/16
to WST-LIST
Solved! I have some potential fixes for base_soap_formatter.pas with regards to the attributes and namespaces.

First I had to in my generated files change
  //client = XRoadClientIdentifierType; ->
  client
= class(XRoadClientIdentifierType)
 
end;
...
  typeRegistryInstance
.Register(sNAME_SPACE,TypeInfo(client),'client');
to get the headers registered in the proper namespace and name. Then I got a bunch of different errors when the SOAP message was being formatted and deserialized etc.

Here's how I solved these. First, in base_soap_formatter.pas in
function TSOAPBaseFormatter.InternalPutData
there's this section
      if IsStrEmpty(s) then begin
        namespaceShortName
:= 'ns' + IntToStr(NextNameSpaceCounter());
       
AddScopeAttribute('xmlns:'+namespaceShortName, namespaceLongName);
        strNodeName
:= s + ':' + strNodeName;
     
end



I figure that the last line is buggy, since we just created a short name for namespace but we haven't set it to s. I simply replaced the last line with
strNodeName := namespaceShortName + ':' + strNodeName;



Next, I'm not sure why, maybe the formatter expects the response headers to be in certain order or something but response deserializing was giving errors on the objectType-attribute. I found out that in function TSOAPBaseFormatter.GetNodeValue the following section was examining completely different header element than the attribute's node:
if ( FSerializationStyle = ssNodeSerialization ) then begin
    locElt := StackTop().FindNode(strNodeName) As TDOMElement;
 
end else begin
    locElt
:= GetCurrentScopeObject().GetAttributeNode(strNodeName);
 
end;

I figure GetCurrentScopeObject() gives the wrong element to examine since it was examining the second header in the response, which corresponded the position of my request's header elements. Somewhat hastily, I solved this by iterating through the siblings until no more siblings or I found the attribute, but I'm not sure if this is the best solution...

iterElt : TDOMNode;
...
 
end else begin
    iterElt
:= GetCurrentScopeObject();
   
while ((locElt = nil) and (iterElt <> nil)) do begin
      locElt
:= (iterElt as TDOMElement).getAttributeNode(strNodeName);
     
if locElt <> nil then break else iterElt := iterElt.nextSibling;
   
end;


PH

unread,
May 26, 2016, 4:36:41 AM5/26/16
to WST-LIST
maanantai 23. toukokuuta 2016 13.06.20 UTC+3 PH kirjoitti:

I figure GetCurrentScopeObject() gives the wrong element to examine since it was examining the second header in the response, which corresponded the position of my request's header elements. Somewhat hastily, I solved this by iterating through the siblings until no more siblings or I found the attribute, but I'm not sure if this is the best solution...

iterElt : TDOMNode;
...
 
end else begin
    iterElt
:= GetCurrentScopeObject();
   
while ((locElt = nil) and (iterElt <> nil)) do begin
      locElt
:= (iterElt as TDOMElement).getAttributeNode(strNodeName);
     
if locElt <> nil then break else iterElt := iterElt.nextSibling;
   
end;


This isn't actually a good solution especially since I have two elements with same attribute name in a request. Root cause with my problem is that when header xml attributes are deserialized into object values, the order of headers is important when it shouldn't be(?). Have to find the root fix for this...

I checked out the svn source now and see some fixes for problems, but also some weird errors. Running ws_helper again produced
function XRoadIdentifierType.wstHas_xRoadInstance() : Boolean;
begin
 
Result := ( FxRoadInstance <> xRoadInstance_Type(0) );
end;


for all UnicodeString-types, where the xRoadInstance_Type(0) gives an invalid cast (should be '' IMO).

Also record_rtti.pas didn't compile with Lazarus, for some reason {$IFDEF FPC} was now {$IFDEF FPC_XXXX} so I changed mine back.

Small typo in wst_consts.pas, should be
SERR_ParamaterNotFound           = 'Parameter not found : "%s".';

Any plans for supporting doc/lit wrapped for server side? I tested commenting out some parts of base_soap_formatter and server_service_soap and I seem to get properly formed responses working :D For now, thank you for this great library!

PH

unread,
May 27, 2016, 3:39:52 AM5/27/16
to WST-LIST


On Thursday, May 26, 2016 at 11:36:41 AM UTC+3, PH wrote:
maanantai 23. toukokuuta 2016 13.06.20 UTC+3 PH kirjoitti:

I figure GetCurrentScopeObject() gives the wrong element to examine since it was examining the second header in the response, which corresponded the position of my request's header elements. Somewhat hastily, I solved this by iterating through the siblings until no more siblings or I found the attribute, but I'm not sure if this is the best solution...

iterElt : TDOMNode;
...
 
end else begin
    iterElt
:= GetCurrentScopeObject();
   
while ((locElt = nil) and (iterElt <> nil)) do begin
      locElt
:= (iterElt as TDOMElement).getAttributeNode(strNodeName);
     
if locElt <> nil then break else iterElt := iterElt.nextSibling;
   
end;


This isn't actually a good solution especially since I have two elements with same attribute name in a request. Root cause with my problem is that when header xml attributes are deserialized into object values, the order of headers is important when it shouldn't be(?). Have to find the root fix for this...
First in
procedure TSOAPFormatter.BeginCallRead(ACallContext : ICallContext);
StackTop() becomes of type TScopedArrayStackItem by
PushStack(hdrNd,asScoped,'').SetNameSpace(sSOAP_ENV);
where we the inlined part of
    asScoped  : Result := FStack.Push(TScopedArrayStackItem.Create(AScopeObject,stArray,AItemName)) as TStackItem;

pushes the header in as a TScopedArrayStackItem.

Then we enter header reading, finally arriving to
function TSOAPBaseFormatter.InternalBeginScopeRead
which calls
    locNode := stk.FindNode(strNodeName);
and now since stk is TScopedArrayStackItem, then FindNode is
function TAbstractArrayStackItem.FindNode(var ANodeName: string): TDOMNode;

This causes a bug with header object reading. FindNode pops from the stack instead of doing a search by ANodeName, so if my request headers aren't in some predefined order, their values are applied to other headers and especially cause errors when it tries to read header attributes into header classes without any attributes and puts wrong values into the elements.

Should TSOAPFormatter.BeginCallRead push the headernode into the FStack as a TObjectStackItem to fix this issue?

-PH
Reply all
Reply to author
Forward
0 new messages