流适配器提供向接口写入的实用程序,还提供读出接口

本文关键字:接口 实用程序 适配器 | 更新日期: 2023-09-27 18:00:31

我不喜欢在流接口之间使用MemoryStream对象。它们很尴尬,需要你重新寻找开始,在要求苛刻的情况下也会达到内存使用的峰值。

有时一个实用程序只能以某种方式工作。也许它会输出byte[]s,或者写入流,或者是您从中读取的管道中的流,从而将数据拉过。

这个Newtonsoft JSON序列化程序是一个只能写入流的实用程序。

var js = new Newtonsoft.Json.JsonSerializer();
var sw = new StreamWriter(ps);
js.Serialize(sw, o);

这对我来说是个问题,因为我想连锁:

  • IEnumerable
  • JSON序列化
  • GZIP压缩
  • HTTP到客户端
  • (网络)
  • 来自服务器的HTTP
  • GZIP解压缩
  • JSON反序列化
  • IEnumerable

除了让JSON反序列化器提供一个漂亮的IEnumerable接口的困难之外,其余部分都没有提供适合流水线的接口。即使是GZIP压缩端也是错误的。

理想情况下,在服务器端,我可以做到:

IEnumerable<object> o = GetData(); 
var js = new Newtonsoft.Json.JsonSerialization(o);
var gz = new System.IO.Compression.GZipStream(js, System.IO.Compression.CompressionMode.Compress, true);
return new FileStreamResult(gz, "application/x-gzip");

我可以扩展Newtonsoft项目以提供管道实现,我也可以这样做。但在那之前,我需要一个解决方案,我相信其他公用事业(包括BCL GZipStream)也需要一个。

  1. 有没有什么解决方案可以让人们更有效地加入这些公用事业
  2. 有没有一个库包含用于这种情况的适配器

我在建这样一个图书馆,没想到会有这样一个。

流适配器提供向接口写入的实用程序,还提供读出接口

答案是全新的StreamAdaptor项目:https://bitbucket.org/merarischroeder/alivate-stream-adaptor.它仍然需要一些工作——将它打包为NuGet包会很好,但它已经全部存在并经过了测试。

所以界面看起来有点像这样:

var data = GetData(); //Get the source data
var sa = new StreamAdaptor(); //This is what wraps the write-only utility source
sa.UpstreamSource((ps) => //ps is the dummy stream which does most of the magic
{
    //This anon. function is run on a separate thread and can therefore be blocked
    var sw = new StreamWriter(ps);
    sw.AutoFlush = true;
    var js = new Newtonsoft.Json.JsonSerializer();
    js.Serialize(sw, data); //This is the main component of the implementation
    sw.Flush();
});
var sa2 = new StreamAdaptor();
sa2.UpstreamSource((ps) =>
{
    using (var gz = new System.IO.Compression.GZipStream(ps, System.IO.Compression.CompressionMode.Compress, true))
        sa.CopyTo(gz);
});

有了对直通管道的自然支持,反向过程更容易

System.IO.Compression.GZipStream sw = new System.IO.Compression.GZipStream(sa2, System.IO.Compression.CompressionMode.Decompress);
var jsonTextReader = new JsonTextReader(new StreamReader(sw));
return TestA.Deserialize(jsonTextReader);

我还演示了IEnumerable<>的变通方法反序列化问题。它要求您使用JsonTextReader创建自己的反序列化程序,但效果很好。

序列化程序本机支持IEnumerable。上面的GetData函数使用IEnumerable函数(以及其他函数)为序列化程序设置数据源:

public static IEnumerable<TestB> GetTestBs()
{
    for (int i = 0; i < 2; i++)
    {
        var b = new TestB();
        b.A = "A";
        b.B = "B";
        b.C = TestB.GetCs();
        yield return b;
    }
}

这是沙漠化,需要一个变通办法。请记住,IEnumerable<>属性需要全部列在JSON流/对象的末尾,因为枚举是延迟的,但JSON反序列化是线性的。

反序列化入口点:

public static TestA Deserialize(JsonTextReader reader)
{
    TestA a = new TestA();
    reader.Read();
    reader.Read();
    if (!reader.Value.Equals("TestBs"))
        throw new Exception("Expected property 'TestBs' first");
    reader.Read(); //Start array
    a.TestBs = DeserializeTestBs(reader); //IEnumerable property last
    return a;
}

IEnumerable反序列化程序函数之一:

static IEnumerable<TestB> DeserializeTestBs(JsonTextReader reader)
{
    while (reader.Read())
    {
        if (reader.TokenType == JsonToken.EndArray)
            break;
        yield return TestB.Deserialize(reader);
    }
    reader.Read(); //End of object
}

当然,这可以通过反复试验来实现,尽管需要在JSON.NET中提供内置支持。