Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

384 lines
15 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
#if FEATURE_DYNAMIC
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.IO;
using System.Json;
using System.Linq.Expressions;
namespace System.Runtime.Serialization.Json
{
/// <summary>
/// This class extends the functionality of the <see cref="JsonValue"/> type.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class JsonValueExtensions
{
/// <summary>
/// Creates a <see cref="System.Json.JsonValue"/> object based on an arbitrary CLR object.
/// </summary>
/// <param name="value">The object to be converted to <see cref="System.Json.JsonValue"/>.</param>
/// <returns>The <see cref="System.Json.JsonValue"/> which represents the given object.</returns>
/// <remarks>The conversion is done through the <see cref="System.Runtime.Serialization.Json.DataContractJsonSerializer"/>;
/// the object is first serialized into JSON using the serializer, then parsed into a <see cref="System.Json.JsonValue"/>
/// object.</remarks>
public static JsonValue CreateFrom(object value)
{
JsonValue jsonValue = null;
if (value != null)
{
jsonValue = value as JsonValue;
if (jsonValue == null)
{
jsonValue = JsonValueExtensions.CreatePrimitive(value);
if (jsonValue == null)
{
jsonValue = JsonValueExtensions.CreateFromDynamic(value);
if (jsonValue == null)
{
jsonValue = JsonValueExtensions.CreateFromComplex(value);
}
}
}
}
return jsonValue;
}
/// <summary>
/// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into the type T.
/// </summary>
/// <typeparam name="T">The type to which the conversion is being performed.</typeparam>
/// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
/// <param name="valueOfT">An instance of T initialized with this instance, or the default
/// value of T, if the conversion cannot be performed.</param>
/// <returns>true if this <see cref="System.Json.JsonValue"/> instance can be read as type T; otherwise, false.</returns>
public static bool TryReadAsType<T>(this JsonValue jsonValue, out T valueOfT)
{
if (jsonValue == null)
{
throw new ArgumentNullException("jsonValue");
}
object value;
if (JsonValueExtensions.TryReadAsType(jsonValue, typeof(T), out value))
{
valueOfT = (T)value;
return true;
}
valueOfT = default(T);
return false;
}
/// <summary>
/// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into the type T.
/// </summary>
/// <typeparam name="T">The type to which the conversion is being performed.</typeparam>
/// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
/// <returns>An instance of T initialized with the <see cref="System.Json.JsonValue"/> value
/// specified if the conversion.</returns>
/// <exception cref="System.NotSupportedException">If this <see cref="System.Json.JsonValue"/> value cannot be
/// converted into the type T.</exception>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter",
Justification = "The generic parameter is used to specify the output type")]
public static T ReadAsType<T>(this JsonValue jsonValue)
{
if (jsonValue == null)
{
throw new ArgumentNullException("jsonValue");
}
return (T)JsonValueExtensions.ReadAsType(jsonValue, typeof(T));
}
/// <summary>
/// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into the type T, returning a fallback value
/// if the conversion fails.
/// </summary>
/// <typeparam name="T">The type to which the conversion is being performed.</typeparam>
/// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
/// <param name="fallback">A fallback value to be retuned in case the conversion cannot be performed.</param>
/// <returns>An instance of T initialized with the <see cref="System.Json.JsonValue"/> value
/// specified if the conversion succeeds or the specified fallback value if it fails.</returns>
public static T ReadAsType<T>(this JsonValue jsonValue, T fallback)
{
if (jsonValue == null)
{
throw new ArgumentNullException("jsonValue");
}
T outVal;
if (JsonValueExtensions.TryReadAsType<T>(jsonValue, out outVal))
{
return outVal;
}
return fallback;
}
/// <summary>
/// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into an instance of the specified type.
/// </summary>
/// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
/// <param name="type">The type to which the conversion is being performed.</param>
/// <returns>An object instance initialized with the <see cref="System.Json.JsonValue"/> value
/// specified if the conversion.</returns>
/// <exception cref="System.NotSupportedException">If this <see cref="System.Json.JsonValue"/> value cannot be
/// converted into the type T.</exception>
public static object ReadAsType(this JsonValue jsonValue, Type type)
{
if (jsonValue == null)
{
throw new ArgumentNullException("jsonValue");
}
if (type == null)
{
throw new ArgumentNullException("type");
}
object result;
if (JsonValueExtensions.TryReadAsType(jsonValue, type, out result))
{
return result;
}
throw new NotSupportedException(RS.Format(System.Json.Properties.Resources.CannotReadAsType, jsonValue.GetType().FullName, type.FullName));
}
/// <summary>
/// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into an instance of the specified type.
/// </summary>
/// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
/// <param name="type">The type to which the conversion is being performed.</param>
/// <param name="value">An object to be initialized with this instance or null if the conversion cannot be performed.</param>
/// <returns>true if this <see cref="System.Json.JsonValue"/> instance can be read as the specified type; otherwise, false.</returns>
[SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate",
Justification = "This is the non-generic version of the method.")]
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception translates to fail.")]
public static bool TryReadAsType(this JsonValue jsonValue, Type type, out object value)
{
if (jsonValue == null)
{
throw new ArgumentNullException("jsonValue");
}
if (type == null)
{
throw new ArgumentNullException("type");
}
if (type == typeof(JsonValue) || type == typeof(object))
{
value = jsonValue;
return true;
}
if (type == typeof(object[]) || type == typeof(Dictionary<string, object>))
{
if (!JsonValueExtensions.CanConvertToClrCollection(jsonValue, type))
{
value = null;
return false;
}
}
if (jsonValue.TryReadAs(type, out value))
{
return true;
}
try
{
using (MemoryStream ms = new MemoryStream())
{
jsonValue.Save(ms);
ms.Position = 0;
DataContractJsonSerializer dcjs = new DataContractJsonSerializer(type);
value = dcjs.ReadObject(ms);
}
return true;
}
catch (Exception)
{
value = null;
return false;
}
}
/// <summary>
/// Determines whether the specified <see cref="JsonValue"/> instance can be converted to the specified collection <see cref="Type"/>.
/// </summary>
/// <param name="jsonValue">The instance to be converted.</param>
/// <param name="collectionType">The collection type to convert the instance to.</param>
/// <returns>true if the instance can be converted, false otherwise</returns>
private static bool CanConvertToClrCollection(JsonValue jsonValue, Type collectionType)
{
if (jsonValue != null)
{
return (jsonValue.JsonType == JsonType.Object && collectionType == typeof(Dictionary<string, object>)) ||
(jsonValue.JsonType == JsonType.Array && collectionType == typeof(object[]));
}
return false;
}
private static JsonValue CreatePrimitive(object value)
{
JsonPrimitive jsonPrimitive;
if (JsonPrimitive.TryCreate(value, out jsonPrimitive))
{
return jsonPrimitive;
}
return null;
}
private static JsonValue CreateFromComplex(object value)
{
DataContractJsonSerializer dcjs = new DataContractJsonSerializer(value.GetType());
using (MemoryStream ms = new MemoryStream())
{
dcjs.WriteObject(ms, value);
ms.Position = 0;
return JsonValue.Load(ms);
}
}
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "value is not the same")]
private static JsonValue CreateFromDynamic(object value)
{
JsonObject parent = null;
DynamicObject dynObj = value as DynamicObject;
if (dynObj != null)
{
parent = new JsonObject();
Stack<CreateFromTypeStackInfo> infoStack = new Stack<CreateFromTypeStackInfo>();
IEnumerator<string> keys = null;
do
{
if (keys == null)
{
keys = dynObj.GetDynamicMemberNames().GetEnumerator();
}
while (keys.MoveNext())
{
JsonValue child = null;
string key = keys.Current;
SimpleGetMemberBinder binder = new SimpleGetMemberBinder(key);
if (dynObj.TryGetMember(binder, out value))
{
DynamicObject childDynObj = value as DynamicObject;
if (childDynObj != null)
{
child = new JsonObject();
parent.Add(key, child);
infoStack.Push(new CreateFromTypeStackInfo(parent, dynObj, keys));
parent = child as JsonObject;
dynObj = childDynObj;
keys = null;
break;
}
else
{
if (value != null)
{
child = value as JsonValue;
if (child == null)
{
child = JsonValueExtensions.CreatePrimitive(value);
if (child == null)
{
child = JsonValueExtensions.CreateFromComplex(value);
}
}
}
parent.Add(key, child);
}
}
}
if (infoStack.Count > 0 && keys != null)
{
CreateFromTypeStackInfo info = infoStack.Pop();
parent = info.JsonObject;
dynObj = info.DynamicObject;
keys = info.Keys;
}
}
while (infoStack.Count > 0);
}
return parent;
}
private class CreateFromTypeStackInfo
{
public CreateFromTypeStackInfo(JsonObject jsonObject, DynamicObject dynamicObject, IEnumerator<string> keyEnumerator)
{
JsonObject = jsonObject;
DynamicObject = dynamicObject;
Keys = keyEnumerator;
}
/// <summary>
/// Gets of sets
/// </summary>
public JsonObject JsonObject { get; set; }
/// <summary>
/// Gets of sets
/// </summary>
public DynamicObject DynamicObject { get; set; }
/// <summary>
/// Gets of sets
/// </summary>
public IEnumerator<string> Keys { get; set; }
}
private class SimpleGetMemberBinder : GetMemberBinder
{
public SimpleGetMemberBinder(string name)
: base(name, false)
{
}
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
{
if (target != null && errorSuggestion == null)
{
string exceptionMessage = RS.Format(System.Json.Properties.Resources.DynamicPropertyNotDefined, target.LimitType, Name);
Expression throwExpression = Expression.Throw(Expression.Constant(new InvalidOperationException(exceptionMessage)), typeof(object));
errorSuggestion = new DynamicMetaObject(throwExpression, target.Restrictions);
}
return errorSuggestion;
}
}
}
}
#endif