ASP.Net Webmethod:如何使用继承类反序列化参数?

时间:2018-12-14 09:47:57

标签: c# asp.net json inheritance webmethod

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

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

类型为 JsonApplicationActivityReport 的参数是抽象基类,并且可以是许多(级别1)子类之一。 问题:当我从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(),就会触发异常。

请告诉我我在这里想念的东西:)

1 个答案:

答案 0 :(得分:1)

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

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

例如,假设您已实现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); }
        }
    }
}