Может Json.NET сериализовать / десериализовать в / из потока?
Я слышал, что Json.NET это быстрее, чем DataContractJsonSerializer, и хотел дать ему попробовать...
но я не мог найти никаких методов на JsonConvert, которые принимают поток, а не строку.
для того, чтобы десериализовать файл, содержащий JSON на WinPhone, например, я использую следующий код, чтобы прочитать содержимое файла в строку, а затем десериализовать в JSON. Кажется, что он примерно в 4 раза медленнее в моем (очень специальном) тестировании, чем при использовании DataContractJsonSerializer для десериализации прямо из потока...
// DCJS
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(Constants));
Constants constants = (Constants)dc.ReadObject(stream);
// JSON.NET
string json = new StreamReader(stream).ReadToEnd();
Constants constants = JsonConvert.DeserializeObject<Constants>(json);
Я делаю это неправильно?
спасибо,
Омри.
5 ответов:
обновление: этот больше не работает в текущей версии, см. ниже для правильного ответа (не нужно голосовать против, это правильно, на старых версиях).
использовать
JsonTextReader
классStreamReader
или использоватьJsonSerializer
перегрузка, которая принимаетStreamReader
напрямую:var serializer = new JsonSerializer(); serializer.Deserialize(streamReader);
текущая версия Json.net не позволяет использовать принятый код ответа. Текущая альтернатива:
public static object DeserializeFromStream(Stream stream) { var serializer = new JsonSerializer(); using (var sr = new StreamReader(stream)) using (var jsonTextReader = new JsonTextReader(sr)) { return serializer.Deserialize(jsonTextReader); } }
документы: десериализовать JSON из потока файлов
public static void Serialize(object value, Stream s) { using (StreamWriter writer = new StreamWriter(s)) using (JsonTextWriter jsonWriter = new JsonTextWriter(writer)) { JsonSerializer ser = new JsonSerializer(); ser.Serialize(jsonWriter, value); jsonWriter.Flush(); } } public static T Deserialize<T>(Stream s) { using (StreamReader reader = new StreamReader(s)) using (JsonTextReader jsonReader = new JsonTextReader(reader)) { JsonSerializer ser = new JsonSerializer(); return ser.Deserialize<T>(jsonReader); } }
Я написал класс расширения, чтобы помочь мне десериализовать из источников JSON (строка, поток, файл).
public static class JsonHelpers { public static T CreateFromJsonStream<T>(this Stream stream) { JsonSerializer serializer = new JsonSerializer(); T data; using (StreamReader streamReader = new StreamReader(stream)) { data = (T)serializer.Deserialize(streamReader, typeof(T)); } return data; } public static T CreateFromJsonString<T>(this String json) { T data; using (MemoryStream stream = new MemoryStream(System.Text.Encoding.Default.GetBytes(json))) { data = CreateFromJsonStream<T>(stream); } return data; } public static T CreateFromJsonFile<T>(this String fileName) { T data; using (FileStream fileStream = new FileStream(fileName, FileMode.Open)) { data = CreateFromJsonStream<T>(fileStream); } return data; } }
десериализация теперь так же просто, как писать:
MyType obj1 = aStream.CreateFromJsonStream<MyType>(); MyType obj2 = "{\"key\":\"value\"}".CreateFromJsonString<MyType>(); MyType obj3 = "data.json".CreateFromJsonFile<MyType>();
надеюсь, что это поможет кому-то еще.
Я пришел к этому вопросу, ища способ передать открытый список объектов на
System.IO.Stream
и читать их с другого конца, без буферизации всего списка перед отправкой. (В частности, я передаю сохраненные объекты из MongoDB через веб-API.)@Paul Tyng и @Rivers отлично справились с ответом на исходный вопрос, и я использовал их ответы, чтобы построить доказательство концепции для моей проблемы. Я решил опубликовать свое тестовое консольное приложение здесь, Если кто-то еще сталкиваются с той же проблемой.
using System; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; namespace TestJsonStream { class Program { static void Main(string[] args) { using(var writeStream = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.None)) { string pipeHandle = writeStream.GetClientHandleAsString(); var writeTask = Task.Run(() => { using(var sw = new StreamWriter(writeStream)) using(var writer = new JsonTextWriter(sw)) { var ser = new JsonSerializer(); writer.WriteStartArray(); for(int i = 0; i < 25; i++) { ser.Serialize(writer, new DataItem { Item = i }); writer.Flush(); Thread.Sleep(500); } writer.WriteEnd(); writer.Flush(); } }); var readTask = Task.Run(() => { var sw = new Stopwatch(); sw.Start(); using(var readStream = new AnonymousPipeClientStream(pipeHandle)) using(var sr = new StreamReader(readStream)) using(var reader = new JsonTextReader(sr)) { var ser = new JsonSerializer(); if(!reader.Read() || reader.TokenType != JsonToken.StartArray) { throw new Exception("Expected start of array"); } while(reader.Read()) { if(reader.TokenType == JsonToken.EndArray) break; var item = ser.Deserialize<DataItem>(reader); Console.WriteLine("[{0}] Received item: {1}", sw.Elapsed, item); } } }); Task.WaitAll(writeTask, readTask); writeStream.DisposeLocalCopyOfClientHandle(); } } class DataItem { public int Item { get; set; } public override string ToString() { return string.Format("{{ Item = {0} }}", Item); } } } }
обратите внимание, что вы можете получить исключение, когда
AnonymousPipeServerStream
утилизируется, я проигнорировал это, поскольку это не имеет отношения к проблеме под рукой.