Question How to get nested complexType parameters for an operation from a SOAP wsdl?

whiteadi

Member
Joined
Nov 19, 2020
Messages
9
Programming Experience
10+
Having an WSDL and given one operation offered by it I want to parse it and fetch the input parameters for that operation, this example works for me only when there are no nested complex types:

How to parse an xsd file which has nested elements(complexType and simpleType elements and attributes)?

For this it works:

http://www.dneonline.com/calculator.asmx?wsdl

And that means it returns for all 4 operations the correct parameters (Add has AddSoapIn with intA and intB...)

but for this doesn't:

http://www.learnwebservices.com/services/hello?WSDL

It gets only to the HelloRequest for SayHello and does not fetch the element Name from HelloRequest.

This should work for any and not specific SOAP WSDLs, what I mean is a generic parsing.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
2,135
Location
Chesapeake, VA
Programming Experience
10+
Show us your code for parsing the WSDL.
 

whiteadi

Member
Joined
Nov 19, 2020
Messages
9
Programming Experience
10+
Show us your code for parsing the WSDL.
C#:
public TheClient(string url) {
        wsdlUrl = url;
        ReadServiceDescription();
        ServiceName = theService.Name;
    }

...
    
void ReadServiceDescription()
    {
        try
        {               
            XmlTextReader reader=new XmlTextReader (wsdlUrl);   
            ServiceDescription service=
                ServiceDescription.Read(reader);
            theService = service;
            _services.Add(service);
        }
        catch (Exception e)
        {                                                       
            throw e;
        }
    }

private static List<Tuple<string, string>> getParams(string methodName, XmlSchema schemaXML)
    {
        List<Tuple<string, string>> parameters = new List<Tuple<string, string>>();
        ServiceDescription serviceDescription = theService;
        XmlSchema xmlSchema;
        WebClient client = new WebClient(); ;
        //Drill down into the WSDL's complex types to list out the individual schema elements
        //and their data types
        Types types = serviceDescription.Types;
        if (schemaXML != null)
        {
            xmlSchema = schemaXML;
        } else
        {
            xmlSchema = types.Schemas[0];
        }

        foreach (object item in xmlSchema.Items)
        {
            XmlSchemaElement schemaElement = item as XmlSchemaElement;
            XmlSchemaComplexType complexType = item as XmlSchemaComplexType;

            if (schemaElement != null && methodName == schemaElement.Name)
            {
                Console.Out.WriteLine("Schema Element: {0}", schemaElement.Name);

                XmlSchemaType schemaType = schemaElement.SchemaType;
                XmlSchemaComplexType schemaComplexType = schemaType as XmlSchemaComplexType;

                if (schemaComplexType != null)
                {
                    XmlSchemaParticle particle = schemaComplexType.Particle;
                    XmlSchemaSequence sequence = particle as XmlSchemaSequence;
                    if (sequence != null)
                    {
                        foreach (XmlSchemaElement childElement in sequence.Items)
                        {
                            Console.Out.WriteLine("    Element/Type: {0}:{1}", childElement.Name, childElement.SchemaTypeName.Name);
                            parameters.Add(new Tuple<string, string>(childElement.Name, childElement.SchemaTypeName.Name));
                        }
                    }
                }
            }
            else if (complexType != null && complexType.Name == methodName)
            {
                Console.Out.WriteLine("Complex Type: {0}", complexType.Name);
                List<Tuple<string, string>> moreparams = OutputElements(complexType.Particle);
                if(moreparams != null && moreparams.Count !=0)
                {
                    parameters.AddRange(moreparams);
                }
            }
            //Console.Out.WriteLine();
        }
        // Loop through all detected imports in the main schema
        List<Tuple<string, string>> importparameters = ImportIncludedSchemasRecursively(wsdlUrl, methodName, xmlSchema);
        if (importparameters != null && importparameters.Count != 0)
        {
            parameters.AddRange(importparameters);
        }
        return parameters;
    }
    
    private static List<Tuple<string, string>> ImportIncludedSchemasRecursively(string mainWsdlUrl, string methodName, XmlSchema currentWsdlSchema)
    {
        List<Tuple<string, string>> parameters = new List<Tuple<string, string>>();

        foreach (XmlSchemaObject externalSchema in currentWsdlSchema.Includes)
        {
            // Read each external schema into a schema object
            if (externalSchema is XmlSchemaImport)
            {
                Uri baseUri = new Uri(mainWsdlUrl);
                Uri schemaUri = new Uri(baseUri, ((XmlSchemaExternal)externalSchema).SchemaLocation);

                WebClient http = new WebClient();
                Stream schemaStream = http.OpenRead(schemaUri);

                System.Xml.Schema.XmlSchema schema = XmlSchema.Read(schemaStream, null);
                List<Tuple<string, string>> complexparams = getParams(methodName, schema);
                if (complexparams != null && complexparams.Count != 0)
                {
                    parameters.AddRange(complexparams);
                }

                List<Tuple<string, string>> morecomplexparams = ImportIncludedSchemasRecursively(mainWsdlUrl.ToString(), methodName, schema);
                if (morecomplexparams != null && morecomplexparams.Count != 0)
                {
                    parameters.AddRange(morecomplexparams);
                }

            }
        }

        return parameters.Distinct().ToList();
    }

    private static List<Tuple<string, string>> OutputElements(XmlSchemaParticle particle)
    {
        List<Tuple<string, string>> parameters = new List<Tuple<string, string>>();

        XmlSchemaSequence sequence = particle as XmlSchemaSequence;
        XmlSchemaChoice choice = particle as XmlSchemaChoice;
        XmlSchemaAll all = particle as XmlSchemaAll;

        if (sequence != null)
        {
            for (int i = 0; i < sequence.Items.Count; i++)
            {
                XmlSchemaElement childElement = sequence.Items[i] as XmlSchemaElement;
                XmlSchemaSequence innerSequence = sequence.Items[i] as XmlSchemaSequence;
                XmlSchemaChoice innerChoice = sequence.Items[i] as XmlSchemaChoice;
                XmlSchemaAll innerAll = sequence.Items[i] as XmlSchemaAll;
                Console.Out.WriteLine("111 child: {0}", childElement.Name);
                if (childElement != null)
                {
                    parameters.Add(new Tuple<string, string>(childElement.Name, childElement.SchemaTypeName.Name));
                }
                else {
                    List<Tuple<string, string>> moreparams = OutputElements(sequence.Items[i] as XmlSchemaParticle);
                    if (moreparams != null && moreparams.Count != 0)
                    {
                        parameters.AddRange(moreparams);
                    }
                }
            }

            return parameters;
        }
        else if (choice != null)
        {
            Console.Out.WriteLine("  Choice");
            for (int i = 0; i < choice.Items.Count; i++)
            {
                XmlSchemaElement childElement = choice.Items[i] as XmlSchemaElement;
                XmlSchemaSequence innerSequence = choice.Items[i] as XmlSchemaSequence;
                XmlSchemaChoice innerChoice = choice.Items[i] as XmlSchemaChoice;
                XmlSchemaAll innerAll = choice.Items[i] as XmlSchemaAll;
                Console.Out.WriteLine("222 child: {0}", childElement.Name);
                if (childElement != null)
                {
                    parameters.Add(new Tuple<string, string>(childElement.Name, childElement.SchemaTypeName.Name));
                }
                else
                {                       
                    List<Tuple<string, string>> moreparams = OutputElements(choice.Items[i] as XmlSchemaParticle);
                    if (moreparams != null && moreparams.Count != 0)
                    {
                        parameters.AddRange(moreparams);
                    }
                }

            }
            return parameters;
        }
        else if (all != null)
        {
            for (int i = 0; i < all.Items.Count; i++)
            {
                XmlSchemaElement childElement = all.Items[i] as XmlSchemaElement;
                XmlSchemaSequence innerSequence = all.Items[i] as XmlSchemaSequence;
                XmlSchemaChoice innerChoice = all.Items[i] as XmlSchemaChoice;
                XmlSchemaAll innerAll = all.Items[i] as XmlSchemaAll;
                Console.Out.WriteLine("333 child: {0}", childElement.Name);
                if (childElement != null)
                {
                    parameters.Add(new Tuple<string, string>(childElement.Name, childElement.SchemaTypeName.Name));
                }
                else
                {
                    List<Tuple<string, string>> moreparams = OutputElements(all.Items[i] as XmlSchemaParticle);
                    if (moreparams != null && moreparams.Count != 0)
                    {
                        parameters.AddRange(moreparams);
                    }
                }
            }
            return parameters;
        }
        return parameters;
    }
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
2,135
Location
Chesapeake, VA
Programming Experience
10+
And when you step through your code with a debugger, what is happening as it finds the "HelloRequest"? My quick scan of that code dump is that it should recursively try to reading things, but granted, I'm only skimming through it and not actively trying to debug it.
 

whiteadi

Member
Joined
Nov 19, 2020
Messages
9
Programming Experience
10+
And when you step through your code with a debugger, what is happening as it finds the "HelloRequest"? My quick scan of that code dump is that it should recursively try to reading things, but granted, I'm only skimming through it and not actively trying to debug it.
when I call getParams for SayHello it displays on command line:
1605797660620.png

first debug from line 49 (in code of previous comment ;)),
second from line 70
and last one from line 138 of OutputElements function.

I also tried a to get the complexType, in this case HelloRequest, when there at line 139 is not null (childElement) and added as param,

and transform it to complex type:

C#:
XmlSchemaComplexType complexTypeChild = sequence.Items[i] as XmlSchemaComplexType;

similar as the SayHello, parent of HelloRequest is processed, and call for it again same function OutputElements with complexTypeChild, recursive

so then if has other child will work

but complexTypeChild is null.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
2,135
Location
Chesapeake, VA
Programming Experience
10+
Perhaps I'm not understanding what you were expecting to happen. "HelloRequest" is already the leaf node (see line 9):
XML:
<wsdl:types>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://learnwebservices.com/services/hello" elementFormDefault="qualified" targetNamespace="http://learnwebservices.com/services/hello" version="1.0">
    <xs:element name="SayHello" type="tns:SayHello"/>

    <xs:element name="SayHelloResponse" type="tns:SayHelloResponse"/>

    <xs:complexType name="SayHello">
      <xs:sequence>
        <xs:element name="HelloRequest" type="tns:helloRequest"/>
      </xs:sequence>
    </xs:complexType>
      
    <xs:complexType name="helloRequest">
      <xs:sequence>
        <xs:element name="Name" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
      
    <xs:complexType name="SayHelloResponse">
      <xs:sequence>
        <xs:element name="HelloResponse" type="tns:helloResponse"/>
      </xs:sequence>
    </xs:complexType>
      
    <xs:complexType name="helloResponse">
      <xs:sequence>
        <xs:element name="Message" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:schema>
</wsdl:types>
 

whiteadi

Member
Joined
Nov 19, 2020
Messages
9
Programming Experience
10+
Perhaps I'm not understanding what you were expecting to happen. "HelloRequest" is already the leaf node (see line 9):
XML:
<wsdl:types>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://learnwebservices.com/services/hello" elementFormDefault="qualified" targetNamespace="http://learnwebservices.com/services/hello" version="1.0">
    <xs:element name="SayHello" type="tns:SayHello"/>

    <xs:element name="SayHelloResponse" type="tns:SayHelloResponse"/>

    <xs:complexType name="SayHello">
      <xs:sequence>
        <xs:element name="HelloRequest" type="tns:helloRequest"/>
      </xs:sequence>
    </xs:complexType>
    
    <xs:complexType name="helloRequest">
      <xs:sequence>
        <xs:element name="Name" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
    
    <xs:complexType name="SayHelloResponse">
      <xs:sequence>
        <xs:element name="HelloResponse" type="tns:helloResponse"/>
      </xs:sequence>
    </xs:complexType>
    
    <xs:complexType name="helloResponse">
      <xs:sequence>
        <xs:element name="Message" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:schema>
</wsdl:types>

HelloRequest has also a child element called Name that is not a complex element but I do need ALL the input parameters of the request to be made so then

I can compose the envelope with the values that the sub created method for this operation will be caleld with, for exampel something like this:


XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
      <SayHello xmlns="http://learnwebservices.com/services/hello">
         <HelloRequest>
            <Name>John Doe</Name>
         </HelloRequest>
      </SayHello>
   </soapenv:Body>
</soapenv:Envelope>

SayHello -> HelloRequest -> Name (and I can get also type for it)

This works now, fetching all input parameters, for the ones without these nested complexTypes with provided code, for ex for this wsdl: http://www.dneonline.com/calculator.asmx?wsdl

I get correct params for each method, and the correct type for final elements, so I can construct the correct enevelope, for ex. for Add operation:

XML:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <Add xmlns="http://tempuri.org/">
      <intA>2</intA>
      <intB>3</intB>
    </Add>
  </soap:Body>
</soap:Envelope>

PS: I also have a BuildEnvelope that works if I get the correct parameters :)
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
2,135
Location
Chesapeake, VA
Programming Experience
10+
You need to build up the "object tree". Notice how the type of "HelloRequest" is "helloRequest":
XML:
<xs:element name="HelloRequest" type="tns:helloRequest"/>
And notice how "helloRequest" contains the "Name" that you are looking for:
XML:
<xs:complexType name="helloRequest">
  <xs:sequence>
    <xs:element name="Name" type="xs:string"/>
  </xs:sequence>
</xs:complexType>
 

whiteadi

Member
Joined
Nov 19, 2020
Messages
9
Programming Experience
10+
You need to build up the "object tree". Notice how the type of "HelloRequest" is "helloRequest":
XML:
<xs:element name="HelloRequest" type="tns:helloRequest"/>
And notice how "helloRequest" contains the "Name" that you are looking for:
XML:
<xs:complexType name="helloRequest">
  <xs:sequence>
    <xs:element name="Name" type="xs:string"/>
  </xs:sequence>
</xs:complexType>
But that is what the code getParams should do,. just that it doesn't, that is the problem.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
2,135
Location
Chesapeake, VA
Programming Experience
10+
As I said in post #4: step through the code with the debugger. Don't just run it. I think that you'll find that when you call OutputElements() on line 71 (which produced the output from line 138), that the rest of the foreach loop for in getParams() has not yet seen the succeeding elements.
 

whiteadi

Member
Joined
Nov 19, 2020
Messages
9
Programming Experience
10+
As I said in post #4: step through the code with the debugger. Don't just run it. I think that you'll find that when you call OutputElements() on line 71 (which produced the output from line 138), that the rest of the foreach loop for in getParams() has not yet seen the succeeding elements.
I will check indeed monday, have anice week end!
 

whiteadi

Member
Joined
Nov 19, 2020
Messages
9
Programming Experience
10+
Hi, I have added a few lines to OutputElements, see highligted ones (21-26):

C#:
private static List<Tuple<string, string, string>> OutputElements(XmlSchemaParticle particle, string parentName)
    {
        List<Tuple<string, string, string>> parameters = new List<Tuple<string, string, string>>();

        XmlSchemaSequence sequence = particle as XmlSchemaSequence;
        XmlSchemaChoice choice = particle as XmlSchemaChoice;
        XmlSchemaAll all = particle as XmlSchemaAll;

        if (sequence != null)
        {
            for (int i = 0; i < sequence.Items.Count; i++)
            {
                XmlSchemaElement childElement = sequence.Items[i] as XmlSchemaElement;
                XmlSchemaSequence innerSequence = sequence.Items[i] as XmlSchemaSequence;
                XmlSchemaChoice innerChoice = sequence.Items[i] as XmlSchemaChoice;
                XmlSchemaAll innerAll = sequence.Items[i] as XmlSchemaAll;
                
                if (childElement != null)
                {
                    parameters.Add(new Tuple<string, string, string>(childElement.Name, childElement.SchemaTypeName.Name, parentName));
                    // if it has children
                    List<Tuple<string, string, string>> moreparams = getParams(childElement.SchemaTypeName.Name, null);
                    if (moreparams != null && moreparams.Count != 0)
                    {
                        parameters.AddRange(moreparams);
                    }
                }
                else {
                    List<Tuple<string, string, string>> moreparams = OutputElements(sequence.Items[i] as XmlSchemaParticle, parentName);
                    if (moreparams != null && moreparams.Count != 0)
                    {
                        parameters.AddRange(moreparams);
                    }
                }
            }

            return parameters;
        }
        else if (choice != null)
        {
            Console.Out.WriteLine("  Choice");
            for (int i = 0; i < choice.Items.Count; i++)
            {
                XmlSchemaElement childElement = choice.Items[i] as XmlSchemaElement;
                XmlSchemaSequence innerSequence = choice.Items[i] as XmlSchemaSequence;
                XmlSchemaChoice innerChoice = choice.Items[i] as XmlSchemaChoice;
                XmlSchemaAll innerAll = choice.Items[i] as XmlSchemaAll;

                if (childElement != null)
                {
                    parameters.Add(new Tuple<string, string, string>(childElement.Name, childElement.SchemaTypeName.Name, parentName));
                }
                else
                {                       
                    List<Tuple<string, string, string>> moreparams = OutputElements(choice.Items[i] as XmlSchemaParticle, parentName);
                    if (moreparams != null && moreparams.Count != 0)
                    {
                        parameters.AddRange(moreparams);
                    }
                }

            }
            return parameters;
        }
        else if (all != null)
        {
            for (int i = 0; i < all.Items.Count; i++)
            {
                XmlSchemaElement childElement = all.Items[i] as XmlSchemaElement;
                XmlSchemaSequence innerSequence = all.Items[i] as XmlSchemaSequence;
                XmlSchemaChoice innerChoice = all.Items[i] as XmlSchemaChoice;
                XmlSchemaAll innerAll = all.Items[i] as XmlSchemaAll;

                if (childElement != null)
                {
                    parameters.Add(new Tuple<string, string, string>(childElement.Name, childElement.SchemaTypeName.Name, parentName));
                }
                else
                {
                    List<Tuple<string, string, string>> moreparams = OutputElements(all.Items[i] as XmlSchemaParticle, parentName);
                    if (moreparams != null && moreparams.Count != 0)
                    {
                        parameters.AddRange(moreparams);
                    }
                }
            }
            return parameters;
        }
        return parameters;
    }

Also I am passing the parent name of an element, to the parameter(s),to know where to attach it later when I create the envelope for calling the method.
 
Top Bottom