186 votes

Json.NET peut-il sérialiser / désérialiser vers / à partir d'un flux?

J'ai entendu dire que Json.NET est plus rapide que DataContractJsonSerializer, et je voulais lui donner un essai...

Mais je ne pouvais pas trouver toutes les méthodes sur JsonConvert que de prendre un flux plutôt qu'une chaîne de caractères.

Pour la désérialisation d'un fichier JSON contenant sur WinPhone, par exemple, j'utilise le code suivant pour lire le contenu du fichier dans une chaîne de caractères, puis désérialiser en JSON. Il semble être d'environ 4 fois plus lent dans mon (très ad-hoc) à l'aide de tests que DataContractJsonSerializer pour désérialiser directement à partir du flux...

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

Suis-je le fais mal?

Merci,

Omri.

339voto

Rivers Points 583

La version actuelle de Json.net ne vous permet pas d'utiliser le code de réponse accepté. Une alternative actuelle est:

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

114voto

ygaradon Points 428
public static void Serialize( object value, Stream s ) 
{
    StreamWriter writer = new StreamWriter(s);
    JsonTextWriter jsonWriter = new JsonTextWriter(writer);
    JsonSerializer ser = new JsonSerializer();
    ser.Serialize(jsonWriter, value );
    jsonWriter.Flush();
}

public static T Deserialize<T>( Stream s ) 
{
    StreamReader reader = new StreamReader(s);
    JsonTextReader jsonReader = new JsonTextReader(reader);
    JsonSerializer ser = new JsonSerializer();
    return ser.Deserialize<T>(jsonReader);
}

63voto

Paul Tyng Points 5203

UPDATE: Cela ne fonctionne plus dans la version actuelle, voir ci-dessous pour la réponse correcte (pas besoin de voter, cela est correct pour les anciennes versions).

Utilisez la classe JsonTextReader avec un StreamReader ou utilisez la surcharge JsonSerializer qui prend un StreamReader directement:

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

32voto

Tok' Points 160

J'ai écrit une classe d'extension pour m'aider à désérialiser à partir de sources JSON (chaîne, flux, fichier).

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

Désérialiser est maintenant aussi simple que d'écrire:

 MyType obj1 = aStream.CreateFromJsonStream<MyType>();
MyType obj2 = "{\"key\":\"value\"}".CreateFromJsonString<MyType>();
MyType obj3 = "data.json".CreateFromJsonFile<MyType>();
 

J'espère que cela aidera quelqu'un d'autre.

17voto

Blake Mitchell Points 685

Je suis arrivé à cette question vous cherchez un moyen de diffuser une fin ouverte de la liste des objets sur un System.IO.Stream et de les lire à l'autre extrémité, sans mise en mémoire tampon de l'ensemble de la liste avant de l'envoyer. (Plus précisément, je streaming persisté objets de MongoDB sur les Web API.)

@Paul Tyng et @Rivières ont fait un excellent travail de répondre à la question d'origine, et j'ai utilisé leurs réponses à construire une preuve de concept pour mon problème. J'ai décidé de poster mon test de la console app ici au cas où quelqu'un d'autre est face à la même question.

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

Notez que vous pouvez recevoir une exception lorsque l' AnonymousPipeServerStream est éliminé, j'ai ignoré ce qu'il n'est pas pertinent pour le problème à portée de main.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X