通过调用静态方法的C#通用反序列化器

时间:2018-12-06 16:23:01

标签: c# json generics

我正在使用Newtonsoft Json.NET库反序列化大量数据。性能是高优先级,因此所有模型类都通过JsonReader手动反序列化。每个模型类都有自己的静态构造方法FromJson,该方法接受JsonReader进行读取。

class Example
{

  public Guid? Id { get; private set; }
  public DateTime? Date { get; private set; }
  public decimal? Amount { get; private set; }

  public static Example FromJson(JsonReader reader)
  {
    var example = new Example();

    reader.SkipToStartObject(); // Extension method, skips to first JsonToken.StartObject
    while(reader.Read() && reader.TokenType == JsonToken.PropertyName)
    {
      var propertyName = reader.Value.ToString();

      switch(propertyName)
      {
        case "id":
          example.Id = reader.ReadAsGuid(); // Extension method
          break;
        case "date":
          example.Date = reader.ReadAsDateTime();
          break;
        case "amount":
          example.Amount = reader.ReadAsDecimal();
          break;
        default:
          break;
      }
    }

    return example;
  }
}

我想以某种方式接口该类,以便我可以编写一个采用该接口并自动调用FromJson()方法的通用解串器。理想情况下,我将能够以这种方式对WebResponse进行反序列化。

var response = await request.GetResponseAsync();
var stream = response.GetResponseStream();

return GenericJsonDeserializer.Deserialize<Example>(stream);

GenericJsonDeserializer会将允许的类型限制为仅具有接口的类型,从流中设置JsonReader,使用FromJson方法反序列化,然后返回对象。

>

一个问题是C#接口不允许必需的构造函数,也不允许静态方法。因此,我不能约束GenericJsonSerializer

这个问题可以通过反思解决,但这带来了一个新问题。性能至关重要,在这种情况下,我不能使用反射。在通用方法内部创建新实例将是:

  • 如果反序列化代码是在常规构造函数中处理的,则要求使用Activator,或者
  • 需要反射来获取静态FromJson函数并调用它,这可能甚至更慢。

无论哪种情况,通过发出IL来编译DynamicMethod都是最佳选择(并且可能提供最佳性能),但是我想尽可能避免这种情况。

还有其他方法可以约束通用方法来要求静态构造函数或接受JsonReader进行反序列化的构造函数重载吗?

2 个答案:

答案 0 :(得分:1)

由于您在此处使用的是“示例”类型:

GenericJsonDeserializer.Deserialize<Example>(stream);

您可以使用:

Example.FromJson

因为您仍然需要知道类型。

只需创建一个接受Stream和JsonReader或任何其他版本的版本。 如果需要,您可以与其他静态类共享创建JsonReader的逻辑。

还有另一种方法。您可以将FromJson方法移动/提取到另一个类/接口:

interface IMyJsonDeserializer
{
    void FromJson(Stream stream, out ExampleClassA result);
    void FromJson(Stream stream, out ExampleClassB result);
}

class MyJsonDeserializer : IMyJsonDeserializer
{
    public void FromJson(Stream stream, out ExampleClassA result)
    {
        // code to deserialize
    }

    public void FromJson(Stream stream, out ExampleClassB result)
    {
        // code to deserialize
    }

    // .. more methods
}

用法:

var deserializer = new MyJsonDeserializer(); // you can create it just once somewhere

ExampleClassA a;
deserializer.FromJson(stream, out a);

ExampleClassB b;
deserializer.FromJson(stream, out b);

如果您有很多类,则可以进行一些接口隔离和继承。现在,您可以共享使用OOP方法从Stream创建JsonReader的逻辑。

如果您很喜欢香水,可以看看Utf8Json。它被证明比Newtonsoft.Json更快

答案 1 :(得分:1)

除了约束ctor外,您还可以约束到一个initialize方法:

  

自引用约束不是真正必要的

public interface IDeserializable<T> where T : IDeserializable<T>, new()
{
    T FromJson(JsonReader reader);
}

然后修改Example以实现该接口:

public class Example : IDeserializable<Example>
{
    //...

    public Example FromJson(JsonReader reader)
    {
        // populate the object with json...
        // you can create complex object like this:
        // this.Example2 = new Example2().FromJson(reader);

        return this;
    }
}

最后,如下定义Deserialize方法:

public static class GenericJsonSerializer
{
    public static T Deserialize<T>(Stream steam) where T :  IDeserializable<T>, new()
    {
        using (var reader = ...)
        {
            var result = new T();
            result.FromJson(reader);

            return result;
        }
    }
}