簡體   English   中英

通過調用靜態方法的C#通用反序列化器

[英]C# Generic Deserializer by calling static methods

我正在使用Newtonsoft Json.NET庫反序列化大量數據。 性能是高度優先的,因此所有模型類都可以通過JsonReader手動反序列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進行反序列化的構造函數重載嗎?

由於您在此處使用類型“示例”:

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更快。

除了約束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;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM