【问题标题】:ASP.Net Webmethod: how to deserialize parameter with inheritance class?ASP.Net Webmethod:如何用继承类反序列化参数?
【发布时间】:2019-05-15 13:47:11
【问题描述】:

我在标有 [ScriptService] 的类中定义了一个 WebMethod,如下所示:

[WebMethod(enableSession: false), ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)]
public static JsonResult ReportApplicationActivity(JsonApplicationActivityReport request, bool isTestUpload)
{ ... }

JsonApplicationActivityReport 类型的参数是一个抽象基类,可能是许多(一级)子类之一。 问题:当我从 WPF 客户端调用此 WebMethod 时,该方法永远不会被命中,并且在客户端抛出异常,说明类似于“内部服务器错误 (500)”。

我在 Global.asax 中的配置:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.SerializationBinder = new JsonApiUtils.InheritanceSerializationBinder();
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.All;

所以我试图告诉 JSON 序列化器使用自定义 SerializationBinder 来解决继承问题。它看起来像这样:

public class InheritanceSerializationBinder : DefaultSerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            switch (typeName)
            {
                case "JsonMeasurementActivityReport[]": return typeof(JsonMeasurementActivityReport[]);
                case "JsonMeasurementActivityReport": return typeof(JsonMeasurementActivityReport);
                case "JsonSimulationActivityReport[]": return typeof(JsonSimulationActivityReport[]);
                case "JsonSimulationActivityReport": return typeof(JsonSimulationActivityReport);
                default: return base.BindToType(assemblyName, typeName);
            }
        }
    }

但是 BindToType() 方法在请求期间永远不会被调用。 我还在 Web.config 中打开了跟踪,可以看到请求(每次调用 2 个,无论出于何种原因......)到达服务器,但 /trace.axd 中没有提供错误消息或任何对我有帮助的信息。

最后,这就是我的客户端代码的样子:

JsonApplicationActivityReport request = new JsonApplicationActivityReport () {...};
HttpWebRequest webRequest = GetWebRequest(Endpoints.ReportApplicationActivity);
using (var writer = webRequest.GetRequestStream())
{
    string jsonRequest = JsonConvert.SerializeObject(new { request = request, isTestUpload = isTestUpload }, Formatting.Indented, new JsonSerializerSettings {  TypeNameHandling = TypeNameHandling.All }); //also includes type information when serializing to JSON (required because the server expects an abstract class that has to be deserialized into the actual child activity class)
    byte[] requestData = Encoding.UTF8.GetBytes(jsonRequest);
    writer.Write(requestData, 0, requestData.Length);
}

var webResponse = (HttpWebResponse)webRequest.GetResponse();
var responseStream = new StreamReader(webResponse.GetResponseStream());

只要调用 GetResponse(),就会触发异常。

请告诉我我在这里缺少什么:)

【问题讨论】:

    标签: c# asp.net json inheritance webmethod


    【解决方案1】:

    WebService/ScriptService 不遵守您在Global.asax 中设置的序列化程序设置。它使用System.Web.Script.Serialization.JavaScriptSerializer,它被硬编码为WebServiceData,您对此没有太多控制权。

    但是,它允许您注册继承自 JavaScriptConverter 的自定义 JavaScript 转换器。为此,您需要在 Web.config 文件中使用 system.web.extensions/scripting/webServices/jsonSerialization/converters 配置。

    例如,假设您实现了JsonApplicationActivityReportConverter 来处理JsonApplicationActivityReport 类的反序列化。在这种情况下,配置如下所示

    <configuration>
      <!-- ... -->
      <system.web.extensions>
        <scripting>
          <webServices>
            <jsonSerialization>
              <converters>
                <add name="JsonApplicationActivityReportConverter" type="YourApplicationNamespace.JsonApplicationActivityReportConverter, YourApplicationAssemblyName"/>
              </converters>
            </jsonSerialization>
          </webServices>
        </scripting>
      </system.web.extensions>
    </configuration>
    

    但是,缺点是您必须重新发明大部分反序列化以在您的 JsonApplicationActivityReportConverter 中正确处理此问题。作为一个起点,这个类看起来像......

    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Web.Script.Serialization;
    
    namespace YourApplicationNamespace    {
        public class JsonApplicationActivityReportConverter : JavaScriptConverter
        {
            public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
            {
                var typeName = (string) dictionary["$type"];
                var typeInfo = Type.GetType(typeName);
                var result = Activator.CreateInstance(typeInfo);
    
                foreach (var property in typeInfo.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    if (property.CanWrite && dictionary.ContainsKey(property.Name))
                    {
                        property.SetValue(result, dictionary[property.Name]);
                    }
                }
    
                return result;
            }
    
            public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
            {
                // Reverse the process here if the type is to be used the other direction
            }
    
            public override IEnumerable<Type> SupportedTypes
            {
                get { yield return typeof(JsonApplicationActivityReport); }
            }
        }
    }
    

    【讨论】:

    • 谢谢,这个答案非常好,解决了我的问题。很遗憾,虽然没有注册自定义 JavaScriptConverter 的编程方式并且必须实现反序列化逻辑有点不方便。太糟糕了,他们没有将 JSON.Net 用于 WebMethods...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多