2015-10-05 15 views
5

La mia applicazione serializza un oggetto utilizzando Json.Net, comprime il JSON risultante, quindi lo salva in un file. Inoltre l'applicazione può caricare un oggetto da uno di questi file. Questi oggetti possono essere decine di MB di dimensione e sono preoccupati per l'utilizzo della memoria, a causa del modo in cui il codice esistente crea grandi stringhe e array di byte: -Posso decomprimere e deserializzare un file utilizzando i flussi?

public void Save(MyClass myObject, string filename) 
{ 
    var json = JsonConvert.SerializeObject(myObject); 
    var bytes = Compress(json); 
    File.WriteAllBytes(filename, bytes); 
} 

public MyClass Load(string filename) 
{  
    var bytes = File.ReadAllBytes(filename); 
    var json = Decompress(bytes); 
    var myObject = JsonConvert.DeserializeObject<MyClass>(json); 
} 

private static byte[] Compress(string s) 
{ 
    var bytes = Encoding.Unicode.GetBytes(s); 

    using (var ms = new MemoryStream()) 
    { 
     using (var gs = new GZipStream(ms, CompressionMode.Compress)) 
     { 
      gs.Write(bytes, 0, bytes.Length); 
      gs.Close(); 
      return ms.ToArray(); 
     } 
    } 
} 

private static string Decompress(byte[] bytes) 
{ 
    using (var msi = new MemoryStream(bytes)) 
    { 
     using (var mso = new MemoryStream()) 
     { 
      using (var gs = new GZipStream(msi, CompressionMode.Decompress)) 
      { 
       gs.CopyTo(mso); 
       return Encoding.Unicode.GetString(mso.ToArray()); 
      } 
     } 
    } 
} 

Mi chiedevo se i metodi Save/carico può essere sostituito con flussi? Ho trovato esempi di utilizzo di stream con Json.Net, ma sto cercando di capire come adattarmi alla compressione aggiuntiva.

+1

Questo può rivelarsi interessa a te http://benfoster.io/blog/aspnet-web-api-compression –

+0

@Roy Ho visto eccezioni OOM ultimamente e questo codice sembrava il colpevole logica. Sto aspettando che il profiler della memoria VS finisca di generare il suo report (quindi lento ...), quindi presto avrò un'idea migliore, ma ho pensato di provare a refactoring questo codice mentre sto twittando i miei pollici! –

+0

@AndrewStephens Ah goodo. Forse menziona la tua OOM nella domanda. In bocca al lupo! – MickyD

risposta

5

JsonSerializer ha metodi per serializzare da un JsonTextReader e un StreamWriter, entrambi i quali possono essere creati in cima a qualsiasi tipo di flusso, compreso un GZipStream. Il loro utilizzo, è possibile creare i seguenti metodi di estensione:

public static class JsonExtensions 
{ 
    public static void SerializeToFileCompressed(object value, string path, JsonSerializerSettings settings = null) 
    { 
     using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) 
      SerializeCompressed(value, fs, settings); 
    } 

    public static void SerializeCompressed(object value, Stream stream, JsonSerializerSettings settings = null) 
    { 
     using (var compressor = new GZipStream(stream, CompressionMode.Compress)) 
     using (var writer = new StreamWriter(compressor)) 
     { 
      var serializer = JsonSerializer.CreateDefault(settings); 
      serializer.Serialize(writer, value); 
     } 
    } 

    public static T DeserializeFromFileCompressed<T>(string path, JsonSerializerSettings settings = null) 
    { 
     using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) 
      return DeserializeCompressed<T>(fs, settings); 
    } 

    public static T DeserializeCompressed<T>(Stream stream, JsonSerializerSettings settings = null) 
    { 
     using (var compressor = new GZipStream(stream, CompressionMode.Decompress)) 
     using (var reader = new StreamReader(compressor)) 
     using (var jsonReader = new JsonTextReader(reader)) 
     { 
      var serializer = JsonSerializer.CreateDefault(settings); 
      return serializer.Deserialize<T>(jsonReader); 
     } 
    } 
} 

Vedi Performance Tips: Optimize Memory Usage nella documentazione Json.NET.

+1

Grandi cose. Mi stavo mescolando con il modo in cui i vari lettori e flussi dovrebbero essere annidati. Refactoring per utilizzare questo codice ha comportato un sostanziale miglioramento nell'utilizzo della memoria. –

1

Per coloro che sono alla ricerca di un'idea su come utilizzare le estensioni da @dbc nelle app uwp, ho modificato il codice a questo - dove StorageFile è un file a cui si ha accesso per scrivere.

public static async void SerializeToFileCompressedAsync(object value, StorageFile file, JsonSerializerSettings settings = null) 
{ 
    using (var stream = await file.OpenStreamForWriteAsync()) 
     SerializeCompressed(value, stream, settings); 
} 

public static void SerializeCompressed(object value, Stream stream, JsonSerializerSettings settings = null) 
{ 
    using (var compressor = new GZipStream(stream, CompressionMode.Compress)) 
    using (var writer = new StreamWriter(compressor)) 
    { 
     var serializer = JsonSerializer.CreateDefault(settings); 
     serializer.Serialize(writer, value); 
    } 
} 

public static async Task<T> DeserializeFromFileCompressedAsync<T>(StorageFile file, JsonSerializerSettings settings = null) 
{ 
    using (var stream = await file.OpenStreamForReadAsync()) 
     return DeserializeCompressed<T>(stream, settings); 
} 

public static T DeserializeCompressed<T>(Stream stream, JsonSerializerSettings settings = null) 
{ 
    using (var compressor = new GZipStream(stream, CompressionMode.Decompress)) 
    using (var reader = new StreamReader(compressor)) 
    using (var jsonReader = new JsonTextReader(reader)) 
    { 
     var serializer = JsonSerializer.CreateDefault(settings); 
     return serializer.Deserialize<T>(jsonReader); 
    } 
}