Потоковый адаптер для обеспечения интерфейса записи для утилиты записи, а также для предоставления интерфейса считывания.

Мне не нравится использовать объекты MemoryStream между потоковыми интерфейсами. Они неудобны, требуют повторного поиска в начале, а также пиковое использование памяти в сложных ситуациях.

Иногда утилита будет работать только определенным образом. Возможно, он будет выводить байты [] или записывать в поток, или это поток в конвейере, из которого вы читаете, извлекая данные.

Этот сериализатор 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. Есть ли библиотека, содержащая адаптер для таких ситуаций?

Я работаю над такой библиотекой, не ожидая, что такая библиотека будет.


person Todd    schedule 28.04.2014    source источник
comment
Я подтвердил, что это возможно, а также получил готовящееся решение для десериализации IEnumerable, используя JSON.NET, но вручную управляя сериализацией и десериализацией. Я подаю заявку на свое решение, прежде чем абстрагироваться в автономный проект для совместного использования. Я надеюсь, что функция десериализации IEnumerable может в конечном итоге найти свой путь в JSON.NET в качестве универсальной реализации (не требующей ручного управления сериализацией).   -  person Todd    schedule 29.04.2014


Ответы (1)


Ответ — новый проект 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.

person Todd    schedule 30.04.2014