/// <summary> /// Builds an object from the provided <see cref="Json"/>. /// </summary> /// <param name="type">The type of object to build</param> /// <param name="sourceJson">The <see cref="Json"/> used</param> /// <param name="settings">Special interpretter settings</param> /// <returns>The object built from the <see cref="Json"/></returns> public static object BuildObjectForJson(Type type, Json sourceJson, JsonInterpretterSettings settings = null) { if (sourceJson.HasErrors) { return(null); } return(BuildObjectForJson(type, sourceJson.Data, settings)); }
/// <summary> /// Build an object for json that has been typed (using the built in <see cref="JsonDocument.kTypeIndicator"/> property) /// </summary> public static object BuildObjectForTypedJson(JsonValue sourceJson, JsonInterpretterSettings settings = null) { if (sourceJson is JsonDocument doc && doc.ValueFor(JsonDocument.kTypeIndicator) is JsonValue typeValue && !String.IsNullOrEmpty(typeValue.StringValue) && TypesByTypeId.TryGetValue(typeValue.StringValue, out Type objectType)) { return(BuildObjectForJson(objectType, sourceJson, settings)); } return(null); }
/// <summary> /// Fills out an existing instance with <see cref="JsonValue"/>. /// </summary> /// <param name="targetItem">The instance to fill out</param> /// <param name="sourceJsonValue">The <see cref="JsonValue"/> used</param> /// <param name="settings">Special interpretter settings</param> public static void FillOutObjectWithJson(ref object targetItem, JsonValue sourceJsonValue, JsonInterpretterSettings settings = null) { Type targetType = targetItem.GetType(); settings = settings ?? JsonInterpretterSettings.Default; if (sourceJsonValue.IsValue) { if (settings.StringParser.CanConvert(targetType)) { targetItem = settings.StringParser.Convert(sourceJsonValue.StringValue, targetType); } return; } if (sourceJsonValue.IsArray) { bool isArray = false, isList = false, isDictionary = false; Type elementType = null; MethodInfo dictionaryAdd = null; if (targetType.IsArray) { isArray = true; elementType = targetType.GetElementType(); } else if (targetType.FindBaseTypeOrInterface(typeof(IList <>)) is Type listType) { isList = true; elementType = listType.GetGenericArguments()[0]; } else if (targetType.FindBaseTypeOrInterface(typeof(IDictionary <,>)) is Type dictionaryType) { isDictionary = true; var generics = dictionaryType.GetGenericArguments(); elementType = typeof(KeyValuePair <,>).MakeGenericType(generics[0], generics[1]); var collectionType = typeof(ICollection <>).MakeGenericType(elementType); dictionaryAdd = collectionType.GetMethod("Add", new[] { elementType }); Debug.Assert(dictionaryAdd != null, $"Could not find add method for dictionary of type {targetType}"); } JsonArray sourceCasted = (JsonArray)sourceJsonValue; for (int index = 0; index < sourceCasted.Count; ++index) { JsonValue value = sourceCasted[index]; object element = BuildObjectForJson(elementType, value, settings); // If it's an array, then we've preallocated it the correct length already if (isArray) { ((IList)targetItem)[index] = element; } else if (isList) { ((IList)targetItem).Insert(index, element); } else if (isDictionary) { dictionaryAdd.Invoke(targetItem, new object[] { element }); } } } if (sourceJsonValue.IsDocument) { JsonDocument sourceCasted = (JsonDocument)sourceJsonValue; PropertyInfo[] allProperties = GetPropertiesFrom(targetType, false, true #if WINDOWS_UWP // Unfortunately, there aren't these kinds of settings in this direciton // going to say that opt in IS NOT required for this way anyway for now , false #endif ); foreach (var kvp in sourceCasted) { if (kvp.Key == JsonDocument.kTypeIndicator) { continue; } PropertyInfo propToSet = allProperties.FirstOrDefault(prop => prop.Name == kvp.Key); if (propToSet != null) { object newPropValue = null; // Resolve runtime type evaluation var runtimeTypeEval = propToSet.GetAttributes <JsonRuntimeTypeEvalAttribute>()?.FirstOrDefault(); if (runtimeTypeEval != null && kvp.Value.IsDocument) { JsonDocument valueDocCasted = (JsonDocument)kvp.Value; if (valueDocCasted.TryGetValue(JsonDocument.kTypeIndicator, out string typeIdForValue) && valueDocCasted.ValueFor(JsonDocument.kRuntimeTypeEvalValue) is JsonValue foundRuntimeValueIndicator && JsonHelper.TryGetTypeForTypeId(typeIdForValue, out Type foundType)) { newPropValue = BuildObjectForJson(foundType, foundRuntimeValueIndicator, settings); } } // If we're not dealing with runtime type evaluation, use the property type if (newPropValue == null) { newPropValue = BuildObjectForJson(propToSet.PropertyType, kvp.Value, settings); } propToSet.SetValue(targetItem, newPropValue); } } } }
/// <summary> /// Builds an object from the provided <see cref="Json"/>. /// </summary> /// <param name="type">The type of object to build</param> /// <param name="sourceJsonValue">The <see cref="JsonValue"/> used</param> /// <param name="settings">Special interpretter settings</param> /// <returns>The object built from the <see cref="JsonValue"/></returns> public static object BuildObjectForJson(Type type, JsonValue sourceJsonValue, JsonInterpretterSettings settings = null) { if (typeof(JsonValue).IsAssignableFrom(type)) { return(sourceJsonValue); } settings = settings ?? JsonInterpretterSettings.Default; object outputInstance = null; // If we're elevating one of the properties of this thing, as itself, then we need to // alter the type we're operating on string elevatedPropertyName = type.GetAttributes <JsonPropertyAsSelfAttribute>()?.FirstOrDefault()?.PropertyName; if (elevatedPropertyName != null) { // If we're doing elevation, then we are dealing with an object at this point // we'll need to create the instance, and set the value on the property indicated PropertyInfo targetProp = type.GetProperty(elevatedPropertyName, BindingFlags.Public | BindingFlags.Instance); if (targetProp != null) { outputInstance = Activator.CreateInstance(type); // Basically forward the json value onto building the child, and set value of that to the output instance object childInstance = BuildObjectForJson(targetProp.PropertyType, sourceJsonValue, settings); targetProp.SetValue(outputInstance, childInstance); return(outputInstance); } } if (sourceJsonValue.IsDocument) { JsonDocument docVersion = (JsonDocument)sourceJsonValue; string typeIndicator = docVersion.ValueFor(JsonDocument.kTypeIndicator)?.StringValue; if (typeIndicator != null) { if (TryGetTypeForTypeId(typeIndicator, out Type targetType)) { outputInstance = Activator.CreateInstance(targetType); } else { Logger.LogError($"Target type provided {typeIndicator ?? "-null-"} could not be translated, skipping"); } } } else if (sourceJsonValue.IsArray) { var array = (JsonArray)sourceJsonValue; // You have to create arrays with an argument if (type.IsArray) { outputInstance = Activator.CreateInstance(type, array.Count); } } if (outputInstance == null) { outputInstance = settings.ConstructInstanceFor(type, sourceJsonValue); } FillOutObjectWithJson(ref outputInstance, sourceJsonValue, settings); return(outputInstance); }
/// <summary> /// Build an object for json that has been typed (using the built in <see cref="JsonDocument.kTypeIndicator"/> property) /// </summary> public static object BuildObjectForTypedJson(Json sourceJson, JsonInterpretterSettings settings = null) { return(sourceJson.HasErrors ? null : BuildObjectForTypedJson(sourceJson.Data, settings)); }
/// <summary> /// Builds a list of objects from the <see cref="JsonArray"/> /// </summary> /// <typeparam name="T">The type of object to build</typeparam> /// <param name="sourceJsonArray">The <see cref="JsonValue"/> being evaluated</param> /// <param name="settings">Special interpretter settings</param> /// <returns>A list built with objects parsed from the array</returns> public static List <T> BuildObjectListForJson <T> (JsonArray sourceJsonArray, JsonInterpretterSettings settings = null) { var list = new List <T>(); foreach (JsonValue value in sourceJsonArray) { list.Add(BuildObjectForJson <T>(value, settings)); } return(list); }
/// <summary> /// Builds an object from the provided <see cref="Json"/>. /// </summary> /// <typeparam name="T">The type of object to build</typeparam> /// <param name="sourceJsonValue">The <see cref="JsonValue"/> used</param> /// <param name="settings">Special interpretter settings</param> /// <returns>The object built from the <see cref="JsonValue"/></returns> public static T BuildObjectForJson <T> (JsonValue sourceJsonValue, JsonInterpretterSettings settings = null) { return((T)BuildObjectForJson(typeof(T), sourceJsonValue, settings)); }