如何不序列化 JSON 对象的 __type 属性
Posted
技术标签:
【中文标题】如何不序列化 JSON 对象的 __type 属性【英文标题】:How to not serialize the __type property on JSON objects 【发布时间】:2009-03-09 17:59:01 【问题描述】:我从ScriptService
的WebMethod
返回的每个对象都被包装到一个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 类中的 internal 或 protected internal 构造函数的建议之外,这对于 Web 服务和大多数 WCF 都非常有效,您可能还需要在您的 web.config 文件中进行额外的更改。
而不是 <enableWebScript/>
元素使用 <webHttp/>
作为您的端点行为,例如:
<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
之前。
[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 属性的主要内容,如果未能解决你的问题,请参考以下文章