如何不序列化 JSON 对象的 __type 属性

Posted

技术标签:

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

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

有可能吗?

【问题讨论】:

ASP.NET 3.5 SP1,C# 服务器端 jQuery 客户端,执行 $.ajax 调用 这些解决方案都不适合我我的回复中仍然有 __type。有没有人有更多的解决方案? 【参考方案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 ()。

虽然方法不同!!

我的理论如下:

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

重要提示

您可以避免 __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 格式),请参阅 ***.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) ??

以上是关于如何不序列化 JSON 对象的 __type 属性的主要内容,如果未能解决你的问题,请参考以下文章

反序列化时如何从json中删除k__BackingField

错误:预期状态。无法反序列化 JSON 对象

如何 JSON 序列化集合?

如何使python对象json序列化?

动态属性的 JSON 模式

ElasticSearch返回不同的type的序列化