2011-11-16 6 views
95

私はJson.NETがDataContractJsonSerializerよりも高速で、それを試してみることにしたかった...Json.NETはストリームをシリアライズ/デシリアライズできますか?

しかし、私は、ストリームではなく、文字列を取るJsonConvert上の任意の方法を見つけることができなかったことを聞いたことがあります。

たとえば、WinPhoneでJSONを含むファイルを逆シリアル化するには、次のコードを使用してファイルの内容を文字列に読み込んだ後、JSONに逆シリアル化します。私はそれが間違ってやっている

// 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); 

...ストリームからまっすぐにデシリアライズするためにDataContractJsonSerializerを使用するよりも、テストの私(非常にアドホック)で約4倍遅くように見えますか?

ありがとう、

Omri。

答えて

36

UPDATE:もはやこの現在のバージョンで動作し、正しい答えをbelowを参照してください(否決する必要はありません、これは古いバージョンで正しいです)。

StreamReaderJsonTextReaderクラスを使用するか、直接 StreamReader取る JsonSerializerオーバーロードを使用します。

var serializer = new JsonSerializer(); 
serializer.Deserialize(streamReader); 
+18

これはもう動作しません。あなたはJsonReaderまたはTextReaderを使用しなければなりません – BradLaney

+3

これは人々がスクロールダウンする時を知るように、まだ動作しているバージョン番号を含めることができます。 – PoeHaH

172

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); 
    } 
} 

は、ドキュメンテーション:Deserialize JSON from a file stream

+2

JsonTextReaderはデフォルトでStreamReaderを閉じます。したがって、この例は、JsonTextReaderコンストラクタの呼び出しでStreamReaderを構築することによって少し簡略化できます。 –

+1

このコードと一緒にカスタムコンバータをどのように使うことができますか?シリアライザで使用するコンバータを指定する方法はありません – alwayslearning

+0

実際には、私はOutOfMemory例外があり、私はすでにこのコードをかなり正確に使用しています。これは保証されていません - 逆シリアル化されたオブジェクトが十分に大きく、32ビットプロセスでスタックされている場合、このコードでメモリエラーが発生する可能性があります – PandaWood

26

私は、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>(); 

が、それは他の誰かを助けることを願っています。

+2

そのようなStringオブジェクトを拡張するのは本当に良い考えですか? –

+1

* *に対して:拡張メソッドですべての文字列を汚染します。 *回避策*: 'SomeJsonHelpersNamespace'を必要に応じて宣言するか' this'キーワードを削除し、 'JsonHelpers.CreateFromJsonString(someJsonString)'を使うだけです。* Pro *:これはとても使いやすいです:) –

11

この質問には、オープンリストのオブジェクトのリストをSystem.IO.Streamにストリームして送信し、送信する前にリスト全体をバッファリングすることなく、それらを読み取る方法を探しました。 (具体的には、Web APIを介してMongoDBから永続オブジェクトをストリーミングしています)

@ 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が配置されているときに例外を受け取ることがあり注意が、私はこれを無視していました。

+0

私はこれを修正して完全なJSONオブジェクトを取得します。私のサーバーとクライアントはJSONのスニペットを送信して通信し、クライアントは '{"サインイン:{"username": "nick"}} {"購入アイテム":{"_ id": "32321123"}} 'とフラグメントを読み取るたびにイベントを通知するJSONの2つのフラグメントと見なす必要があります。 nodejでは、3行のコードでこれを行うことができます。 –

36
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); 
    } 
} 
+6

私はなぜライブラリがそれを単純なものにしていないのか疑問に思います... – Zyo

+0

ありがとう!これにより、非常に大きなオブジェクトコレクションを文字列にシリアル化してから、その文字列を(ストリームに直接シリアル化するのではなく)ストリームに書き込むときに、OutOfMemoryExceptionを回避できました。 –

+0

デシリアライズする前にストリームの位置を0にリセットしてください。 –

関連する問題