1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.Diagnostics.CodeAnalysis;
9 using System.Linq.Expressions;
11 namespace System.Runtime.Serialization.Json
14 /// This class extends the functionality of the <see cref="JsonValue"/> type.
16 [EditorBrowsable(EditorBrowsableState.Never)]
17 public static class JsonValueExtensions
20 /// Creates a <see cref="System.Json.JsonValue"/> object based on an arbitrary CLR object.
22 /// <param name="value">The object to be converted to <see cref="System.Json.JsonValue"/>.</param>
23 /// <returns>The <see cref="System.Json.JsonValue"/> which represents the given object.</returns>
24 /// <remarks>The conversion is done through the <see cref="System.Runtime.Serialization.Json.DataContractJsonSerializer"/>;
25 /// the object is first serialized into JSON using the serializer, then parsed into a <see cref="System.Json.JsonValue"/>
27 public static JsonValue CreateFrom(object value)
29 JsonValue jsonValue = null;
33 jsonValue = value as JsonValue;
35 if (jsonValue == null)
37 jsonValue = JsonValueExtensions.CreatePrimitive(value);
39 if (jsonValue == null)
41 jsonValue = JsonValueExtensions.CreateFromDynamic(value);
43 if (jsonValue == null)
45 jsonValue = JsonValueExtensions.CreateFromComplex(value);
55 /// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into the type T.
57 /// <typeparam name="T">The type to which the conversion is being performed.</typeparam>
58 /// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
59 /// <param name="valueOfT">An instance of T initialized with this instance, or the default
60 /// value of T, if the conversion cannot be performed.</param>
61 /// <returns>true if this <see cref="System.Json.JsonValue"/> instance can be read as type T; otherwise, false.</returns>
62 public static bool TryReadAsType<T>(this JsonValue jsonValue, out T valueOfT)
64 if (jsonValue == null)
66 throw new ArgumentNullException("jsonValue");
70 if (JsonValueExtensions.TryReadAsType(jsonValue, typeof(T), out value))
76 valueOfT = default(T);
81 /// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into the type T.
83 /// <typeparam name="T">The type to which the conversion is being performed.</typeparam>
84 /// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
85 /// <returns>An instance of T initialized with the <see cref="System.Json.JsonValue"/> value
86 /// specified if the conversion.</returns>
87 /// <exception cref="System.NotSupportedException">If this <see cref="System.Json.JsonValue"/> value cannot be
88 /// converted into the type T.</exception>
89 [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter",
90 Justification = "The generic parameter is used to specify the output type")]
91 public static T ReadAsType<T>(this JsonValue jsonValue)
93 if (jsonValue == null)
95 throw new ArgumentNullException("jsonValue");
98 return (T)JsonValueExtensions.ReadAsType(jsonValue, typeof(T));
102 /// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into the type T, returning a fallback value
103 /// if the conversion fails.
105 /// <typeparam name="T">The type to which the conversion is being performed.</typeparam>
106 /// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
107 /// <param name="fallback">A fallback value to be retuned in case the conversion cannot be performed.</param>
108 /// <returns>An instance of T initialized with the <see cref="System.Json.JsonValue"/> value
109 /// specified if the conversion succeeds or the specified fallback value if it fails.</returns>
110 public static T ReadAsType<T>(this JsonValue jsonValue, T fallback)
112 if (jsonValue == null)
114 throw new ArgumentNullException("jsonValue");
118 if (JsonValueExtensions.TryReadAsType<T>(jsonValue, out outVal))
127 /// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into an instance of the specified type.
129 /// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
130 /// <param name="type">The type to which the conversion is being performed.</param>
131 /// <returns>An object instance initialized with the <see cref="System.Json.JsonValue"/> value
132 /// specified if the conversion.</returns>
133 /// <exception cref="System.NotSupportedException">If this <see cref="System.Json.JsonValue"/> value cannot be
134 /// converted into the type T.</exception>
135 public static object ReadAsType(this JsonValue jsonValue, Type type)
137 if (jsonValue == null)
139 throw new ArgumentNullException("jsonValue");
144 throw new ArgumentNullException("type");
148 if (JsonValueExtensions.TryReadAsType(jsonValue, type, out result))
153 throw new NotSupportedException(RS.Format(System.Json.Properties.Resources.CannotReadAsType, jsonValue.GetType().FullName, type.FullName));
157 /// Attempts to convert this <see cref="System.Json.JsonValue"/> instance into an instance of the specified type.
159 /// <param name="jsonValue">The <see cref="JsonValue"/> instance this method extension is to be applied to.</param>
160 /// <param name="type">The type to which the conversion is being performed.</param>
161 /// <param name="value">An object to be initialized with this instance or null if the conversion cannot be performed.</param>
162 /// <returns>true if this <see cref="System.Json.JsonValue"/> instance can be read as the specified type; otherwise, false.</returns>
163 [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate",
164 Justification = "This is the non-generic version of the method.")]
165 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception translates to fail.")]
166 public static bool TryReadAsType(this JsonValue jsonValue, Type type, out object value)
168 if (jsonValue == null)
170 throw new ArgumentNullException("jsonValue");
175 throw new ArgumentNullException("type");
178 if (type == typeof(JsonValue) || type == typeof(object))
184 if (type == typeof(object[]) || type == typeof(Dictionary<string, object>))
186 if (!JsonValueExtensions.CanConvertToClrCollection(jsonValue, type))
193 if (jsonValue.TryReadAs(type, out value))
200 using (MemoryStream ms = new MemoryStream())
204 DataContractJsonSerializer dcjs = new DataContractJsonSerializer(type);
205 value = dcjs.ReadObject(ms);
218 /// Determines whether the specified <see cref="JsonValue"/> instance can be converted to the specified collection <see cref="Type"/>.
220 /// <param name="jsonValue">The instance to be converted.</param>
221 /// <param name="collectionType">The collection type to convert the instance to.</param>
222 /// <returns>true if the instance can be converted, false otherwise</returns>
223 private static bool CanConvertToClrCollection(JsonValue jsonValue, Type collectionType)
225 if (jsonValue != null)
227 return (jsonValue.JsonType == JsonType.Object && collectionType == typeof(Dictionary<string, object>)) ||
228 (jsonValue.JsonType == JsonType.Array && collectionType == typeof(object[]));
234 private static JsonValue CreatePrimitive(object value)
236 JsonPrimitive jsonPrimitive;
238 if (JsonPrimitive.TryCreate(value, out jsonPrimitive))
240 return jsonPrimitive;
246 private static JsonValue CreateFromComplex(object value)
248 DataContractJsonSerializer dcjs = new DataContractJsonSerializer(value.GetType());
249 using (MemoryStream ms = new MemoryStream())
251 dcjs.WriteObject(ms, value);
253 return JsonValue.Load(ms);
257 [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "value is not the same")]
258 private static JsonValue CreateFromDynamic(object value)
260 JsonObject parent = null;
261 DynamicObject dynObj = value as DynamicObject;
265 parent = new JsonObject();
266 Stack<CreateFromTypeStackInfo> infoStack = new Stack<CreateFromTypeStackInfo>();
267 IEnumerator<string> keys = null;
273 keys = dynObj.GetDynamicMemberNames().GetEnumerator();
276 while (keys.MoveNext())
278 JsonValue child = null;
279 string key = keys.Current;
280 SimpleGetMemberBinder binder = new SimpleGetMemberBinder(key);
282 if (dynObj.TryGetMember(binder, out value))
284 DynamicObject childDynObj = value as DynamicObject;
286 if (childDynObj != null)
288 child = new JsonObject();
289 parent.Add(key, child);
291 infoStack.Push(new CreateFromTypeStackInfo(parent, dynObj, keys));
293 parent = child as JsonObject;
294 dynObj = childDynObj;
303 child = value as JsonValue;
307 child = JsonValueExtensions.CreatePrimitive(value);
311 child = JsonValueExtensions.CreateFromComplex(value);
316 parent.Add(key, child);
321 if (infoStack.Count > 0 && keys != null)
323 CreateFromTypeStackInfo info = infoStack.Pop();
325 parent = info.JsonObject;
326 dynObj = info.DynamicObject;
330 while (infoStack.Count > 0);
336 private class CreateFromTypeStackInfo
338 public CreateFromTypeStackInfo(JsonObject jsonObject, DynamicObject dynamicObject, IEnumerator<string> keyEnumerator)
340 JsonObject = jsonObject;
341 DynamicObject = dynamicObject;
342 Keys = keyEnumerator;
348 public JsonObject JsonObject { get; set; }
353 public DynamicObject DynamicObject { get; set; }
358 public IEnumerator<string> Keys { get; set; }
361 private class SimpleGetMemberBinder : GetMemberBinder
363 public SimpleGetMemberBinder(string name)
368 public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
370 if (target != null && errorSuggestion == null)
372 string exceptionMessage = RS.Format(System.Json.Properties.Resources.DynamicPropertyNotDefined, target.LimitType, Name);
373 Expression throwExpression = Expression.Throw(Expression.Constant(new InvalidOperationException(exceptionMessage)), typeof(object));
375 errorSuggestion = new DynamicMetaObject(throwExpression, target.Restrictions);
378 return errorSuggestion;