/// <summary> /// Apply the merge as a full collection replacement; there is <b>no</b> way to detect changes or perform partial property update. /// </summary> private static JsonEntityMergeResult MergeApplyComplexItems(JsonEntityMergeArgs args, IPropertyReflector pr, JProperty jp, object entity) { var hasError = false; var lo = new List <object>(); var ier = pr.GetItemEntityReflector(); foreach (var ji in jp.Values()) { if (ji.Type == JTokenType.Null) { lo.Add(null); continue; } var ival = pr.ComplexTypeReflector.CreateItemValue(); if (MergeApply(args, ier, ji, ival) == JsonEntityMergeResult.Error) { hasError = true; } else { lo.Add(ival); } } if (hasError) { return(JsonEntityMergeResult.Error); } pr.ComplexTypeReflector.SetValue(entity, lo); return(JsonEntityMergeResult.SuccessWithChanges); }
/// <summary> /// Apply the merge from the json to the entity value as a more complex type. /// </summary> private static JsonEntityMergeResult MergeApplyComplex(JsonEntityMergeArgs args, IPropertyReflector pr, JProperty jp, object entity) { if (jp.Value.Type == JTokenType.Null) { return(pr.SetValue(entity, null) ? JsonEntityMergeResult.SuccessWithChanges : JsonEntityMergeResult.SuccessNoChanges); } // Update the sub-entity. if (pr.ComplexTypeReflector !.ComplexTypeCode == ComplexTypeCode.Object) { if (jp.Value.Type != JTokenType.Object) { return(args.Log(MessageItem.CreateMessage(jp.Path, MessageType.Error, $"The JSON token is malformed and could not be parsed."))); } var hasChanged = true; var current = pr.PropertyExpression.GetValue(entity); if (current == null) { current = pr.NewValue(entity).value; } else { hasChanged = false; } var mr = MergeApply(args, pr.GetEntityReflector() !, jp.Value, current !); return(mr == JsonEntityMergeResult.SuccessNoChanges ? (hasChanged ? JsonEntityMergeResult.SuccessWithChanges : JsonEntityMergeResult.SuccessNoChanges) : mr); }
/// <summary> /// Apply the merge from the json to the entity value as a more complex type. /// </summary> private static JsonEntityMergeResult MergeApplyComplex(JsonEntityMergeArgs args, IPropertyReflector pr, JProperty jp, object entity) { if (jp.Value.Type == JTokenType.Null) { return(pr.SetValue(entity, null) ? JsonEntityMergeResult.SuccessWithChanges : JsonEntityMergeResult.SuccessNoChanges); } // Update the sub-entity. if (pr.ComplexTypeReflector.ComplexTypeCode == ComplexTypeCode.Object) { if (jp.Value.Type != JTokenType.Object) { return(args.Log(MessageItem.CreateMessage(jp.Path, MessageType.Error, $"The JSON token is malformed and could not be parsed."))); } var hasChanged = true; var current = pr.PropertyExpression.GetValue(entity); if (current == null) { current = pr.NewValue(entity).value; } else { hasChanged = false; } var mr = MergeApply(args, pr.GetEntityReflector(), jp.Value, current); return(mr == JsonEntityMergeResult.SuccessNoChanges ? (hasChanged ? JsonEntityMergeResult.SuccessWithChanges : JsonEntityMergeResult.SuccessNoChanges) : mr); } else { // Ensure we are dealing with an array. if (jp.Value.Type != JTokenType.Array) { return(args.Log(MessageItem.CreateMessage(jp.Path, MessageType.Error, $"The JSON token is malformed and could not be parsed."))); } // Where empty array then update as such. if (!jp.Value.HasValues) { return(UpdateArrayValue(args, pr, entity, (IEnumerable)pr.PropertyExpression.GetValue(entity), (IEnumerable)pr.ComplexTypeReflector.CreateValue())); } // Handle array with primitive types. if (!pr.ComplexTypeReflector.IsItemComplexType) { var lo = new List <object>(); foreach (var iv in jp.Value.Values()) { lo.Add(iv.ToObject(pr.ComplexTypeReflector.ItemType)); } return(UpdateArrayValue(args, pr, entity, (IEnumerable)pr.PropertyExpression.GetValue(entity), (IEnumerable)pr.ComplexTypeReflector.CreateValue(lo))); } // Finally, handle array with complex entity items. return((pr.Tag == null) ? MergeApplyComplexItems(args, pr, jp, entity) : MergeApplyUniqueKeyItems(args, pr, jp, entity)); } }
/// <summary> /// Updates the array value. /// </summary> private static JsonEntityMergeResult UpdateArrayValue(JsonEntityMergeArgs args, IPropertyReflector pr, object entity, IEnumerable curVal, IEnumerable newVal) { if (pr.ComplexTypeReflector.CompareSequence(newVal, curVal)) { return(JsonEntityMergeResult.SuccessNoChanges); } pr.ComplexTypeReflector.SetValue(entity, newVal); return(JsonEntityMergeResult.SuccessWithChanges); }
/// <summary> /// Apply the merge from the json to the entity value. /// </summary> private static JsonEntityMergeResult MergeApply(JsonEntityMergeArgs args, IEntityReflector er, JToken json, object entity) { if (!json.HasValues) { return(JsonEntityMergeResult.SuccessNoChanges); } bool hasError = false; bool hasChanged = false; foreach (var jp in json.Children <JProperty>()) { // Get the corresponding property from the entity. var pr = er.GetJsonProperty(jp.Name); if (pr == null) { if (args.Log(MessageItem.CreateMessage(jp.Path, MessageType.Warning, $"The JSON path is not valid for the entity.")) == JsonEntityMergeResult.Error) { hasError = true; } continue; } // Handle the intrinsic types. if (!pr.IsComplexType) { if (jp.Value.Type == JTokenType.Array || jp.Value.Type == JTokenType.Object) { return(args.Log(MessageItem.CreateMessage(jp.Path, MessageType.Error, $"The JSON token is malformed and could not be parsed."))); } try { if (pr.SetValueFromJToken(entity, jp.Value)) { hasChanged = true; } } catch (FormatException fex) { return(args.Log(MessageItem.CreateMessage(jp.Path, MessageType.Error, $"The JSON token is malformed: {fex.Message}"))); } catch (Exception) { throw; } continue; } // Handle complex types (objects, arrays, collections, etc). switch (MergeApplyComplex(args, pr, jp, entity)) { case JsonEntityMergeResult.SuccessWithChanges: hasChanged = true; break; case JsonEntityMergeResult.Error: hasError = true; break; } } return(hasError ? JsonEntityMergeResult.Error : hasChanged?JsonEntityMergeResult.SuccessWithChanges : JsonEntityMergeResult.SuccessNoChanges); }
/// <summary> /// Apply the merge using the UniqueKey to match items between JSON and entity. /// </summary> private static JsonEntityMergeResult MergeApplyUniqueKeyItems(JsonEntityMergeArgs args, IPropertyReflector pr, JProperty jp, object entity) { var hasError = false; var hasChanges = false; var count = 0; var ukc = (UniqueKeyConfig)pr.Tag; var lo = new List <object>(); var ier = pr.GetItemEntityReflector(); // Determine the unique key for a comparison. var ukpr = ukc.GetPropertyReflectors(ier); // Get the current value to update. var current = (IEnumerable)pr.PropertyExpression.GetValue(entity); if (current == null) { hasChanges = true; } // Merge each item into the new collection. foreach (var ji in jp.Values()) { // Check not null. if (ji.Type != JTokenType.Object) { hasError = true; args.Log(MessageItem.CreateErrorMessage(ji.Path, "The JSON token must be an object where Unique Key value(s) are required.")); continue; } // Generate the unique key from the json properties. bool skip = false; var uk = new object[ukpr.Length]; for (int i = 0; i < ukc.Properties.Length; i++) { var jk = ji[ukpr[i].JsonName]; if (jk == null) { hasError = skip = true; args.Log(MessageItem.CreateErrorMessage(ji.Path, $"The JSON object must specify the '{ukpr[i].JsonName}' token as required for the unique key.")); break; } try { uk[i] = ukpr[i].GetJtokenValue(jk); } catch (FormatException fex) { hasError = skip = true; args.Log(MessageItem.CreateMessage(jk.Path, MessageType.Error, $"The JSON token is malformed: {fex.Message}")); break; } catch (Exception) { throw; } } if (skip) { continue; } // Get existing by unique key. var uniqueKey = new UniqueKey(uk); var item = current == null ? null : ukc.IsEntityBaseCollection ? ((IEntityBaseCollection)current).GetByUniqueKey(uniqueKey) : current.OfType <EntityBase>().FirstOrDefault(x => uniqueKey.Equals(x.UniqueKey)); // Create new if not found. if (item == null) { hasChanges = true; item = pr.ComplexTypeReflector.CreateItemValue(); } // Update. count++; var mr = MergeApply(args, ier, ji, item); if (mr == JsonEntityMergeResult.Error) { hasError = true; } else { if (mr == JsonEntityMergeResult.SuccessWithChanges) { hasChanges = true; } lo.Add(item); } } if (hasError) { return(JsonEntityMergeResult.Error); } // Confirm nothing was deleted (only needed where nothing changed so far). if (!hasChanges && count == (ukc.IsEntityBaseCollection ? ((IEntityBaseCollection)current).Count : current.OfType <EntityBase>().Count())) { return(JsonEntityMergeResult.SuccessNoChanges); } pr.ComplexTypeReflector.SetValue(entity, lo); return(JsonEntityMergeResult.SuccessWithChanges); }
/// <summary> /// Merges the JSON content into the <paramref name="value"/>. /// </summary> /// <typeparam name="TEntity">The entity <see cref="Type"/>.</typeparam> /// <param name="json">The <see cref="JToken"/> to merge.</param> /// <param name="value">The value to merge into.</param> /// <param name="args">The <see cref="JsonEntityMergeArgs"/>.</param> /// <returns><c>true</c> indicates that a least one change was made to the value; otherwise, <c>false</c>.</returns> public static JsonEntityMergeResult Merge <TEntity>(JToken json, TEntity value, JsonEntityMergeArgs args = null) { Check.NotNull(json, nameof(json)); Check.NotNull(value, nameof(value)); args = args ?? new JsonEntityMergeArgs(); if (json.Type != JTokenType.Object) { return(args.Log(MessageItem.CreateMessage(json.Path, MessageType.Error, $"The JSON document is malformed and could not be parsed."))); } return(MergeApply(args, _erArgs.GetReflector(typeof(TEntity)), json, value)); }