46 votos

Puede Json.NET serializar / deserializar a / desde un stream?

He oído que Json.NET es más rápido que DataContractJsonSerializer, y quería darle una oportunidad...

Pero no pude encontrar cualquier métodos en JsonConvert que tomar un stream en lugar de una cadena.

Para deserializar un archivo JSON que contiene en WinPhone, por ejemplo, yo uso el siguiente código para leer el contenido del archivo en una cadena y, a continuación, deserializar en JSON. Parece ser alrededor de 4 veces más lento en mi (muy ad-hoc) pruebas de que el uso de DataContractJsonSerializer deserializar directamente de la corriente...

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

¿Lo estoy haciendo mal?

Gracias,

Omri.

79voto

Rivers Puntos 583

La versión actual de Json.net no permitir el uso de aceptado el código de respuesta. Una alternativa actual es:

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

17voto

Paul Tyng Puntos 5203

ACTUALIZACIÓN: Esto no funciona en la versión actual, ver más abajo para la respuesta correcta (no hay necesidad de votar en contra, esto es correcto en versiones anteriores).

El uso de la JsonTextReader clase con un StreamReader o el uso de la JsonSerializer de sobrecarga que lleva un StreamReader directamente:

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

16voto

Tok' Puntos 160

He escrito una extensión de la clase para que me ayude a deserializar de JSON fuentes (cadena, secuencia, fichero).

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

Deserializar es ahora tan fácil como escribir:

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

Espero que ayude a alguien más.

5voto

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

4voto

Blake Mitchell Puntos 685

Llegué a esta pregunta buscando una manera de transmitir una abierta lista de objetos en un System.IO.Stream y leer el otro extremo, sin búfer toda la lista antes de enviar. (En concreto me estoy streaming de objetos almacenados de MongoDB a través de la Web API).

@Pablo Tyng y @Ríos hizo un excelente trabajo respondiendo a la pregunta original, y he utilizado sus respuestas para construir una prueba de concepto para mi problema. Me decidí a publicar mi consola de pruebas de aplicación en el caso de que cualquier persona se enfrenta al mismo problema.

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

Tenga en cuenta que puede recibir una excepción cuando el AnonymousPipeServerStream es dispuesto, me ignora esto, ya que no es relevante para el problema en cuestión.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: