14

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

我们有一些具有许多子集合类属性的类,以及可能根据使用情况设置或不设置的其他属性。这些对象是从用 ScriptService 属性修饰的 ASP.NET .asmx WebService 返回的,因此在由各种 WebMethods 返回时通过 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 服务参数以使用泛型类.

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

4

5 回答 5

2

我知道这个线程已经安静了一段时间,但我想我会提出,如果您在自定义转换器中覆盖 JavaScriptConverter 的 SupportedTypes 属性,您可以添加应该使用转换器的类型。如有必要,这可以进入配置文件。这样你就不需要每个类的自定义转换器。

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

我在尝试解决上述问题时得到了这个想法,并偶然发现了 Nick Berardi 的“创建更准确的 JSON .NET 序列化程序”(google it)。

为我工作:)

谢谢大家。

于 2009-06-01T05:31:15.710 回答
2

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

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

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

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

}

序列化程序可能不太关心您发送给它的对象的类型,因为它只是将其转换为文本。

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

于 2009-04-29T14:30:03.843 回答
1

如果您使用.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

于 2008-10-14T06:03:33.613 回答
0

You can use the System.Runtime.Serialization.Json.DataContractJsonSerializer class in the System.ServiceModel.Web.dll assembly.

于 2008-10-01T21:20:41.187 回答
0

不要肯定地引用我的工作,但我相信这就是您正在寻找的。

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public XmlDocument GetXmlDocument()
{
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(_xmlString);
    return xmlDoc;
}
于 2008-10-01T21:49:21.320 回答