
Asked 11 years, 1 month ago
Modified 4 months ago
Viewed 237k times
186
I have heard that Json.NET is faster than DataContractJsonSerializer, and wanted to give it a try...
But I couldn't find any methods on JsonConvert that take a stream rather than a string.
For deserializing a file containing JSON on WinPhone, for example, I use the following code to read the file contents into a string, and then deserialize into JSON. It appears to be about 4 times slower in my (very ad-hoc) testing than using DataContractJsonSerializer to deserialize straight from the stream...
// 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);Am I doing it wrong?
Follow
155k3737 gold badges322322 silver badges275275 bronze badges
asked Nov 16, 2011 at 19:41
3,42822 gold badges2121 silver badges2727 bronze badges
Sorted by:
Highest score (default) Trending (recent votes count more) Date modified (newest first) Date created (oldest first)
339
The current version of Json.net does not allow you to use the accepted answer code. A current alternative is:
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);
}
}Documentation: Deserialize JSON from a file stream
Follow
47.3k2424 gold badges109109 silver badges129129 bronze badges
answered Jul 22, 2013 at 12:52
user2535425
stream.Position = 0; to correctly deserialize my json. – hybrid2102
Mar 21, 2019 at 14:54JsonTextReader – John Henckel
Nov 17 at 20:16114
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);
}
}Follow
2,3552525 silver badges4141 bronze badges
answered Mar 27, 2014 at 14:02
2,16822 gold badges2020 silver badges2727 bronze badges
JsonSerializer ser = JsonSerializer.Create(settings); you can define which settings to use during de/serialization. – mike
May 15, 2018 at 14:12Serialize implementation is that it closes the Stream passed as an argument, which depending on the application can be a problem. With .NET 4.5+ you can avoid this problem by using a StreamWriter constructor overload with a parameter leaveOpen that lets you leave the stream open. – Joe
Aug 29, 2019 at 13:2363
UPDATE: This no longer works in the current version, see below for correct answer (no need to vote down, this is correct on older versions).
Use the JsonTextReader class with a StreamReader or use the JsonSerializer overload that takes a StreamReader directly:
var serializer = new JsonSerializer();
serializer.Deserialize(streamReader);Follow
CommunityBot
111 silver badge
answered Nov 16, 2011 at 20:29
7,87811 gold badge3333 silver badges5757 bronze badges
32
I've written an extension class to help me deserializing from JSON sources (string, stream, file).
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;
}
}Deserializing is now as easy as writing:
MyType obj1 = aStream.CreateFromJsonStream<MyType>();
MyType obj2 = "{\"key\":\"value\"}".CreateFromJsonString<MyType>();
MyType obj3 = "data.json".CreateFromJsonFile<MyType>();Hope it will help someone else.
Follow
answered Jul 29, 2013 at 13:42
56588 silver badges1111 bronze badges
Using SomeJsonHelpersNamespace where needed or remove the this keyword and use JsonHelpers.CreateFromJsonString(someJsonString) Pro: it's so easier to use :) – Tok'
Jul 17, 2017 at 10:24 Encoding.UTF8. The code as is will produce garbled strings or fail to deserialize if non-ASCII characters are used. – ckuri
Oct 25, 2019 at 7:11 17
I arrived at this question looking for a way to stream an open ended list of objects onto a System.IO.Stream and read them off the other end, without buffering the entire list before sending. (Specifically I'm streaming persisted objects from MongoDB over Web API.)
@Paul Tyng and @Rivers did an excellent job answering the original question, and I used their answers to build a proof of concept for my problem. I decided to post my test console app here in case anyone else is facing the same issue.
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);
}
}
}
}Note that you may receive an exception when the AnonymousPipeServerStream is disposed, I ignored this as it isn't relevant to the problem at hand.
Follow
answered Feb 24, 2014 at 22:39
2,56711 gold badge2525 silver badges2222 bronze badges
{"sign in":{"username":"nick"}}{"buy item":{"_id":"32321123"}} and it needs to see this as two fragments of JSON signaling an event each time it reads a fragment. In nodejs this can be done in 3 lines of code. – Nick Sotiros
Jun 26, 2016 at 6:35-1
another option that is handy when you are running out of memory is to periodically flush
/// <summary>serialize the value in the stream.</summary>
/// <typeparam name="T">the type to serialize</typeparam>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
/// <param name="settings">The json settings to use.</param>
/// <param name="bufferSize"></param>
/// <param name="leaveOpen"></param>
public static void JsonSerialize<T>(this Stream stream,[DisallowNull] T value, [DisallowNull] JsonSerializerSettings settings, int bufferSize=1024, bool leaveOpen=false)
{
using (var writer = new StreamWriter(stream,encoding: System.Text.Encoding.UTF32,bufferSize,leaveOpen))
using (var jsonWriter = new JsonTextWriter(writer))
{
var ser = JsonSerializer.Create( settings );
ser.Serialize(jsonWriter, value);
jsonWriter.Flush();
}
}
/// <summary>serialize the value in the stream asynchronously.</summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
/// <param name="settings">The settings.</param>
/// <param name="bufferSize">The buffer size, in bytes, set -1 to not flush till done</param>
/// <param name="leaveOpen"> true to leave the stream open </param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
public static Task JsonSerializeAsync<T>(this Stream stream,[DisallowNull] T value, [DisallowNull] JsonSerializerSettings settings, int bufferSize=1024, bool leaveOpen=false, CancellationToken cancellationToken=default)
{
using (var writer = new StreamWriter(stream,encoding: System.Text.Encoding.UTF32,bufferSize: bufferSize,leaveOpen: leaveOpen))
using (var jsonWriter = new JsonTextWriter(writer))
{
var ser = JsonSerializer.Create( settings );
ser.Serialize(jsonWriter, value);
return jsonWriter.Flush();
}
//jsonWriter.FlushAsnc with my version gives an error on the stream
return Task.CompletedTask;
}You can test/ use it like so:
[TestMethod()]
public void WriteFileIntoJsonTest()
{
var file = new FileInfo(Path.GetTempFileName());
try
{
var list = new HashSet<Guid>();
for (int i = 0; i < 100; i++)
{
list.Add(Guid.NewGuid());
}
file.JsonSerialize(list);
var sr = file.IsValidJson<List<Guid>>(out var result);
Assert.IsTrue(sr);
Assert.AreEqual<int>(list.Count, result.Count);
foreach (var item in result)
{
Assert.IsFalse(list.Add(item), $"The GUID {item} should already exist in the hash set");
}
}
finally
{
file.Refresh();
file.Delete();
}
}you'd need to create the extension methods, here is the whole set:
public static class JsonStreamReaderExt
{
static JsonSerializerSettings _settings ;
static JsonStreamReaderExt()
{
_settings = JsonConvert.DefaultSettings?.Invoke() ?? new JsonSerializerSettings();
_settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
_settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
_settings.DateFormatHandling = DateFormatHandling.IsoDateFormat ;
}
/// <summary>
/// serialize the value in the stream.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="stream">The stream.</param>
/// <param name="value">The value.</param>
public static void JsonSerialize<T>(this Stream stream,[DisallowNull] T value)
{
stream.JsonSerialize(value,_settings);
}
/// <summary>
/// serialize the value in the file .
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="file">The file.</param>
/// <param name="value">The value.</param>
public static void JsonSerialize<T>(this FileInfo file,[DisallowNull] T value)
{
if (string.IsNullOrEmpty(file.DirectoryName)==true && Directory.Exists(file.DirectoryName) == false)
{
Directory.CreateDirectory(file.FullName);
}