如何从ASP.NET Web服务实现自定义JSON序列化?

时间:2020-03-06 14:59:52  来源:igfitidea点击:

从WebService返回自定义类的实例时,有哪些序列化选项?

我们有一些带有许多子集合类属性的类,以及根据使用情况可能会或者可能不会设置的其他属性。这些对象是从装饰有ScriptService属性的ASP.NET .asmx WebService返回的,因此,当由各种WebMethod返回时,这些对象将通过JSON序列化进行序列化。

问题在于,开箱即用的序列化将返回所有公共属性(无论是否使用它们),并且以比我们想要限制的数量更多的冗长方式返回类名和其他信息。交通。

当前,对于返回的类,我们添加了处理JSON序列化的自定义javascript转换器,并将它们添加到web.config中,如下所示:

<system.web.extensions>
  <scripting>
    <webServices>
      <jsonSerialization>
        <converters>
          <add name="CustomClassConverter" type="Namespace.CustomClassConverter" />
        </converters>
      </jsonSerialization>
    </webServices>
  </scripting>
</system.web.extensions>

但这需要为每个类自定义转换器。是否有其他方法可以通过扩展服务,创建自定义序列化程序等来更改现成的JSON序列化?

跟进
@marxidad:

我们在其他应用程序中使用了DataContractJsonSerializer类,但是我一直无法弄清楚如何将其应用于这些服务。这是有关如何设置服务的示例:

[ScriptService]
public class MyService : System.Web.Services.WebService
{
    [WebMethod]
    public CustomClass GetCustomClassMethod
    {
        return new customClass();
    }
}

WebMethods由javascript调用,并返回以JSON序列化的数据。我们能够更改序列化的唯一方法是使用上面提到的javascript转换器?

有没有办法告诉WebService使用自定义DataContractJsonSerializer?是否通过web.config配置,使用属性装饰服务等?

更新
好吧,除了如上所述创建单个JavaScriptConverters之外,我们找不到其他方法来切换现成的JavaScriptSerializer。

为此,我们为避免必须创建单独的转换器而所做的就是创建一个通用的JavaScriptConverter。我们向想要处理的类中添加了一个空接口,并且在Web服务启动时调用的SupportedTypes使用反射来查找实现该接口类型的任何类型,如下所示:

public override IEnumerable<Type> SupportedTypes
{
  get
  {
    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
      AssemblyBuilder dynamicAssemblyCheck = assembly as AssemblyBuilder;
      if (dynamicAssemblyCheck == null)
      {
        foreach (Type type in assembly.GetExportedTypes())
        {
          if (typeof(ICustomClass).IsAssignableFrom(type))
          {
            yield return type;
          }
        }
      }
    }
  }
}

实际的实现略有不同,因此可以缓存类型,我们很可能会将其重构为使用自定义属性,而不是使用空接口。

但是,在处理自定义集合时,我们遇到了一个稍微不同的问题。这些通常只是扩展通用列表,但是使用自定义类而不是List <>本身,因为在集合类中通常存在自定义逻辑,排序等。

问题是JavaScriptConverter的Serialize方法返回一个字典,该字典作为具有相关类型的名称值对序列化为JSON,而列表作为数组返回。因此,使用转换器无法轻松地将收集类序列化。解决方案是不将那些类型包括在转换器的SupportedTypes中,而是将它们完美地序列化为列表。

因此,序列化是可行的,但是当我们尝试以其他方式将这些对象作为Web服务调用的参数传递时,反序列化会中断,因为不能将它们作为输入,将其视为字符串/对象字典的列表,这可以不会转换为该集合包含的任何自定义类的列表。我们可以发现的唯一方法是创建一个通用类,该通用类是字符串/对象字典的列表,然后将列表转换为适当的自定义集合类,然后更改任何Web服务参数以使用通用类代替。

我敢肯定,这里有很多问题和违反"最佳实践"的问题,但是它可以为我们完成工作,而无需创建大量自定义转换器类。

解决方案

我们可以在System.ServiceModel.Web.dll程序集中使用System.Runtime.Serialization.Json.DataContractJsonSerializer类。

一定不要在此方面引用我,但我相信这是我们想要的。

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public XmlDocument GetXmlDocument()
{
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(_xmlString);
    return xmlDoc;
}

如果我们使用的是.NET 3.x(或者可以),那么WCF服务将是最好的选择。

我们可以使用[DataMember]属性有选择地控制将哪些属性序列化到客户端。如果需要,WCF还允许对JSON序列化和反序列化进行更细粒度的控制。

这是一个很好的入门示例:http://blogs.msdn.com/kaevans/archive/2007/09/04/using-wcf-json-linq-and-ajax-passing-complex-types-to-wcf -services-with-json-encoding.aspx

如果不使用代码生成的类,则可以使用ScriptIgnoreAttribute装饰属性,以告诉序列化程序忽略某些属性。 Xml序列化具有类似的属性。

当然,如果要在一个服务方法调用上返回某个类的某些属性,而在另一服务方法调用上返回同一类的不同属性,则不能使用此方法。如果要这样做,请在服务方法中返回匿名类型。

[WebMethod]
[ScriptMethod]
public object GimmieData()
{
    var dalEntity = dal.GimmieEntity(); //However yours works...

    return new
       {
           id = dalEntity.Id,
           description = dalEntity.Desc
       };

}

序列化程序可能不太在乎我们发送给它的对象的类型,因为无论如何它只会将其转换为文本。

我也相信我们可以在数据实体上实现ISerializable(如果我们具有代码生成的数据实体,则可以作为部分类),以实现对序列化过程的细粒度控制,但是我没有尝试过。

我知道这个线程已经安静了一段时间,但是我想我要提供的是,如果我们在自定义转换器中覆盖JavaScriptConverter的SupportedTypes属性,则可以添加应该使用该转换器的类型。如有必要,可以将其放入配置文件中。这样,我们无需为每个类都使用自定义转换器。

我试图创建一个通用转换器,但无法在web.config中弄清楚如何识别它。很想知道是否有人管理过它。

在尝试解决上述问题时,我想到了这个主意,偶然发现了Nick Berardi的"创建更准确的JSON .NET序列化器"(谷歌搜索)。

为我工作:)

谢谢大家。