problema deserialización ASP.NET ScriptService con tipos derivados
-
26-09-2019 - |
Pregunta
Tengo un método ScriptService
web (.NET 3.5), que toma un único parámetro de un tipo de base abstracta:
[WebMethod(EnableSession=true)]
[ScriptMethod()]
public bool Test(Item item) { ... }
Y:
namespace Namespace {
public abstract class Item
{
public int id;
}
public class Group : Item
{
public Item[] items;
}
public class Instance : Item
{
public string whatever;
}
}
Por lo general, cuando se llama al método, item
será un Group
que contiene Instance
y / o Group
objetos. Voy a llamar a este servicio desde jQuery; No estoy usando el marco del lado del cliente Microsoft. Las llamadas a otros métodos bien el trabajo.
El problema: Cuando hago la llamada, se produce una excepción antes de que mi método es incluso invoca. Por ejemplo, si mi llamada es:
POST /WebService.asmx/Test HTTP/1.1
Content-Type: application/json; charset=UTF-8
Accept: application/json, text/javascript, */*
{"item":{"id":0,"__type":"Namespace.Group","items":[]}}
... me sale un InvalidOperationException
:
{"Message":"Operation is not valid due to the current state of the object.","StackTrace":" at System.Web.Script.Serialization.ObjectConverter.ConvertDictionaryToObject(IDictionary`2 dictionary, Type type, JavaScriptSerializer serializer, Boolean throwOnError, Object& convertedObject)\r\n at System.Web.Script.Serialization.ObjectConverter.ConvertObjectToTypeInternal(Object o, Type type, JavaScriptSerializer serializer, Boolean throwOnError, Object& convertedObject)\r\n at System.Web.Script.Serialization.ObjectConverter.ConvertObjectToTypeMain(Object o, Type type, JavaScriptSerializer serializer, Boolean throwOnError, Object& convertedObject)\r\n at System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth)\r\n at System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeDictionary(Int32 depth)\r\n at System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth)\r\n at System.Web.Script.Serialization.JavaScriptObjectDeserializer.BasicDeserialize(String input, Int32 depthLimit, JavaScriptSerializer serializer)\r\n at System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit)\r\n at System.Web.Script.Serialization.JavaScriptSerializer.Deserialize[T](String input)\r\n at System.Web.Script.Services.RestHandler.ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)","ExceptionType":"System.InvalidOperationException"}
Si me cae el miembro __type
del objeto JSON o cambiarla a Namespace.Item
(y eliminar el modificador abstract
de Item
), la excepción desaparece, pero el objeto deserializado resultante es obviamente un poco inútil.
¿Qué me falta?
Solución
Ah, ja! Al hacer clic en torno a los enlaces relacionados en el lado derecho de esta pregunta, he encontrado una respuesta que me ayudó a resolver este problema.
El atributo GenerateScriptType
se debe aplicar a la clase de servicio web:
[WebService( ... )]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
[GenerateScriptType(typeof(Group))]
[GenerateScriptType(typeof(Instance))]
public class WebService : System.Web.Services.WebService
{
[WebMethod(EnableSession=true)]
[ScriptMethod()]
public bool Test(Item item) { ... }
}
Sin estos atributos, el deserializer no sabe nada de mis tipos derivados.
Otros consejos
Me podría resolver esto utilizando Newtonsofts JsonConvert
como a continuación.
[System.Web.Services.WebMethod]
public static void MyAjaxCallingMethod(object inputs)
{
string stingDataFromAjaxCall = JsonConvert.SerializeObject(inputs);
MyObject myObj = JsonConvert.DeserializeObject<MyObject>(stingDataFromAjaxCall);
....}