我从WebMethod
的ScriptService
返回的每个对象都被包装到一个JSON对象中,其中的数据名为d
。没关系。但我不希望将额外的__type
属性提供给客户端,因为我使用jQuery进行手动处理。
可能吗?
我发现,如果我创建我的类的默认构造函数,我的webmethod返回除public之外的任何东西,它将不会序列化__type:ClassName
部分。
您可能想要声明默认构造函数protected internal ClassName() { }
线程有点晚了但是这里有。
当添加到json字符串的属性是List <T>时,我们遇到了同样的问题。我们所做的是添加另一个属性,它是一个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 { }
}
虽然不是理想的解决方案,但它可以解决问题。
我的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
...
}
这是一种方法
[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);
}
这应该解决它。
在System.WebExtensions.dll中的JavaScriptSerializer的私有SerializeValue方法中,如果可以解析__type,则将其添加到内部字典中。
来自Reflector:
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。奇迹般有效。
除了@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
命名空间中,默认情况下未引用。
这有点像黑客,但这对我有用(使用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="")]
John的解决方案对我不起作用,因为我返回的类型是单独的DLL。我完全控制该DLL,但如果构造函数是内部的,我无法构造我的返回类型。
我想知道返回类型是否是库中的公共类型甚至可能是原因 - 我已经做了很多Ajax并且以前没有见过这个。
快速测试:
__type
序列化。奇怪的是我没有得到带有通用返回类型的__type
:
[WebMethod]
public static WebMethodReturn<IEnumerable<FleetObserverLiteAddOns.VehicleAddOnAccountStatus>> GetAccountCredits()
但是,我的解决方案是将我的返回类型保留在DLL中,但将WebMethod返回类型更改为object,即
[WebMethod]
public static object ApplyCredits(int addonid, int[] vehicleIds)
代替
[WebMethod]
public static WebMethodReturn ApplyCredits(int addonid, int[] vehicleIds)
我一直在使用.NET 4 WCF服务尝试其中一些建议,但它们似乎不起作用 - JSON响应仍然包含__type。
我发现删除类型提示的最简单方法是将端点行为从enableWebScript更改为webHttp。
<behavior name="MapData.MapDataServiceAspNetAjaxBehavior">
<webHttp />
</behavior>
如果您使用的是ASP.NET AJAX客户端,则需要默认的enableWebScript行为,但如果您使用JavaScript或jQuery操作JSON,则webHttp行为可能是更好的选择。
如果您正在使用ServiceStack.Text JSON Serializer,您只需:
JsConfig.ExcludeTypeInfo = true;
此功能在v2.28中自动添加回来,但上面的代码将其保留在序列化之外。您还可以通过Type
更改此行为:
JsConfig<Type>.ExcludeTypeInfo = true;
为JavaScriptTypeResolver传入null,并且不会序列化__type
JavaScriptSerializer serializer = new JavaScriptSerializer(null);
string json = serializer.Serialize(foo);
我想我已经缩小了神秘出现的“__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()。
即使他们是不同的方法!!
我的理论如下:
重要的拿走笔记
您可以避免在生成的JSON中出现__type属性,避免将有问题的类(我的情况下为Cat)作为Web服务中任何WebMethods的参数。因此,在上面的代码中,只需尝试修改MyMethodA()以删除Cat参数。这会导致不生成__type属性。
我不确定这是一个好的解决方案,但是如果你使用Json.net库,你可以通过添加[JsonIgnore]属性来忽略一些属性。
除了John Morrison对DataContract类中的内部或受保护内部构造函数的建议之外,它对Web服务和大多数WCF都非常有效,您可能需要在web.config文件中进行其他更改。而不是<enableWebScript/>
元素使用<webHttp/>
作为您的endpointBehaviors,例如:
<endpointBehaviors>
<behavior name="MyServiceEndpoint">
<webHttp/>
</behavior>
</endpointBehaviors>
不要使用[Serializable]属性。
以下应该这样做
JavaScriptSerializer ser = new JavaScriptSerializer(); string json = ser.Serialize(objectClass);