none
Serialize属性をつけたクラスのWeb参照の更新について RRS feed

  • 質問

  • 同じ名前空間内に、2つのクラスを作成し、各々のクラス内に同じ名称のクラスをネストして作成し、
    いずれのクラスにも[Serialize]属性をつけ、そのインスタンスを取得するためのWebMethodを作成して、Web参照の更新を行った ところエラーが発生。
    同一の名前空間内に同じクラスを定義できないというエラーだったため、名前空間を指定するための下のような属性を追加した。
    [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://tempuri.org/testclass1")]
    その後Web参照の更新を行ったところエラーは発生しなかったが、Reference.csに定義された
    ネストしたクラスのクラス名が、「クラス名1」のように後に1が追加されていた。
    しかもネストしてほしいところネストが解除されていた。
    入れ子のクラスを入れ子のままReference.csに記述されるようにするにはどうしたら良いでしょうか。

    (1)クラスの定義とWebメソッド

    namespace WebService1
    {
     /// <summary>
     /// Service1 の概要の説明です
     /// </summary>
     [WebService(Namespace = "http://tempuri.org/")]
     [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
     [ToolboxItem(false)]
     public class Service1 : System.Web.Services.WebService
     {

      [WebMethod]
      public string HelloWorld()
      {
       return "Hello World";
      }

      [WebMethod]
      public testclass1 GetTest1()
      {
       testclass1 t = new testclass1();
       return t;
      }
      [WebMethod]
      public testclass2 GetTest2()
      {
       testclass2 t = new testclass2();
       return t;
      }
     }

     [Serializable()]
     [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://tempuri.org/testclass1")]
     public class testclass1
     {
      public testclassSub[] tstsub;
      [Serializable()]
      public class testclassSub{}
     }

     [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://tempuri.org/testclass2")]
     [Serializable()]
     public class testclass2
     {
      public testclassSub[] tstsub;
      [Serializable()]
      public class testclassSub{}
     }

    (2)作成されたReference.cs 抜粋

        /// <remarks/>
        [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.4016")]
        [System.SerializableAttribute()]
        [System.Diagnostics.DebuggerStepThroughAttribute()]
        [System.ComponentModel.DesignerCategoryAttribute("code")]
        [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/testclass1")]
        public partial class testclassSub {
        }
       
        /// <remarks/>
        [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.4016")]
        [System.SerializableAttribute()]
        [System.Diagnostics.DebuggerStepThroughAttribute()]
        [System.ComponentModel.DesignerCategoryAttribute("code")]
        [System.Xml.Serialization.XmlTypeAttribute(TypeName="testclassSub", Namespace="http://tempuri.org/testclass2")]
        public partial class testclassSub1 {
        }

     

    2011年7月13日 2:17

回答

  • WebService はWSDL という記述言語によって宣言されるようになっていて、Visual Studio は C# のクラス等を WSDL に変換したり、WSDL を元に C# のクラス等を生成することを助けてくれるようになっています。

    WSDL の仕様には詳しくありませんが、WSDL でネストされたクラス構造を表現できないか、Visual Studio の変換がサポートしていないという理由で、サービス定義側で

    > 同一の名前空間内に同じクラスを定義できない

    という問題が発生します。(おそらく、前者= WSDL ではネストされたクラスがサポートされていないため ではないかと想像します)

    このようにして作成されたサービスをインポートすると、今度は WSDL から Reference.cs 等が作成されるわけですが、WSDL には Types として同じ名前のものが2つあります。C# では同じ名前のクラスを複数作成できないので、Visual Studio の自動生成機能では後ろに連番のついたクラス名を生成します。

    解決方法としては、

    • サービス定義側で作成した WebMethod が WSDL 等の仕様によってどのように公開されるかを考えて、適切な名称を与える。(namespace と同様に typename で指定できます)
    • サービス呼び出し側で自動生成されたコードを使用しない。(自動生成されたコードを、期待したとおりのネストされた型構造にあわせて手作業で調整する)

    前者の場合は、呼び出し側に自動生成される型の名前が連番ではなく、サービス定義側で指定した名前になるはずです。作成されているサービスと呼び出し側が親密な関係にあって、完全なペアである場合を除いて WSDL でどのように定義されているかは重要な情報になることが多いので、こちらの変更はできるだけやったほうがよいかと思います。

    後者は、自動生成されたコードは WebService の呼び出し処理だけですので、メソッド名や型名などを自由に変更できるので、素直に変更してしまえばよいということになります。サービス側の定義が変わって自動生成しなおす場合などを考えると、派生クラス等での対応も考えられます。

    前者と後者を両方実施するという手もあります。前者の対応をしておいて、重複しない判りやすい Type name を指定しておき、呼び出し側では自動生成されたコードとは別の呼び出し用クラスを作成します。

    • 回答としてマーク ts17682 2011年7月15日 8:31
    2011年7月13日 3:47