解决WebMethod返回JSON被{d:}包裹致UWP反序列化失败问题
d字段包裹的问题 这个问题我太熟悉了!你遇到的{"d":"..."}是ASMX WebMethod的默认行为——ASP.NET为了防止JSON劫持,会自动把返回值包装在d字段里。而你的代码里还额外手动序列化了一次对象成字符串,导致了双重序列化:WebMethod把你返回的JSON字符串当成普通字符串值,又给它套了一层d包装,最终返回的是转义后的字符串,自然反序列化不了TestClass。
下面给你几种靠谱的解决思路:
1. 最规范的做法:直接返回对象,让ASP.NET自动序列化
别手动调用JsonConvert.SerializeObject了!WebMethod支持直接返回强类型对象,ASP.NET会帮你完成序列化,而且返回的d字段里是正确的对象JSON(不是字符串)。
修改你的WebMethod:
[WebMethod] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public TestClass PostDropDowns() { TestClass t = new TestClass() { abc = "this has been sent over", Strings = new List<string> { "fdfdfd", "fdfdfdf" } }; // 直接返回对象,不用手动序列化 return t; }
然后客户端收到的响应是:
{"d":{"abc":"this has been sent over","Strings":["fdfdfd","fdfdfdf"]}}
这时候客户端可以通过两种方式反序列化:
方式A:用包装类接收
创建一个通用的包装类,专门处理ASMX的d字段:
public class JsonResponseWrapper<T> { public T d { get; set; } }
然后反序列化:
var wrapper = JsonConvert.DeserializeObject<JsonResponseWrapper<TestClass>>(responseFromServer); var dds = wrapper.d; // 这就是你要的TestClass实例
方式B:直接解析JObject取出d的值
如果不想创建包装类,可以用Newtonsoft.Json的JObject直接提取:
var responseObj = JObject.Parse(responseFromServer); var testClassJson = responseObj["d"].ToString(); var dds = JsonConvert.DeserializeObject<TestClass>(testClassJson);
2. 直接返回纯净JSON(你已经用的方法)
如果一定要绕过d字段,直接返回纯净的JSON,你手动写入响应流的方法是可行的。不过可以稍微优化一下代码,让它更简洁:
[WebMethod] public void PostDropDowns() { TestClass t = new TestClass() { abc = "this has been sent over", Strings = new List<string> { "fdfdfd", "fdfdfdf" } }; var json = JsonConvert.SerializeObject(t); var response = HttpContext.Current.Response; response.Clear(); response.ContentType = "application/json; charset=utf-8"; response.Write(json); response.End(); }
这种方法完全绕过了WebMethod的默认包装逻辑,直接输出JSON字符串,客户端收到的就是你预期的纯净JSON,不需要额外处理。
为什么会有d字段?
最后说下背景:ASP.NET添加d字段是为了防止JSON劫持攻击。如果直接返回数组(比如["a","b","c"]),攻击者可以通过<script>标签加载你的接口,然后劫持数组数据。而把返回值包装在对象里({"d": [...]}),脚本无法直接获取到数组,就避免了这个风险。所以如果没有特殊需求,更推荐用第一种规范的方法,兼顾安全和代码简洁。
内容的提问来源于stack exchange,提问作者iandavidson1982

