// -----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// -----------------------------------------------------------------------
namespace Microsoft.Samples.Kinect.Webserver
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using Microsoft.Samples.Kinect.Webserver.Properties;
///
/// Static class that defines extensions used to serialize/deserialize objects to/from
/// JSON strings.
///
public static class JsonSerializationExtensions
{
///
/// Serialization default buffer size.
///
private const int BufferSize = 512;
///
/// Serialize specified object to a stream as UTF8-encoded JSON.
///
///
/// Type of object to serialize.
///
///
/// Object to serialize.
///
///
/// Stream where UTF8-encoded JSON representing object will be output.
///
public static void ToJson(this T obj, Stream outputStream)
{
using (var writer = new StreamWriter(outputStream, new UTF8Encoding(false), BufferSize, true))
{
writer.Write(obj.ToJson());
}
}
///
/// Serialize specified object to a JSON string.
///
///
/// Type of object to serialize.
///
///
/// Object to serialize.
///
///
/// JSON string representing serialized object.
///
public static string ToJson(this T obj)
{
return (new JavaScriptSerializer()).Serialize(obj);
}
///
/// Serialize specified dictionary to a stream as UTF8-encoded JSON.
///
///
/// Dictionary to serialize.
///
///
/// Stream where UTF8-encoded JSON representing dictionary will be output.
///
///
///
/// Only IDictionary<string,object> objects and default types will be
/// recognized by the serializer.
///
///
/// Dictionaries mapping string keys to object values will be treated as direct
/// representations of JSON objects, so that a dictionary that contains:
///
///
/// a key "foo" that maps to a numeric value of 23
///
///
/// a key "bar" that maps to a string value of "tar"
///
///
/// will be serialized as {"foo":23,"bar":"tar"}, rather than as
/// [{"Key":"foo","Value":23},{"Key":"bar","Value":"tar"}], which is the default
/// way to serialize dictionary objects as JSON.
///
///
/// This method does not look for circular references and therefore does not
/// support them.
///
///
public static void DictionaryToJson(this IDictionary dictionary, Stream outputStream)
{
// Leave output stream open after we're done writing
using (var writer = new StreamWriter(outputStream, new UTF8Encoding(false), BufferSize, true))
{
writer.Write(dictionary.DictionaryToJson());
}
}
///
/// Asynchronously serialize specified dictionary to a stream as UTF8-encoded JSON.
///
///
/// Dictionary to serialize.
///
///
/// Stream where UTF8-encoded JSON representing dictionary will be output.
///
///
/// Await-able task.
///
///
///
/// Only IDictionary<string,object> objects and default types will be
/// recognized by the serializer.
///
///
/// Dictionaries mapping string keys to object values will be treated as direct
/// representations of JSON objects, so that a dictionary that contains:
///
///
/// a key "foo" that maps to a numeric value of 23
///
///
/// a key "bar" that maps to a string value of "tar"
///
///
/// will be serialized as {"foo":23,"bar":"tar"}, rather than as
/// [{"Key":"foo","Value":23},{"Key":"bar","Value":"tar"}], which is the default
/// way to serialize dictionary objects as JSON.
///
///
/// This method does not look for circular references and therefore does not
/// support them.
///
///
public static async Task DictionaryToJsonAsync(this IDictionary dictionary, Stream outputStream)
{
// Leave output stream open after we're done writing
using (var writer = new StreamWriter(outputStream, new UTF8Encoding(false), BufferSize, true))
{
await writer.WriteAsync(dictionary.DictionaryToJson());
}
}
///
/// Serialize specified dictionary to a JSON string.
///
///
/// Dictionary to serialize.
///
///
/// JSON string representing serialized object.
///
///
///
/// Only IDictionary<string,object> objects and default types will be
/// recognized by the serializer.
///
///
/// Dictionaries mapping string keys to object values will be treated as direct
/// representations of JSON objects, so that a dictionary that contains:
///
///
/// a key "foo" that maps to a numeric value of 23
///
///
/// a key "bar" that maps to a string value of "tar"
///
///
/// will be serialized as {"foo":23,"bar":"tar"}, rather than as
/// [{"Key":"foo","Value":23},{"Key":"bar","Value":"tar"}], which is the default
/// way to serialize dictionary objects as JSON.
///
///
/// This method does not look for circular references and therefore does not
/// support them.
///
///
public static string DictionaryToJson(this IDictionary dictionary)
{
return (new JavaScriptSerializer()).Serialize(dictionary);
}
///
/// Deserialize specified object from UTF8-encoded JSON read from a stream.
///
///
/// Type of object to deserialize.
///
///
/// Stream from which to read UTF8-encoded JSON representing serialized object.
///
///
/// Deserialized object corresponding to input JSON.
///
public static T FromJson(this Stream inputStream)
{
using (var reader = new StreamReader(inputStream, Encoding.UTF8))
{
return reader.ReadToEnd().FromJson();
}
}
///
/// Deserialize specified object from a JSON string.
///
///
/// Type of object to deserialize.
///
///
/// JSON string representing serialized object.
///
///
/// Deserialized object corresponding to JSON string.
///
///
/// Errors encountered during serialization might throw SerializationException.
///
public static T FromJson(this string input)
{
try
{
return (new JavaScriptSerializer()).Deserialize(input);
// Convert exceptions to Serialization exception to provide a single exception to
// catch for callers.
}
catch (ArgumentException e)
{
throw new SerializationException(@"Exception encountered deserializing JSON string", e);
}
catch (InvalidOperationException e)
{
throw new SerializationException(@"Exception encountered deserializing JSON string", e);
}
}
///
/// Deserialize specified dictionary from a stream as UTF8-encoded JSON.
///
///
/// Stream containing UTF8-encoded JSON representation of dictionary.
///
///
/// Deserialized dictionary.
///
///
///
/// Dictionaries mapping string keys to object values will be treated as direct
/// representations of JSON objects, so that a JSON object such as
/// {"foo":23,"bar":"tar"} will be deserialized as a dictionary that contains:
///
///
/// a key "foo" that maps to a numeric value of 23
///
///
/// a key "bar" that maps to a string value of "tar"
///
///
///
///
/// This method does not look for circular references and therefore does not
/// support them.
///
///
public static Dictionary DictionaryFromJson(this Stream inputStream)
{
using (var reader = new StreamReader(inputStream, Encoding.UTF8))
{
return reader.ReadToEnd().DictionaryFromJson();
}
}
///
/// Asynchronously deserialize specified dictionary from a stream as UTF8-encoded JSON.
///
///
/// Stream containing UTF8-encoded JSON representation of dictionary.
///
///
/// Await-able task Deserialized dictionary.
///
///
///
/// Dictionaries mapping string keys to object values will be treated as direct
/// representations of JSON objects, so that a JSON object such as
/// {"foo":23,"bar":"tar"} will be deserialized as a dictionary that contains:
///
///
/// a key "foo" that maps to a numeric value of 23
///
///
/// a key "bar" that maps to a string value of "tar"
///
///
///
///
/// This method does not look for circular references and therefore does not
/// support them.
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Clients won't have to create nested structure themselves. They will just await on task to get dictionary object.")]
public static async Task> DictionaryFromJsonAsync(this Stream inputStream)
{
using (var reader = new StreamReader(inputStream, Encoding.UTF8))
{
var input = await reader.ReadToEndAsync();
return input.DictionaryFromJson();
}
}
///
/// Deserialize specified dictionary from a JSON string.
///
///
/// JSON string containing JSON representation of dictionary.
///
///
/// Deserialized dictionary.
///
///
///
/// Dictionaries mapping string keys to object values will be treated as direct
/// representations of JSON objects, so that a JSON object such as
/// {"foo":23,"bar":"tar"} will be deserialized as a dictionary that contains:
///
///
/// a key "foo" that maps to a numeric value of 23
///
///
/// a key "bar" that maps to a string value of "tar"
///
///
///
///
/// This method does not look for circular references and therefore does not
/// support them.
///
///
/// Errors encountered during serialization might throw SerializationException.
///
///
public static Dictionary DictionaryFromJson(this string input)
{
try
{
return (new JavaScriptSerializer()).Deserialize>(input);
// Convert exceptions to Serialization exception to provide a single exception to
// catch for callers.
}
catch (ArgumentException e)
{
throw new SerializationException(@"Exception encountered deserializing JSON string", e);
}
catch (InvalidOperationException e)
{
throw new SerializationException(@"Exception encountered deserializing JSON string", e);
}
}
///
/// Extract serializable JSON data from specified value, ensuring that property names
/// match JSON naming conventions (camel case).
///
/// The object to be converted.
/// The converted object.
internal static object ExtractSerializableJsonData(object value)
{
if (value == null || IsSerializationPrimitive(value))
{
return value;
}
if (IsDictionary(value))
{
// The key type for the given dictionary must be string type.
if (!IsSerializableGenericDictionary(value) && !IsSerializableDictionary(value))
{
throw new NotSupportedException(Resources.UnsupportedKeyType);
}
var result = new Dictionary();
var dict = (IDictionary)value;
foreach (var key in dict.Keys)
{
result.Add((string)key, ExtractSerializableJsonData(dict[key]));
}
return result;
}
if (IsEnumerable(value))
{
// For the object with IEnumerable interface, serialize each items in it.
var result = new List