【问题标题】:How to not serialize the __type property on JSON objects如何不序列化 JSON 对象的 __type 属性
【发布时间】:2009-03-09 17:59:01
【问题描述】:

我从ScriptServiceWebMethod 返回的每个对象都被包装到一个JSON 对象中,其中的数据位于一个名为d 的属性中。没关系。但我不希望将额外的 __type 属性提供给客户端,因为我使用 jQuery 进行手动处理。

有可能吗?

【问题讨论】:

  • ASP.NET 3.5 SP1,C# 服务器端 jQuery 客户端,执行 $.ajax 调用
  • 这些解决方案都不适合我我的回复中仍然有 __type。有没有人有更多的解决方案?

标签: c# asp.net json asmx javascriptserializer


【解决方案1】:

我发现,如果我将我的类的默认构造函数设为我的 web 方法返回除 public 以外的任何内容,它不会序列化 __type:ClassName 部分。

你可能想要声明你的默认构造函数protected internal ClassName() { }

【讨论】:

  • 我没有得到这个结果,尽管我正在返回一个对象数组。不确定这是否是问题。
  • 如果您不能将其设为非公开,并且您的对象来自不同的程序集,因此它不能是内部的,是否有任何解决方案?我很长时间没有 __type 了,现在它偶尔会出现,即使我已经定义了自己的 JavaScriptConverter :/
  • @Naor:我的 JS 对象包含一个数组,其中包含大约 1000 个具有两个属性(ID、名称)的简单对象。然后如果__type=namespace.name 被添加(1000 次),那么传输的数据量大约会增加一倍。
  • 这对我不起作用。有人可以发布一些示例代码吗?
  • 对我来说它也不起作用......仍然有这个 __type 属性。
【解决方案2】:

John 的解决方案对我不起作用,因为我返回的类型位于单独的 DLL 中。我可以完全控制那个 DLL,但是如果构造函数是内部的,我就不能构造我的返回类型。

我想知道返回类型是否是库中的公共类型甚至可能是原因 - 我已经做了很多 Ajax 并且以前没有见过这个。

快速测试:

  • 暂时将返回类型声明移到 App_Code 中。仍然得到__type 序列化。

  • 同上并为每个 JM 应用受保护的内部构造函数。这行得通(所以他得到了投票)。

奇怪的是,我没有得到具有通用返回类型的 __type

[WebMethod]
public static WebMethodReturn<IEnumerable<FleetObserverLiteAddOns.VehicleAddOnAccountStatus>> GetAccountCredits()

然而,对我来说解决方案是将返回类型保留在 DLL 中,但将 WebMethod 返回类型更改为对象,即

[WebMethod]
public static object ApplyCredits(int addonid, int[] vehicleIds) 

而不是

[WebMethod]
public static WebMethodReturn ApplyCredits(int addonid, int[] vehicleIds)

【讨论】:

    【解决方案3】:

    我一直在使用 .NET 4 WCF 服务尝试其中一些建议,但它们似乎不起作用 - JSON 响应仍包含 __type。

    我发现删除类型提示的最简单方法是将端点行为从 enableWebScript 更改为 webHttp。

        <behavior name="MapData.MapDataServiceAspNetAjaxBehavior">
          <webHttp />
        </behavior>
    

    如果您使用的是 ASP.NET AJAX 客户端,则需要默认 enableWebScript 行为,但如果您使用 JavaScript 或 jQuery 操作 JSON,那么 webHttp 行为可能是更好的选择。

    【讨论】:

    • 这是 .Net 4 Web 服务中唯一对我有用的东西。非常有用,谢谢。
    • 这种方式真是太棒了,'d'关键字同时被删除了。非常感谢。
    • 我还必须明确设置消息格式:[WebGet(ResponseFormat = WebMessageFormat.Json)]
    • ^ 从enableWebScript 切换到webHttp 后,我还必须指定defaultOutgoingResponseFormat="Json"automaticFormatSelectionEnabled="true"
    • 这对我有用,但是 /jsdebug 现在丢失了。如果你使用 asp:ScriptManager 来生成 js 存根,那就不好了。
    【解决方案4】:

    如果您使用的是ServiceStack.Text JSON Serializer,您只需:

    JsConfig.ExcludeTypeInfo = true;
    

    此功能已自动添加回v2.28,但上面的代码将其排除在序列化之外。您还可以通过Type 更改此行为:

    JsConfig<Type>.ExcludeTypeInfo = true;
    

    【讨论】:

      【解决方案5】:

      我想我已经缩小了神秘出现的“__type”的根本原因!

      这是一个您可以重新创建问题的示例。

      [WebService(Namespace = "http://tempuri.org/")]
      [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
      [System.ComponentModel.ToolboxItem(false)]
      [System.Web.Script.Services.ScriptService]
      public class Test : System.Web.Services.WebService
      {
          public class Cat
          {
              public String HairType { get; set; }
              public int MeowVolume { get; set; }
              public String Name { get; set; }
          }
      
          [WebMethod]
          public String MyMethodA(Cat cat)
          {
              return "return value does not matter";
          }
      
          [WebMethod]
          public Cat MyMethodB(String someParam)
          {
              return new Cat() { HairType = "Short", MeowVolume = 13, Name = "Felix the Cat" };
          }
      }
      

      这是关键部分!

      仅仅是因为 MyMethodA() 存在于同一个 .asmx 文件中并且 将 Cat 类作为参数...... __type 将被添加到调用另一个方法返回的 JSON 中:MyMethodB ()。

      虽然方法不同!!

      我的理论如下:

      1. 在编写这样的 Web 服务时,Microsoft 的代码会自动为您关联 JSON 序列化/反序列化行为,因为您使用了正确的属性,例如 [WebMethod] 和 [ScriptService]。
      2. 当这个自动魔术 Microsoft 代码执行时,它会找到一个将 Cat 类作为参数的方法。
      3. 它显示...哦...好吧....因为我将从 JSON 接收 Cat 对象...因此...如果我曾经返回一个 Cat 对象作为来自当前 Web 服务类中的任何方法的 JSON...我会给它一个 __type 属性,以便稍后在反序列化回 C# 时很容易识别。
      4. 呐-哈哈哈哈……

      重要提示

      您可以避免 __type 属性出现在生成的 JSON 中,方法是避免将有问题的类(在我的例子中为 Cat)作为 Web 服务中任何 WebMethods 的参数。因此,在上面的代码中,只需尝试修改 MyMethodA() 以删除 Cat 参数。这会导致 __type 属性生成。

      【讨论】:

      • 这也是我的问题。我有一个函数将类作为参数,另一个函数返回它。一旦我删除了输入类的函数,__type 就消失了。所以对我来说,我将为任何需要双向处理的类制作 2 个版本,一个用于请求,一个用于响应。这样我就不会得到额外的 __type 开销。
      【解决方案6】:

      为JavaScriptTypeResolver传入null,__type不会被序列化

      JavaScriptSerializer serializer = new JavaScriptSerializer(null);
      string json = serializer.Serialize(foo);
      

      【讨论】:

      • 尝试了上述所有解决方案。仍然没有为我工作。
      【解决方案7】:

      我不确定这是一个好的解决方案,但是如果您使用Json.net 库,您可以通过添加 [JsonIgnore] 属性来忽略某些属性。

      【讨论】:

      • 是的 Mironline,这是一个很好的解决方案。我个人使用它,我认为忽略该属性的最佳选择是 [jsonIngore],谢谢
      【解决方案8】:

      除了 John Morrison 对 DataContract 类中的 internalprotected internal 构造函数的建议之外,这对于 Web 服务和大多数 WCF 都非常有效,您可能还需要在您的 web.config 文件中进行额外的更改。 而不是 &lt;enableWebScript/&gt; 元素使用 &lt;webHttp/&gt; 作为您的端点行为,例如:

      <endpointBehaviors>
        <behavior name="MyServiceEndpoint">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
      

      【讨论】:

        【解决方案9】:

        我的 2 美分,但在当天很晚:正如其他人所提到的,似乎有两种方法可以防止“__type”属性:

        a) 保护无参数构造函数

        b) 避免将类作为参数传递给 Web 方法

        如果您永远不需要将类作为参数传递,那么您可以将构造函数设置为“受保护的内部”。如果您需要创建一个空对象,请添加工厂方法或其他带有虚拟参数的构造函数。

        但是,如果你需要将类作为参数传递给web方法,那么你会发现如果无参构造函数被保护,这将不起作用(ajax调用失败,大概是因为传入的json数据无法反序列化进入你的班级)。

        这是我的问题,所以我不得不结合使用 (a) 和 (b):保护无参数构造函数并创建一个虚拟派生类,专门用于 Web 方法的参数。例如:

        public class MyClass
        {
            protected internal MyClass() { }
            public MyClass(Object someParameter) { }
            ...
        }
        
        // Use this class when we need to pass a JSON object into a web method
        public class MyClassForParams : MyClass
        {
            public MyClassForParams() : base() { }
        }
        

        任何需要接受 MyClass 的 web 方法然后使用 MyClassForParams 代替:

        [WebMethod]
        [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
        public MyClass DoSomething(MyClassForParams someObject)
        {
            // Do something with someObject
            ...
            // Maybe return a MyClass object
            ...
        }
        

        【讨论】:

          【解决方案10】:

          不要使用 [Serializable] 属性。

          下面的就应该这样做

          JavaScriptSerializer ser = new JavaScriptSerializer(); string json = ser.Serialize(objectClass);

          【讨论】:

          • 它会输出一个 JSON 格式,该 JSON 在所有键上都已超出配额 (\") - 必须在客户端处理。为避免这种情况(双重 JSON 格式),请参阅 stackoverflow.com/a/3079326 。您还必须使用 System.Web.Extensions.dll 中的 System.Web.Script.Serialization 命名空间
          【解决方案11】:

          线程有点晚了,但这里继续。

          当添加到 json 字符串的属性是 List 时,我们遇到了同样的问题。我们所做的是添加另一个属性,它是一个 T 数组,类似于。

          之前。

          [DataMember]
          public List<Person> People { get; set; }
          

          之后。

          public List<Person> People { get; set; }
          
          [DataMember(Name = "People")]
          public Person[] Persons {
              get {
                  return People.ToArray();
              }
              private set { }
          }
          

          虽然不是理想的解决方案,但它可以解决问题。

          【讨论】:

            【解决方案12】:

            这是一种解决方法

                [WebMethod]
                [ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
                public void Status()
                {
                    MyObject myObject = new MyObject(); // Your class here
                    var json = Newtonsoft.Json.JsonConvert.SerializeObject(myObject);
            
                    HttpContext.Current.Response.Write(json);
                }
            

            【讨论】:

              【解决方案13】:

              这应该可以解决。

              在 System.WebExtensions.dll 中 JavaScriptSerializer 的私有 SerializeValue 方法中, 如果可以解析 __type,则将其添加到内部字典中。

              来自反射器:

              private void SerializeValue(object o, StringBuilder sb, int depth, Hashtable objectsInUse)
              {
                  if (++depth > this._recursionLimit)
                  {
                      throw new ArgumentException(AtlasWeb.JSON_DepthLimitExceeded);
                  }
                  JavaScriptConverter converter = null;
                  if ((o != null) && this.ConverterExistsForType(o.GetType(), out converter))
                  {
                      IDictionary<string, object> dictionary = converter.Serialize(o, this);
                      if (this.TypeResolver != null)
                      {
                          string str = this.TypeResolver.ResolveTypeId(o.GetType());
                          if (str != null)
                          {
                              dictionary["__type"] = str;
                          }
                      }
                      sb.Append(this.Serialize(dictionary));
                  }
                  else
                  {
                      this.SerializeValueInternal(o, sb, depth, objectsInUse);
                  }
              }
              

              如果无法确定类型,序列化仍会继续,但类型会被忽略。好消息是,由于匿名类型继承 getType() 并且返回的名称是由编译器动态生成的,因此 TypeResolver 为 ResolveTypeId 返回 null 并且“__type”属性随后被忽略。

              为了以防万一,我还在内部构造函数中采纳了 John Morrison 的建议,尽管仅使用这种方法,我仍然在我的 JSON 响应中获得 __type 属性。

              //Given the following class
              [XmlType("T")]
              public class Foo
              {
                  internal Foo()
                  {
              
                  }
              
                  [XmlAttribute("p")]
                  public uint Bar
                  {
                      get;
                      set;
                  }
              }
              
              [WebService(Namespace = "http://me.com/10/8")]
              [System.ComponentModel.ToolboxItem(false)]
              [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
              [ScriptService]
              public class MyService : System.Web.Services.WebService
              {
              
                  //Return Anonymous Type to omit the __type property from JSON serialization
                  [WebMethod(EnableSession = true)]
                  [System.Web.Script.Services.ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json, XmlSerializeString = false)]
                  public object GetFoo(int pageId)
                  {
                      //Kludge, returning an anonymois type using link, prevents returning the _type attribute.
                      List<Foo> foos = new List<Foo>();
                      rtnFoos.Add( new Foo(){
                          Bar=99
                      }};
              
                      var rtn = from g in foos.AsEnumerable()
                                 select g;
              
                      return rtn;
                  }
              }
              

              注意:我正在使用继承的 JSON 类型转换器,它从序列化类型中读取 XML 序列化属性以进一步压缩 JSON。感谢CodeJournal。像魅力一样工作。

              【讨论】:

              • 为什么他们会认为在您完成更改之后 修改您的自定义序列化是一个好主意?我在 ASP.NET 2.0 中,这里没有 var 关键字。知道有没有其他类似的方法?
              【解决方案14】:

              除了@sean 对使用JavaScriptSerializer 的回答。

              当使用 JavaScriptSerializer 并标记方法的 ResponseFormat = WebMessageFormat.Json 时,生成的响应具有双 JSON 编码,加上如果生成的响应是 string,它将被置于双引号之间。

              为避免这种情况,请使用this excellent answer 的解决方案将内容类型定义为 JSON(覆盖)并将JavaScriptSerializer 的二进制结果流式传输。

              来自上述答案的代码示例:

              public Stream GetCurrentCart()
              {
                  //Code ommited
                  var j = new { Content = response.Content, Display=response.Display,
                                SubTotal=response.SubTotal};
                  var s = new JavaScriptSerializer();
                  string jsonClient = s.Serialize(j);
                  WebOperationContext.Current.OutgoingResponse.ContentType =
                      "application/json; charset=utf-8";
                  return new MemoryStream(Encoding.UTF8.GetBytes(jsonClient));
              }
              

              JavaScriptSerializer 位于 System.Web.Script.Serialization 命名空间中,该命名空间位于 System.Web.Extensions.dll 中,默认情况下未引用。

              【讨论】:

                【解决方案15】:

                您可以使用创建自己的返回类型来发送响应。并且在发送响应时使用对象作为返回类型。所以 _type 属性将被忽略。

                【讨论】:

                  【解决方案16】:
                  var settings = new DataContractJsonSerializerSettings();
                  settings.EmitTypeInformation = EmitTypeInformation.Never;
                  DataContractJsonSerializer serializerInput = new DataContractJsonSerializer(typeof(Person), settings);
                  var ms = new MemoryStream();
                  serializerInput.WriteObject(ms, personObj);
                  string newRequest = Encoding.UTF8.GetString(ms.ToArray());
                  

                  【讨论】:

                    【解决方案17】:

                    这有点小技巧,但这对我有用(使用 C#):

                    s = (JSON string with "__type":"clsname", attributes)
                    string match = "\"__type\":\"([^\\\"]|\\.)*\",";
                    RegEx regex = new Regex(match, RegexOptions.Singleline);
                    string cleaned = regex.Replace(s, "");
                    

                    适用于 [DataContract][DataContract(Namespace="")]

                    【讨论】:

                    • 我 -1 因为建议别人这样做太糟糕了,最好什么也不说。
                    • 这是什么语言:s = (JSON string with "__type":"clsname", attributes) ??
                    猜你喜欢
                    • 1970-01-01
                    • 2019-03-21
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-08-22
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多