如何从 C# 中的 SOAP wsdl 获取操作的嵌套复杂类型参数

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

有了 WSDL 并给定了它提供的一个操作,我想解析它并获取该操作的输入参数,此示例仅在没有嵌套复杂类型时对我有效:

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

为此有效:

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

这意味着它 returns 所有 4 个操作的正确参数(Add 具有 AddSoapIn 和 intA 和 intB...)

但这不是:

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

它只获取 SayHello 的 HelloRequest,不从 HelloRequest 获取元素名称。

这应该适用于任何而非特定的 SOAP WSDL,我的意思是通用解析。

这是代码的相关部分:

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

当我为 SayHello 调用 getParams 时,它显示在命令行上:

从第 49 行开始调试(在之前评论的代码中 ;)), 第 70 行的第二个 最后一个来自 OutputElements 函数的第 138 行。

我还尝试获取复杂类型,在本例中为 HelloRequest,当第 139 行不为空(childElement)并添加为参数时,

并将其转换为复杂类型:

XmlSchemaComplexType complexTypeChild = sequence.Items[i] as XmlSchemaComplexType;

类似SayHello,处理HelloRequest的parent,再次调用同函数OutputElements with complexTypeChild,递归

所以如果有其他 child 会起作用

但是 complexTypeChild 为空。

我有机会得到这样的 OutputElements:

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

查看

之后的5行
// if it has children

所以如果找到然后检查是否有 children,我还向参数添加了 parent parameter/element 这样我就可以在创建信封时使用它来调用该操作.

我使用了 WSDL 并删除了架构部分。然后 运行 部分的 xsd.exe 并转到以下 c# 类

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System.Xml.Serialization;

// 
// This source code was auto-generated by xsd, Version=4.0.30319.33440.
// 


/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://learnwebservices.com/services/hello")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://learnwebservices.com/services/hello", IsNullable=false)]
public partial class SayHello {
    
    private helloRequest helloRequestField;
    
    /// <remarks/>
    public helloRequest HelloRequest {
        get {
            return this.helloRequestField;
        }
        set {
            this.helloRequestField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://learnwebservices.com/services/hello")]
public partial class helloRequest {
    
    private string nameField;
    
    /// <remarks/>
    public string Name {
        get {
            return this.nameField;
        }
        set {
            this.nameField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://learnwebservices.com/services/hello")]
public partial class helloResponse {
    
    private string messageField;
    
    /// <remarks/>
    public string Message {
        get {
            return this.messageField;
        }
        set {
            this.messageField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://learnwebservices.com/services/hello")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://learnwebservices.com/services/hello", IsNullable=false)]
public partial class SayHelloResponse {
    
    private helloResponse helloResponseField;
    
    /// <remarks/>
    public helloResponse HelloResponse {
        get {
            return this.helloResponseField;
        }
        set {
            this.helloResponseField = value;
        }
    }
}

所以现在它可以工作了,问题是我最初使用代码的解决方案:here

实际上是为一种 wsdl 定制的,具有一个深层次的 complexTypes,我需要为任何 wsdl 工作,我现在已经尝试了一些我已经拥有并且它有效但可能是我会的找到一个包含一些无法使用的复杂内容的示例,在那种情况下我会回到这里并 post 固定代码。

无论如何,解决方案是这样的,至少就目前而言,正如我所说,它似乎适用于 document 和 rpc 以及 soap 1.1 和 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;
}

这个,给定任何解析的 wsdl,请参阅 initial/original post 的代码,将为操作提供参数以与每个参数一起调用它。