Json.NET bir akışa / akıştan serileştirebilir / seriyi kaldırabilir mi?


155

Json.NET'in DataContractJsonSerializer'dan daha hızlı olduğunu duydum ve denemek istedim ...

Ancak JsonConvert üzerinde dizeden ziyade akış alan herhangi bir yöntem bulamadım.

Örneğin, WinPhone'da JSON içeren bir dosyanın serisini kaldırmak için, aşağıdaki kodu kullanarak dosya içeriğini bir dizeye okudum ve ardından JSON olarak seriyi kaldırıyorum. (Çok geçici) testimde, DataContractJsonSerializer'ı doğrudan akıştan seri durumdan çıkarmak için kullanmaktan yaklaşık 4 kat daha yavaş görünüyor ...

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

Yanlış mı yapıyorum?

Yanıtlar:


57

GÜNCELLEME: Bu artık mevcut sürümde çalışmamaktadır, doğru yanıt için aşağıya bakın ( geri oylamaya gerek yoktur, bu eski sürümlerde doğrudur ).

Kullan JsonTextReaderbir ile sınıf StreamReaderveya kullanmak JsonSerializerbir sürer aşırı StreamReaderdoğrudan:

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

25
Bunun artık çalışmadığından oldukça eminim. Bir JsonReader veya TextReader kullanmalısınız
BradLaney

9
İnsanların ne zaman aşağı kaydıracaklarını bilmeleri için hala üzerinde çalıştığı sürüm numarasını eklemek isteyebilirsiniz.
PoeHaH

@BradLaney yup JsonTextReader (givenStreamReader) şimdi gitmenin yolu
Antoine Meltzheim

Yanıtınızın çalışma durumunu yeniden düzenlemek ve öneriyi yanıtlamak için zaman ayırdığınız için teşekkür ederiz
Nick Bull

"Oy vermenize gerek yok, bu eski sürümlerde doğrudur" - Artık çoğu insan için geçerli değildir, bu nedenle daha iyi bir cevap olduğu için reddedilmelidir.
Ed S.

290

Json.net'in mevcut sürümü, kabul edilen cevap kodunu kullanmanıza izin vermiyor. Mevcut bir alternatif:

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

Dokümantasyon: Bir dosya akışından JSON serisini kaldır


4
JsonTextReader, StreamReader'ı varsayılan olarak kapatır, bu nedenle bu örnek, JsonTextReader yapıcısına yapılan çağrıda StreamReader'ı oluşturarak biraz basitleştirilebilir.
Oliver Bock

1
Bu kodla birlikte özel bir dönüştürücüyü nasıl kullanabileceğime dair bir fikriniz var mı? Serileştirici tarafından kullanılacak bir dönüştürücü belirlemenin hiçbir yolu yok
zaman

2
Aslında, bir OutOfMemory istisnam var ve bu kodu zaten tam olarak kullanıyorum. Ki inanıyorum ki, bu bir garanti değil - serileştirilmiş nesne yeterince büyükse ve 32 bitlik bir süreçte sıkışıp kalırsanız, bu kodla hala bellek hataları alabilirsiniz
PandaWood

1
Bir hata alıyorum "'JsonTextReader' türü veya ad alanı adı bulunamadı" ... herhangi bir öneri?
hnvasa

4
stream.Position = 0;Json'umu doğru şekilde seriyi kaldırmak için eklemem gerekiyordu.
hybrid2102

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

2
Teşekkürler! Bu, çok büyük bir nesne koleksiyonunu bir dizeye serileştirirken ve ardından bu dizeyi akışıma yazarken (doğrudan akışa dizgeleştirmek yerine) aldığım bir OutOfMemoryException'dan kaçınmama yardımcı oldu.
Jon Schneider

3
Neden floş? Kullanım bloğunun neden olduğu Dispose çağrısı zaten bunu yapmıyor mu?
Şafak Gür

bu nasıl kullanılır ?
Sana

4
Yan not, başkalarına yardımcı olabileceği için: eğer kullanırsanız JsonSerializer ser = JsonSerializer.Create(settings);, serileştirme sırasında hangi ayarların kullanılacağını tanımlayabilirsiniz.
mike

1
Bu Serializeuygulamayla ilgili olası bir sorun Stream, uygulamaya bağlı olarak bir sorun olabilen bir argüman olarak iletileni kapatmasıdır . .NET 4.5+ StreamWriterile leaveOpenakışı açık bırakmanıza izin veren bir parametre ile bir yapıcı aşırı yüklemesi kullanarak bu sorunu önleyebilirsiniz .
Joe

29

JSON kaynaklarından (dizge, akış, dosya) seriyi kaldırmama yardımcı olacak bir uzantı sınıfı yazdım.

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

Seriyi kaldırmak artık yazmak kadar kolay:

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

Umarım başkasına yardımcı olur.


2
Karşı : tüm dizeleri uzatma yöntemleriyle kirletecektir. Çözümler : Yalnızca Using SomeJsonHelpersNamespacegerektiğinde bildirin veya thisanahtar kelimeyi kaldırın ve Pro'yu kullanınJsonHelpers.CreateFromJsonString(someJsonString) : çok daha kolay :)
Tok '

1
"Kirletici" olarak görülebilmesine rağmen, String nesnesindeki uzantıların neredeyse yarısı aynı şekilde görülebilir. Bu, bir nesneyi sürekli olarak dizeden (json) JSON'a değişen herkes için yararlı olarak görülen bir şekilde genişletir.
vipersassassin

Ayrıca kullanmak Encoding.Default, farklı makinelerde farklı davranacağı için kötüdür (Microsoft docu'daki büyük uyarıya bakın). JSON'un UTF-8 olması bekleniyor ve bu, JsonSerializer'ın beklediği şey. Öyle olmalıdır Encoding.UTF8. Kod olduğu gibi bozuk dizeler üretir veya ASCII olmayan karakterler kullanılırsa seri durumdan çıkarılamaz.
ckuri

17

Bu soruya, açık uçlu bir nesne System.IO.Streamlistesini göndermeden önce tüm listeyi ara belleğe almadan, bir nesneye akıtmanın ve diğer uçtan okumanın bir yolunu ararken ulaştım. (Özellikle Web API üzerinden MongoDB'den kalıcı nesnelerin akışını yapıyorum.)

@Paul Tyng ve @Rivers orijinal soruyu yanıtlarken mükemmel bir iş çıkardılar ve ben de onların yanıtlarını sorunum için bir kavram kanıtı oluşturmak için kullandım. Başka birinin aynı sorunla karşılaşması durumunda test konsolu uygulamamı burada yayınlamaya karar verdim.

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

İmha edildiğinde bir istisna alabileceğinizi unutmayın, AnonymousPipeServerStreammevcut sorunla ilgili olmadığı için bunu görmezden geldim.


1
Herhangi bir eksiksiz JSON nesnesini alabilmek için bunu değiştirmem gerekiyor. Sunucum ve istemcim JSON parçacıkları göndererek iletişim kuruyor, böylece istemci gönderebiliyor {"sign in":{"username":"nick"}}{"buy item":{"_id":"32321123"}}ve bunu, bir parçayı her okuduğunda bir olayı işaret eden iki JSON parçası olarak görmesi gerekiyor. Nodejs'de bu 3 satır kodla yapılabilir.
Nick Sotiros
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.