Search code examples
c#parsingsoapwsdlcomplextype

How to get nested complexType parameters for an operation from a SOAP wsdl in c#


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.

This is the relevant part of the code:

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;
}

when I call getParams for SayHello it displays on command line: enter image description here

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:

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.

I have chanced the OutputElements like this:

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;
}

see the 5 lines after

// if it has children

so if found then check if has children, also I added the parent parameter/element to the param so I can use it when creating the envelope to call that operation with.


Solution

  • So now it works, the problem was that the solution I used the code from, initially: here

    was actually made custom for a type of wsdl, with one deep level of complexTypes, and I needed to work for ANY wsdl, I have now tried for a few I have and it works but it might be that I will find an example with some complex stuff that will not work for, in that case I will come back here and post the fixed code.

    Anyway the solution is this, at least for now as I say it seems to work for document and rpc and soap 1.1 and soap 1.2 wsdls:

    private static List<Tuple<string, string, string>> getParams(string methodName, XmlSchema schemaXML)
    {
        List<Tuple<string, string, string>> parameters = new List<Tuple<string, 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)
                        {
                            parameters.Add(new Tuple<string, string, string>(childElement.Name, childElement.SchemaTypeName.Name, schemaElement.Name));
                        }
                    }
                }
            }
            else if (complexType != null && complexType.Name == methodName)
            {
                Console.Out.WriteLine("Complex Type: {0}", complexType.Name);
                List<Tuple<string, string, string>> moreparams = OutputElements(complexType.Particle, complexType.Name);
                if(moreparams != null && moreparams.Count !=0)
                {
                    parameters.AddRange(moreparams);
                }
            }
        }
        // Loop through all detected imports in the main schema
        List<Tuple<string, string, string>> importparameters = ImportIncludedSchemasRecursively(wsdlUrl, methodName, xmlSchema);
        if (importparameters != null && importparameters.Count != 0)
        {
            parameters.AddRange(importparameters);
        }
        return parameters;
    }
    
    private static List<Tuple<string, string, string>> ImportIncludedSchemasRecursively(string mainWsdlUrl, string methodName, XmlSchema currentWsdlSchema)
    {
        List<Tuple<string, string, string>> parameters = new List<Tuple<string, 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, string>> complexparams = getParams(methodName, schema);
                if (complexparams != null && complexparams.Count != 0)
                {
                    parameters.AddRange(complexparams);
                }
    
                List<Tuple<string, 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, 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;
    }
    

    This, given ANY parsed wsdl, see the initial/original post's code for this, will give for a operation the parameters to call it with together with the parent for each.