Fix for #2280 - Cannot do an "add" patch to IList<Anything>
Added unit tests
This commit is contained in:
parent
e48565dcd8
commit
bf712263fc
|
|
@ -3,10 +3,13 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.JsonPatch.Exceptions;
|
using Microsoft.AspNet.JsonPatch.Exceptions;
|
||||||
using Microsoft.AspNet.JsonPatch.Helpers;
|
using Microsoft.AspNet.JsonPatch.Helpers;
|
||||||
using Microsoft.AspNet.JsonPatch.Operations;
|
using Microsoft.AspNet.JsonPatch.Operations;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.JsonPatch.Adapters
|
namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
|
|
@ -124,17 +127,16 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
positionAsInteger = PropertyHelpers.GetNumericEnd(path);
|
positionAsInteger = GetNumericEnd(path);
|
||||||
|
|
||||||
if (positionAsInteger > -1)
|
if (positionAsInteger > -1)
|
||||||
{
|
{
|
||||||
actualPathToProperty = path.Substring(0,
|
actualPathToProperty = path.Substring(0,
|
||||||
path.IndexOf('/' + positionAsInteger.ToString()));
|
path.LastIndexOf('/' + positionAsInteger.ToString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var patchProperty = PropertyHelpers
|
var patchProperty = FindPropertyAndParent(objectToApplyTo, actualPathToProperty);
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualPathToProperty, ContractResolver);
|
|
||||||
|
|
||||||
// does property at path exist?
|
// does property at path exist?
|
||||||
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operationToReport, path))
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operationToReport, path))
|
||||||
|
|
@ -148,13 +150,12 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
if (appendList || positionAsInteger > -1)
|
if (appendList || positionAsInteger > -1)
|
||||||
{
|
{
|
||||||
// what if it's an array but there's no position??
|
// what if it's an array but there's no position??
|
||||||
if (IsNonStringArray(patchProperty))
|
if (IsNonStringArray(patchProperty.Property.PropertyType))
|
||||||
{
|
{
|
||||||
// now, get the generic type of the enumerable
|
// now, get the generic type of the IList<> from Property type.
|
||||||
var genericTypeOfArray = PropertyHelpers.GetEnumerableType(
|
var genericTypeOfArray = GetIListType(patchProperty.Property.PropertyType);
|
||||||
patchProperty.Property.PropertyType);
|
|
||||||
|
|
||||||
var conversionResult = PropertyHelpers.ConvertToActualType(genericTypeOfArray, value);
|
var conversionResult = ConvertToActualType(genericTypeOfArray, value);
|
||||||
|
|
||||||
if (!CheckIfPropertyCanBeSet(conversionResult, objectToApplyTo, operationToReport, path))
|
if (!CheckIfPropertyCanBeSet(conversionResult, objectToApplyTo, operationToReport, path))
|
||||||
{
|
{
|
||||||
|
|
@ -198,7 +199,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var conversionResultTuple = PropertyHelpers.ConvertToActualType(
|
var conversionResultTuple = ConvertToActualType(
|
||||||
patchProperty.Property.PropertyType,
|
patchProperty.Property.PropertyType,
|
||||||
value);
|
value);
|
||||||
|
|
||||||
|
|
@ -244,7 +245,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
var positionAsInteger = -1;
|
var positionAsInteger = -1;
|
||||||
var actualFromProperty = operation.from;
|
var actualFromProperty = operation.from;
|
||||||
|
|
||||||
positionAsInteger = PropertyHelpers.GetNumericEnd(operation.from);
|
positionAsInteger = GetNumericEnd(operation.from);
|
||||||
|
|
||||||
if (positionAsInteger > -1)
|
if (positionAsInteger > -1)
|
||||||
{
|
{
|
||||||
|
|
@ -252,8 +253,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
operation.from.IndexOf('/' + positionAsInteger.ToString()));
|
operation.from.IndexOf('/' + positionAsInteger.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var patchProperty = PropertyHelpers
|
var patchProperty = FindPropertyAndParent(objectToApplyTo, actualFromProperty);
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualFromProperty, ContractResolver);
|
|
||||||
|
|
||||||
// does property at from exist?
|
// does property at from exist?
|
||||||
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.from))
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.from))
|
||||||
|
|
@ -265,10 +265,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
// the path must end with "/position" or "/-", which we already determined before.
|
// the path must end with "/position" or "/-", which we already determined before.
|
||||||
if (positionAsInteger > -1)
|
if (positionAsInteger > -1)
|
||||||
{
|
{
|
||||||
if (IsNonStringArray(patchProperty))
|
if (IsNonStringArray(patchProperty.Property.PropertyType))
|
||||||
{
|
{
|
||||||
// now, get the generic type of the enumerable
|
// now, get the generic type of the IList<> from Property type.
|
||||||
var genericTypeOfArray = PropertyHelpers.GetEnumerableType(patchProperty.Property.PropertyType);
|
var genericTypeOfArray = GetIListType(patchProperty.Property.PropertyType);
|
||||||
|
|
||||||
// get value (it can be cast, we just checked that)
|
// get value (it can be cast, we just checked that)
|
||||||
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
||||||
|
|
@ -345,7 +345,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
positionAsInteger = PropertyHelpers.GetNumericEnd(path);
|
positionAsInteger = GetNumericEnd(path);
|
||||||
|
|
||||||
if (positionAsInteger > -1)
|
if (positionAsInteger > -1)
|
||||||
{
|
{
|
||||||
|
|
@ -354,8 +354,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var patchProperty = PropertyHelpers
|
var patchProperty = FindPropertyAndParent(objectToApplyTo, actualPathToProperty);
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualPathToProperty, ContractResolver);
|
|
||||||
|
|
||||||
// does the target location exist?
|
// does the target location exist?
|
||||||
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operationToReport, path))
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operationToReport, path))
|
||||||
|
|
@ -369,10 +368,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
if (removeFromList || positionAsInteger > -1)
|
if (removeFromList || positionAsInteger > -1)
|
||||||
{
|
{
|
||||||
// what if it's an array but there's no position??
|
// what if it's an array but there's no position??
|
||||||
if (IsNonStringArray(patchProperty))
|
if (IsNonStringArray(patchProperty.Property.PropertyType))
|
||||||
{
|
{
|
||||||
// now, get the generic type of the enumerable
|
// now, get the generic type of the IList<> from Property type.
|
||||||
var genericTypeOfArray = PropertyHelpers.GetEnumerableType(patchProperty.Property.PropertyType);
|
var genericTypeOfArray = GetIListType(patchProperty.Property.PropertyType);
|
||||||
|
|
||||||
// get value (it can be cast, we just checked that)
|
// get value (it can be cast, we just checked that)
|
||||||
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
||||||
|
|
@ -480,7 +479,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
var positionInPathAsInteger = -1;
|
var positionInPathAsInteger = -1;
|
||||||
var actualPathProperty = operation.path;
|
var actualPathProperty = operation.path;
|
||||||
|
|
||||||
positionInPathAsInteger = PropertyHelpers.GetNumericEnd(operation.path);
|
positionInPathAsInteger = GetNumericEnd(operation.path);
|
||||||
|
|
||||||
if (positionInPathAsInteger > -1)
|
if (positionInPathAsInteger > -1)
|
||||||
{
|
{
|
||||||
|
|
@ -488,8 +487,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
operation.path.IndexOf('/' + positionInPathAsInteger.ToString()));
|
operation.path.IndexOf('/' + positionInPathAsInteger.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var patchProperty = PropertyHelpers
|
var patchProperty = FindPropertyAndParent(objectToApplyTo, actualPathProperty);
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualPathProperty, ContractResolver);
|
|
||||||
|
|
||||||
// does property at path exist?
|
// does property at path exist?
|
||||||
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.path))
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.path))
|
||||||
|
|
@ -504,11 +502,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
// the path must end with "/position" or "/-", which we already determined before.
|
// the path must end with "/position" or "/-", which we already determined before.
|
||||||
if (positionInPathAsInteger > -1)
|
if (positionInPathAsInteger > -1)
|
||||||
{
|
{
|
||||||
if (IsNonStringArray(patchProperty))
|
if (IsNonStringArray(patchProperty.Property.PropertyType))
|
||||||
{
|
{
|
||||||
// now, get the generic type of the enumerable
|
// now, get the generic type of the IList<> from Property type.
|
||||||
typeOfFinalPropertyAtPathLocation = PropertyHelpers
|
typeOfFinalPropertyAtPathLocation = GetIListType(patchProperty.Property.PropertyType);
|
||||||
.GetEnumerableType(patchProperty.Property.PropertyType);
|
|
||||||
|
|
||||||
// get value (it can be cast, we just checked that)
|
// get value (it can be cast, we just checked that)
|
||||||
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
||||||
|
|
@ -542,9 +539,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
typeOfFinalPropertyAtPathLocation = patchProperty.Property.PropertyType;
|
typeOfFinalPropertyAtPathLocation = patchProperty.Property.PropertyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
var conversionResultTuple = PropertyHelpers.ConvertToActualType(
|
var conversionResultTuple = ConvertToActualType(typeOfFinalPropertyAtPathLocation, operation.value);
|
||||||
typeOfFinalPropertyAtPathLocation,
|
|
||||||
operation.value);
|
|
||||||
|
|
||||||
// Is conversion successful
|
// Is conversion successful
|
||||||
if (!CheckIfPropertyCanBeSet(conversionResultTuple, objectToApplyTo, operation, operation.path))
|
if (!CheckIfPropertyCanBeSet(conversionResultTuple, objectToApplyTo, operation, operation.path))
|
||||||
|
|
@ -610,7 +605,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
var positionAsInteger = -1;
|
var positionAsInteger = -1;
|
||||||
var actualFromProperty = operation.from;
|
var actualFromProperty = operation.from;
|
||||||
|
|
||||||
positionAsInteger = PropertyHelpers.GetNumericEnd(operation.from);
|
positionAsInteger = GetNumericEnd(operation.from);
|
||||||
|
|
||||||
if (positionAsInteger > -1)
|
if (positionAsInteger > -1)
|
||||||
{
|
{
|
||||||
|
|
@ -618,8 +613,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
operation.from.IndexOf('/' + positionAsInteger.ToString()));
|
operation.from.IndexOf('/' + positionAsInteger.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var patchProperty = PropertyHelpers
|
var patchProperty = FindPropertyAndParent(objectToApplyTo, actualFromProperty);
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualFromProperty, ContractResolver);
|
|
||||||
|
|
||||||
// does property at from exist?
|
// does property at from exist?
|
||||||
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.from))
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.from))
|
||||||
|
|
@ -632,10 +626,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
// the path must end with "/position" or "/-", which we already determined before.
|
// the path must end with "/position" or "/-", which we already determined before.
|
||||||
if (positionAsInteger > -1)
|
if (positionAsInteger > -1)
|
||||||
{
|
{
|
||||||
if (IsNonStringArray(patchProperty))
|
if (IsNonStringArray(patchProperty.Property.PropertyType))
|
||||||
{
|
{
|
||||||
// now, get the generic type of the enumerable
|
// now, get the generic type of the IList<> from Property type.
|
||||||
var genericTypeOfArray = PropertyHelpers.GetEnumerableType(patchProperty.Property.PropertyType);
|
var genericTypeOfArray = GetIListType(patchProperty.Property.PropertyType);
|
||||||
|
|
||||||
// get value (it can be cast, we just checked that)
|
// get value (it can be cast, we just checked that)
|
||||||
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
||||||
|
|
@ -702,11 +696,14 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsNonStringArray(JsonPatchProperty patchProperty)
|
private bool IsNonStringArray(Type type)
|
||||||
{
|
{
|
||||||
return !(patchProperty.Property.PropertyType == typeof(string))
|
if (GetIListType(type) != null)
|
||||||
&& typeof(IList).GetTypeInfo().IsAssignableFrom(
|
{
|
||||||
patchProperty.Property.PropertyType.GetTypeInfo());
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!(type == typeof(string)) && typeof(IList).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckIfPropertyCanBeSet(
|
private bool CheckIfPropertyCanBeSet(
|
||||||
|
|
@ -739,5 +736,111 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
throw new JsonPatchException<T>(jsonPatchError);
|
throw new JsonPatchException<T>(jsonPatchError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JsonPatchProperty FindPropertyAndParent(
|
||||||
|
object targetObject,
|
||||||
|
string propertyPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var splitPath = propertyPath.Split('/');
|
||||||
|
|
||||||
|
// skip the first one if it's empty
|
||||||
|
var startIndex = (string.IsNullOrWhiteSpace(splitPath[0]) ? 1 : 0);
|
||||||
|
|
||||||
|
for (int i = startIndex; i < splitPath.Length; i++)
|
||||||
|
{
|
||||||
|
var jsonContract = (JsonObjectContract)ContractResolver.ResolveContract(targetObject.GetType());
|
||||||
|
|
||||||
|
foreach (var property in jsonContract.Properties)
|
||||||
|
{
|
||||||
|
if (string.Equals(property.PropertyName, splitPath[i], StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (i == (splitPath.Length - 1))
|
||||||
|
{
|
||||||
|
return new JsonPatchProperty(property, targetObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetObject = property.ValueProvider.GetValue(targetObject);
|
||||||
|
|
||||||
|
// if property is of IList type then get the array index from splitPath and get the
|
||||||
|
// object at the indexed position from the list.
|
||||||
|
if (GetIListType(property.PropertyType) != null)
|
||||||
|
{
|
||||||
|
var index = int.Parse(splitPath[++i]);
|
||||||
|
targetObject = ((IList)targetObject)[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// will result in JsonPatchException in calling class, as expected
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConversionResult ConvertToActualType(Type propertyType, object value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var o = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), propertyType);
|
||||||
|
|
||||||
|
return new ConversionResult(true, o);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return new ConversionResult(false, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type GetIListType([NotNull] Type type)
|
||||||
|
{
|
||||||
|
if (IsGenericListType(type))
|
||||||
|
{
|
||||||
|
return type.GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Type interfaceType in type.GetTypeInfo().ImplementedInterfaces)
|
||||||
|
{
|
||||||
|
if (IsGenericListType(interfaceType))
|
||||||
|
{
|
||||||
|
return interfaceType.GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsGenericListType([NotNull] Type type)
|
||||||
|
{
|
||||||
|
if (type.GetTypeInfo().IsGenericType &&
|
||||||
|
type.GetGenericTypeDefinition() == typeof(IList<>))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetNumericEnd(string path)
|
||||||
|
{
|
||||||
|
var possibleIndex = path.Substring(path.LastIndexOf("/") + 1);
|
||||||
|
var castedIndex = -1;
|
||||||
|
|
||||||
|
if (int.TryParse(possibleIndex, out castedIndex))
|
||||||
|
{
|
||||||
|
return castedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.JsonPatch.Helpers
|
|
||||||
{
|
|
||||||
internal static class PropertyHelpers
|
|
||||||
{
|
|
||||||
public static JsonPatchProperty FindPropertyAndParent(
|
|
||||||
object targetObject,
|
|
||||||
string propertyPath,
|
|
||||||
IContractResolver contractResolver)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var splitPath = propertyPath.Split('/');
|
|
||||||
|
|
||||||
// skip the first one if it's empty
|
|
||||||
var startIndex = (string.IsNullOrWhiteSpace(splitPath[0]) ? 1 : 0);
|
|
||||||
|
|
||||||
for (int i = startIndex; i < splitPath.Length; i++)
|
|
||||||
{
|
|
||||||
var jsonContract = (JsonObjectContract)contractResolver.ResolveContract(targetObject.GetType());
|
|
||||||
|
|
||||||
foreach (var property in jsonContract.Properties)
|
|
||||||
{
|
|
||||||
if (string.Equals(property.PropertyName, splitPath[i], StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (i == (splitPath.Length - 1))
|
|
||||||
{
|
|
||||||
return new JsonPatchProperty(property, targetObject);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
targetObject = property.ValueProvider.GetValue(targetObject);
|
|
||||||
|
|
||||||
// if property is of IList type then get the array index from splitPath and get the
|
|
||||||
// object at the indexed position from the list.
|
|
||||||
if (typeof(IList).GetTypeInfo().IsAssignableFrom(property.PropertyType.GetTypeInfo()))
|
|
||||||
{
|
|
||||||
var index = int.Parse(splitPath[++i]);
|
|
||||||
targetObject = ((IList)targetObject)[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// will result in JsonPatchException in calling class, as expected
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ConversionResult ConvertToActualType(Type propertyType, object value)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var o = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), propertyType);
|
|
||||||
|
|
||||||
return new ConversionResult(true, o);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return new ConversionResult(false, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Type GetEnumerableType(Type type)
|
|
||||||
{
|
|
||||||
if (type == null) throw new ArgumentNullException();
|
|
||||||
foreach (Type interfaceType in type.GetInterfaces())
|
|
||||||
{
|
|
||||||
if (interfaceType.GetTypeInfo().IsGenericType &&
|
|
||||||
interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
|
||||||
{
|
|
||||||
return interfaceType.GetGenericArguments()[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static int GetNumericEnd(string path)
|
|
||||||
{
|
|
||||||
var possibleIndex = path.Substring(path.LastIndexOf("/") + 1);
|
|
||||||
var castedIndex = -1;
|
|
||||||
|
|
||||||
if (int.TryParse(possibleIndex, out castedIndex))
|
|
||||||
{
|
|
||||||
return castedIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -201,6 +201,110 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.SimpleDTO.IntegerList);
|
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.SimpleDTO.IntegerList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToIntegerIList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTO = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerIList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Add<int>(o => (List<int>)o.SimpleDTO.IntegerIList, 4, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.SimpleDTO.IntegerIList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToIntegerIListWithSerialization()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTO = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerIList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Add<int>(o => (List<int>)o.SimpleDTO.IntegerIList, 4, 0);
|
||||||
|
|
||||||
|
var serialized = JsonConvert.SerializeObject(patchDoc);
|
||||||
|
var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument<SimpleDTOWithNestedDTO>>(serialized);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
deserialized.ApplyTo(doc);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.SimpleDTO.IntegerIList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToNestedIntegerIList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTOIList = new List<SimpleDTO>
|
||||||
|
{
|
||||||
|
new SimpleDTO
|
||||||
|
{
|
||||||
|
IntegerIList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Add<int>(o => (List<int>)o.SimpleDTOIList[0].IntegerIList, 4, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.SimpleDTOIList[0].IntegerIList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToNestedIntegerIListWithSerialization()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTOIList = new List<SimpleDTO>
|
||||||
|
{
|
||||||
|
new SimpleDTO
|
||||||
|
{
|
||||||
|
IntegerIList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Add<int>(o => (List<int>)o.SimpleDTOIList[0].IntegerIList, 4, 0);
|
||||||
|
|
||||||
|
var serialized = JsonConvert.SerializeObject(patchDoc);
|
||||||
|
var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument<SimpleDTOWithNestedDTO>>(serialized);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
deserialized.ApplyTo(doc);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.SimpleDTOIList[0].IntegerIList);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddToComplextTypeListSpecifyIndex()
|
public void AddToComplextTypeListSpecifyIndex()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,49 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.IntegerList);
|
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.IntegerList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToIntegerIList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerIList = new List<int>() { 1, 2, 3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTO>();
|
||||||
|
patchDoc.Add<int>(o => o.IntegerIList, 4, 0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.IntegerIList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToIntegerIListWithSerialization()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerIList = new List<int>() { 1, 2, 3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTO>();
|
||||||
|
patchDoc.Add<int>(o => o.IntegerIList, 4, 0);
|
||||||
|
|
||||||
|
var serialized = JsonConvert.SerializeObject(patchDoc);
|
||||||
|
var deserialized = JsonConvert.DeserializeObject<JsonPatchDocument<SimpleDTO>>(serialized);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
deserialized.ApplyTo(doc);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new List<int>() { 4, 1, 2, 3 }, doc.IntegerIList);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddToListInvalidPositionTooLarge()
|
public void AddToListInvalidPositionTooLarge()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.JsonPatch.Test
|
namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
@ -9,6 +10,7 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
public class SimpleDTO
|
public class SimpleDTO
|
||||||
{
|
{
|
||||||
public List<int> IntegerList { get; set; }
|
public List<int> IntegerList { get; set; }
|
||||||
|
public IList<int> IntegerIList { get; set; }
|
||||||
public int IntegerValue { get; set; }
|
public int IntegerValue { get; set; }
|
||||||
public string StringProperty { get; set; }
|
public string StringProperty { get; set; }
|
||||||
public string AnotherStringProperty { get; set; }
|
public string AnotherStringProperty { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
||||||
public List<SimpleDTO> SimpleDTOList { get; set; }
|
public List<SimpleDTO> SimpleDTOList { get; set; }
|
||||||
|
|
||||||
|
public IList<SimpleDTO> SimpleDTOIList { get; set; }
|
||||||
|
|
||||||
public SimpleDTOWithNestedDTO()
|
public SimpleDTOWithNestedDTO()
|
||||||
{
|
{
|
||||||
this.NestedDTO = new NestedDTO();
|
this.NestedDTO = new NestedDTO();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue