diff --git a/src/Microsoft.AspNetCore.JsonPatch/Adapters/AdapterFactory.cs b/src/Microsoft.AspNetCore.JsonPatch/Adapters/AdapterFactory.cs new file mode 100644 index 0000000..9565bd3 --- /dev/null +++ b/src/Microsoft.AspNetCore.JsonPatch/Adapters/AdapterFactory.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.JsonPatch.Internal; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Adapters +{ + /// + /// The default AdapterFactory to be used for resolving . + /// + public class AdapterFactory : IAdapterFactory + { + /// + public virtual IAdapter Create(object target, IContractResolver contractResolver) + { + var jsonContract = contractResolver.ResolveContract(target.GetType()); + + if (target is IList) + { + return new ListAdapter(); + } + else if (jsonContract is JsonDictionaryContract jsonDictionaryContract) + { + var type = typeof(DictionaryAdapter<,>).MakeGenericType(jsonDictionaryContract.DictionaryKeyType, jsonDictionaryContract.DictionaryValueType); + return (IAdapter)Activator.CreateInstance(type); + } + else if (jsonContract is JsonDynamicContract) + { + return new DynamicObjectAdapter(); + } + else + { + return new PocoAdapter(); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.JsonPatch/Adapters/IAdapterFactory.cs b/src/Microsoft.AspNetCore.JsonPatch/Adapters/IAdapterFactory.cs new file mode 100644 index 0000000..43ca1e6 --- /dev/null +++ b/src/Microsoft.AspNetCore.JsonPatch/Adapters/IAdapterFactory.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.JsonPatch.Internal; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.JsonPatch.Adapters +{ + /// + /// Defines the operations used for loading an based on the current object and ContractResolver. + /// + public interface IAdapterFactory + { + /// + /// Creates an for the current object + /// + /// The target object + /// The current contract resolver + /// The needed + IAdapter Create(object target, IContractResolver contractResolver); + } +} diff --git a/src/Microsoft.AspNetCore.JsonPatch/Adapters/ObjectAdapter.cs b/src/Microsoft.AspNetCore.JsonPatch/Adapters/ObjectAdapter.cs index 73095b5..4e3783a 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Adapters/ObjectAdapter.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Adapters/ObjectAdapter.cs @@ -11,17 +11,31 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters /// public class ObjectAdapter : IObjectAdapterWithTest { + /// + /// Initializes a new instance of using the default AdapterFactory. + /// + /// The . + /// The for logging . + public ObjectAdapter( + IContractResolver contractResolver, + Action logErrorAction):this(contractResolver, logErrorAction, new AdapterFactory()) + { + } + /// /// Initializes a new instance of . /// /// The . /// The for logging . + /// The to use when creating adaptors. public ObjectAdapter( IContractResolver contractResolver, - Action logErrorAction) + Action logErrorAction, + IAdapterFactory adapterFactory) { ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver)); LogErrorAction = logErrorAction; + AdapterFactory = adapterFactory; } /// @@ -29,6 +43,11 @@ public ObjectAdapter( /// public IContractResolver ContractResolver { get; } + /// + /// Gets the + /// + public IAdapterFactory AdapterFactory { get; } + /// /// Action for logging . /// @@ -75,7 +94,7 @@ private void Add( } var parsedPath = new ParsedPath(path); - var visitor = new ObjectVisitor(parsedPath, ContractResolver); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); var target = objectToApplyTo; if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) @@ -144,7 +163,7 @@ public void Remove(Operation operation, object objectToApplyTo) private void Remove(string path, object objectToApplyTo, Operation operationToReport) { var parsedPath = new ParsedPath(path); - var visitor = new ObjectVisitor(parsedPath, ContractResolver); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); var target = objectToApplyTo; if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) @@ -175,7 +194,7 @@ public void Replace(Operation operation, object objectToApplyTo) } var parsedPath = new ParsedPath(operation.path); - var visitor = new ObjectVisitor(parsedPath, ContractResolver); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); var target = objectToApplyTo; if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) @@ -239,7 +258,7 @@ public void Test(Operation operation, object objectToApplyTo) } var parsedPath = new ParsedPath(operation.path); - var visitor = new ObjectVisitor(parsedPath, ContractResolver); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); var target = objectToApplyTo; if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) @@ -281,7 +300,7 @@ private bool TryGetValue( propertyValue = null; var parsedPath = new ParsedPath(fromLocation); - var visitor = new ObjectVisitor(parsedPath, ContractResolver); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); var target = objectToGetValueFrom; if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) diff --git a/src/Microsoft.AspNetCore.JsonPatch/Converters/JsonPatchDocumentConverter.cs b/src/Microsoft.AspNetCore.JsonPatch/Converters/JsonPatchDocumentConverter.cs index aed9c48..b5e3397 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Converters/JsonPatchDocumentConverter.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Converters/JsonPatchDocumentConverter.cs @@ -51,7 +51,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist serializer.Populate(jObjectReader, targetOperations); // container target: the JsonPatchDocument. - var container = new JsonPatchDocument(targetOperations, new DefaultContractResolver()); + var container = CreateContainer(targetOperations); return container; } @@ -72,5 +72,14 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s serializer.Serialize(writer, lst); } } + + /// + /// Create the JsonPatchDocument using the appropriate constructor + /// + /// The operations to assign to the JsonPatchDocument + /// A new instance of the JsonPatchDocument + protected virtual JsonPatchDocument CreateContainer(List operations ) => + new JsonPatchDocument(operations, new DefaultContractResolver()); + } } diff --git a/src/Microsoft.AspNetCore.JsonPatch/Converters/TypedJsonPatchDocumentConverter.cs b/src/Microsoft.AspNetCore.JsonPatch/Converters/TypedJsonPatchDocumentConverter.cs index fd779ba..b37cb90 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Converters/TypedJsonPatchDocumentConverter.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Converters/TypedJsonPatchDocumentConverter.cs @@ -51,7 +51,7 @@ public override object ReadJson( serializer.Populate(jObjectReader, targetOperations); // container target: the typed JsonPatchDocument. - var container = Activator.CreateInstance(objectType, targetOperations, new DefaultContractResolver()); + var container = CreateTypedContainer(objectType, targetOperations); return container; } @@ -60,5 +60,16 @@ public override object ReadJson( throw new JsonSerializationException(Resources.InvalidJsonPatchDocument, ex); } } + + /// + /// Create the JsonPatchDocument using the appropriate constructor + /// + /// The model type for the JsonPatchDocument + /// The operations to assign to the JsonPatchDocument + /// A new instance of the JsonPatchDocument + protected virtual object CreateTypedContainer(Type objectType, object operations) + { + return Activator.CreateInstance(objectType, operations, new DefaultContractResolver()); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapterOfTU.cs b/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapterOfTU.cs index 8a344e2..be2bbff 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapterOfTU.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapterOfTU.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal { public class DictionaryAdapter : IAdapter { - public bool TryAdd( + public virtual bool TryAdd( object target, string segment, IContractResolver contractResolver, @@ -37,7 +37,7 @@ public bool TryAdd( return true; } - public bool TryGet( + public virtual bool TryGet( object target, string segment, IContractResolver contractResolver, @@ -66,7 +66,7 @@ public bool TryGet( return true; } - public bool TryRemove( + public virtual bool TryRemove( object target, string segment, IContractResolver contractResolver, @@ -94,7 +94,7 @@ public bool TryRemove( return true; } - public bool TryReplace( + public virtual bool TryReplace( object target, string segment, IContractResolver contractResolver, @@ -128,7 +128,7 @@ public bool TryReplace( return true; } - public bool TryTest( + public virtual bool TryTest( object target, string segment, IContractResolver contractResolver, @@ -177,7 +177,7 @@ public bool TryTest( } } - public bool TryTraverse( + public virtual bool TryTraverse( object target, string segment, IContractResolver contractResolver, @@ -208,7 +208,7 @@ public bool TryTraverse( } } - private bool TryConvertKey(string key, out TKey convertedKey, out string errorMessage) + protected virtual bool TryConvertKey(string key, out TKey convertedKey, out string errorMessage) { var conversionResult = ConversionResultProvider.ConvertTo(key, typeof(TKey)); if (conversionResult.CanBeConverted) @@ -225,7 +225,7 @@ private bool TryConvertKey(string key, out TKey convertedKey, out string errorMe } } - private bool TryConvertValue(object value, out TValue convertedValue, out string errorMessage) + protected virtual bool TryConvertValue(object value, out TValue convertedValue, out string errorMessage) { var conversionResult = ConversionResultProvider.ConvertTo(value, typeof(TValue)); if (conversionResult.CanBeConverted) diff --git a/src/Microsoft.AspNetCore.JsonPatch/Internal/DynamicObjectAdapter.cs b/src/Microsoft.AspNetCore.JsonPatch/Internal/DynamicObjectAdapter.cs index fb4adeb..dc3c482 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Internal/DynamicObjectAdapter.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Internal/DynamicObjectAdapter.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal { public class DynamicObjectAdapter : IAdapter { - public bool TryAdd( + public virtual bool TryAdd( object target, string segment, IContractResolver contractResolver, @@ -31,7 +31,7 @@ public bool TryAdd( return true; } - public bool TryGet( + public virtual bool TryGet( object target, string segment, IContractResolver contractResolver, @@ -48,7 +48,7 @@ public bool TryGet( return true; } - public bool TryRemove( + public virtual bool TryRemove( object target, string segment, IContractResolver contractResolver, @@ -78,7 +78,7 @@ public bool TryRemove( } - public bool TryReplace( + public virtual bool TryReplace( object target, string segment, IContractResolver contractResolver, @@ -105,7 +105,7 @@ public bool TryReplace( return true; } - public bool TryTest( + public virtual bool TryTest( object target, string segment, IContractResolver contractResolver, @@ -135,7 +135,7 @@ public bool TryTest( } } - public bool TryTraverse( + public virtual bool TryTraverse( object target, string segment, IContractResolver contractResolver, @@ -155,7 +155,7 @@ public bool TryTraverse( } } - private bool TryGetDynamicObjectProperty( + protected virtual bool TryGetDynamicObjectProperty( object target, IContractResolver contractResolver, string segment, @@ -191,7 +191,7 @@ private bool TryGetDynamicObjectProperty( } } - private bool TrySetDynamicObjectProperty( + protected virtual bool TrySetDynamicObjectProperty( object target, IContractResolver contractResolver, string segment, @@ -227,7 +227,7 @@ private bool TrySetDynamicObjectProperty( } } - private bool TryConvertValue(object value, Type propertyType, out object convertedValue) + protected virtual bool TryConvertValue(object value, Type propertyType, out object convertedValue) { var conversionResult = ConversionResultProvider.ConvertTo(value, propertyType); if (!conversionResult.CanBeConverted) diff --git a/src/Microsoft.AspNetCore.JsonPatch/Internal/ListAdapter.cs b/src/Microsoft.AspNetCore.JsonPatch/Internal/ListAdapter.cs index d1348fd..597f7b9 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Internal/ListAdapter.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Internal/ListAdapter.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal { public class ListAdapter : IAdapter { - public bool TryAdd( + public virtual bool TryAdd( object target, string segment, IContractResolver contractResolver, @@ -50,7 +50,7 @@ public bool TryAdd( return true; } - public bool TryGet( + public virtual bool TryGet( object target, string segment, IContractResolver contractResolver, @@ -84,7 +84,7 @@ public bool TryGet( return true; } - public bool TryRemove( + public virtual bool TryRemove( object target, string segment, IContractResolver contractResolver, @@ -115,7 +115,7 @@ public bool TryRemove( return true; } - public bool TryReplace( + public virtual bool TryReplace( object target, string segment, IContractResolver contractResolver, @@ -152,7 +152,7 @@ public bool TryReplace( return true; } - public bool TryTest( + public virtual bool TryTest( object target, string segment, IContractResolver contractResolver, @@ -189,7 +189,7 @@ public bool TryTest( } } - public bool TryTraverse( + public virtual bool TryTraverse( object target, string segment, IContractResolver contractResolver, @@ -224,7 +224,7 @@ public bool TryTraverse( return true; } - private bool TryConvertValue( + protected virtual bool TryConvertValue( object originalValue, Type listTypeArgument, string segment, @@ -244,7 +244,7 @@ private bool TryConvertValue( return true; } - private bool TryGetListTypeArgument(IList list, out Type listTypeArgument, out string errorMessage) + protected virtual bool TryGetListTypeArgument(IList list, out Type listTypeArgument, out string errorMessage) { // Arrays are not supported as they have fixed size and operations like Add, Insert do not make sense var listType = list.GetType(); @@ -272,7 +272,7 @@ private bool TryGetListTypeArgument(IList list, out Type listTypeArgument, out s } } - private bool TryGetPositionInfo( + protected virtual bool TryGetPositionInfo( IList list, string segment, OperationType operationType, @@ -318,7 +318,7 @@ private bool TryGetPositionInfo( } } - private struct PositionInfo + protected struct PositionInfo { public PositionInfo(PositionType type, int index) { @@ -330,7 +330,7 @@ public PositionInfo(PositionType type, int index) public int Index { get; } } - private enum PositionType + protected enum PositionType { Index, // valid index EndOfList, // '-' @@ -338,7 +338,7 @@ private enum PositionType OutOfBounds } - private enum OperationType + protected enum OperationType { Add, Remove, diff --git a/src/Microsoft.AspNetCore.JsonPatch/Internal/ObjectVisitor.cs b/src/Microsoft.AspNetCore.JsonPatch/Internal/ObjectVisitor.cs index 8994f0a..297a8b4 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Internal/ObjectVisitor.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Internal/ObjectVisitor.cs @@ -3,19 +3,37 @@ using System; using System.Collections; +using Microsoft.AspNetCore.JsonPatch.Adapters; using Newtonsoft.Json.Serialization; namespace Microsoft.AspNetCore.JsonPatch.Internal { public class ObjectVisitor { + private readonly IAdapterFactory _adapterFactory; private readonly IContractResolver _contractResolver; private readonly ParsedPath _path; - public ObjectVisitor(ParsedPath path, IContractResolver contractResolver) + /// + /// Initializes a new instance of using the default . + /// + /// The path of the JsonPatch operation + /// The . + public ObjectVisitor(ParsedPath path, IContractResolver contractResolver) : this(path, contractResolver, new AdapterFactory()) + { + } + + /// + /// Initializes a new instance of . + /// + /// The path of the JsonPatch operation + /// The . + /// The to use when creating adaptors. + public ObjectVisitor(ParsedPath path, IContractResolver contractResolver, IAdapterFactory adapterFactory) { _path = path; _contractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver)); + _adapterFactory = adapterFactory; } public bool TryVisit(ref object target, out IAdapter adapter, out string errorMessage) @@ -48,25 +66,7 @@ public bool TryVisit(ref object target, out IAdapter adapter, out string errorMe private IAdapter SelectAdapter(object targetObject) { - var jsonContract = _contractResolver.ResolveContract(targetObject.GetType()); - - if (targetObject is IList) - { - return new ListAdapter(); - } - else if (jsonContract is JsonDictionaryContract jsonDictionaryContract) - { - var type = typeof(DictionaryAdapter<,>).MakeGenericType(jsonDictionaryContract.DictionaryKeyType, jsonDictionaryContract.DictionaryValueType); - return (IAdapter)Activator.CreateInstance(type); - } - else if (jsonContract is JsonDynamicContract) - { - return new DynamicObjectAdapter(); - } - else - { - return new PocoAdapter(); - } + return _adapterFactory.Create(targetObject, _contractResolver); } } } diff --git a/src/Microsoft.AspNetCore.JsonPatch/Internal/PocoAdapter.cs b/src/Microsoft.AspNetCore.JsonPatch/Internal/PocoAdapter.cs index 0eee0fc..5ba3e55 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Internal/PocoAdapter.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Internal/PocoAdapter.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal { public class PocoAdapter : IAdapter { - public bool TryAdd( + public virtual bool TryAdd( object target, string segment, IContractResolver contractResolver, @@ -43,7 +43,7 @@ public bool TryAdd( return true; } - public bool TryGet( + public virtual bool TryGet( object target, string segment, IContractResolver contractResolver, @@ -69,7 +69,7 @@ public bool TryGet( return true; } - public bool TryRemove( + public virtual bool TryRemove( object target, string segment, IContractResolver contractResolver, @@ -102,7 +102,7 @@ public bool TryRemove( return true; } - public bool TryReplace( + public virtual bool TryReplace( object target, string segment, IContractResolver @@ -134,7 +134,7 @@ public bool TryReplace( return true; } - public bool TryTest( + public virtual bool TryTest( object target, string segment, IContractResolver @@ -171,7 +171,7 @@ public bool TryTest( return true; } - public bool TryTraverse( + public virtual bool TryTraverse( object target, string segment, IContractResolver contractResolver, @@ -197,7 +197,7 @@ public bool TryTraverse( return false; } - private bool TryGetJsonProperty( + protected virtual bool TryGetJsonProperty( object target, IContractResolver contractResolver, string segment, @@ -220,7 +220,7 @@ private bool TryGetJsonProperty( return false; } - private bool TryConvertValue(object value, Type propertyType, out object convertedValue) + protected virtual bool TryConvertValue(object value, Type propertyType, out object convertedValue) { var conversionResult = ConversionResultProvider.ConvertTo(value, propertyType); if (!conversionResult.CanBeConverted) diff --git a/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocument.cs b/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocument.cs index 4420e57..094ff35 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocument.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocument.cs @@ -24,26 +24,25 @@ public class JsonPatchDocument : IJsonPatchDocument [JsonIgnore] public IContractResolver ContractResolver { get; set; } - public JsonPatchDocument() + /// + /// Gets or sets the AdapterFactory to use when processing this document + /// + [JsonIgnore] + public IAdapterFactory AdapterFactory { get; set; } + + public JsonPatchDocument():this(new List(), new DefaultContractResolver()) { - Operations = new List(); - ContractResolver = new DefaultContractResolver(); } - public JsonPatchDocument(List operations, IContractResolver contractResolver) + public JsonPatchDocument(List operations, IContractResolver contractResolver):this(operations, contractResolver, new AdapterFactory()) { - if (operations == null) - { - throw new ArgumentNullException(nameof(operations)); - } - - if (contractResolver == null) - { - throw new ArgumentNullException(nameof(contractResolver)); - } + } - Operations = operations; - ContractResolver = contractResolver; + public JsonPatchDocument(List operations, IContractResolver contractResolver, IAdapterFactory adapterFactory) + { + Operations = operations ?? throw new ArgumentNullException(nameof(operations)); + ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver)); + AdapterFactory = adapterFactory ?? throw new ArgumentNullException(nameof(adapterFactory)); } /// @@ -174,7 +173,7 @@ public void ApplyTo(object objectToApplyTo) throw new ArgumentNullException(nameof(objectToApplyTo)); } - ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction: null)); + ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, null, AdapterFactory)); } /// @@ -189,7 +188,7 @@ public void ApplyTo(object objectToApplyTo, Action logErrorActio throw new ArgumentNullException(nameof(objectToApplyTo)); } - var adapter = new ObjectAdapter(ContractResolver, logErrorAction); + var adapter = new ObjectAdapter(ContractResolver, logErrorAction, AdapterFactory); foreach (var op in Operations) { try diff --git a/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs b/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs index 8ae1430..c5c5b19 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs @@ -28,17 +28,25 @@ public class JsonPatchDocument : IJsonPatchDocument where TModel : class [JsonIgnore] public IContractResolver ContractResolver { get; set; } - public JsonPatchDocument() + /// + /// Gets or sets the AdapterFactory to use when processing this document + /// + [JsonIgnore] + public IAdapterFactory AdapterFactory { get; set; } + + public JsonPatchDocument() : this(new List>(), new DefaultContractResolver()) + { + } + + public JsonPatchDocument(List> operations, IContractResolver contractResolver) : this(operations, contractResolver, new AdapterFactory()) { - Operations = new List>(); - ContractResolver = new DefaultContractResolver(); } - // Create from list of operations - public JsonPatchDocument(List> operations, IContractResolver contractResolver) + public JsonPatchDocument(List> operations, IContractResolver contractResolver, IAdapterFactory adapterFactory) { Operations = operations ?? throw new ArgumentNullException(nameof(operations)); ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver)); + AdapterFactory = adapterFactory ?? throw new ArgumentNullException(nameof(adapterFactory)); } /// @@ -697,7 +705,7 @@ public void ApplyTo(TModel objectToApplyTo) throw new ArgumentNullException(nameof(objectToApplyTo)); } - ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction: null)); + ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, null, AdapterFactory)); } /// @@ -712,7 +720,7 @@ public void ApplyTo(TModel objectToApplyTo, Action logErrorActio throw new ArgumentNullException(nameof(objectToApplyTo)); } - var adapter = new ObjectAdapter(ContractResolver, logErrorAction); + var adapter = new ObjectAdapter(ContractResolver, logErrorAction, AdapterFactory); foreach (var op in Operations) { try diff --git a/test/Microsoft.AspNetCore.JsonPatch.Test/JsonPatchDocumentTest.cs b/test/Microsoft.AspNetCore.JsonPatch.Test/JsonPatchDocumentTest.cs index 197e514..1da12fc 100644 --- a/test/Microsoft.AspNetCore.JsonPatch.Test/JsonPatchDocumentTest.cs +++ b/test/Microsoft.AspNetCore.JsonPatch.Test/JsonPatchDocumentTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Microsoft.AspNetCore.JsonPatch.Test; using Newtonsoft.Json; using Xunit; @@ -158,5 +159,30 @@ var deserialized // Assert Assert.Equal("The JSON patch document was malformed and could not be parsed.", exception.Message); } + + [Fact] + public void Deserialization_Supports_Custom_AdapterFactory() + { + // Arrange + var doc = new SimpleObject() + { + StringProperty = "A", + DecimalValue = 10, + DoubleValue = 10, + FloatValue = 10, + IntegerValue = 10 + }; + + var patchDocument = new JsonPatchDocument(); + + var serialized = JsonConvert.SerializeObject(patchDocument); + JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); + serializerSettings.ContractResolver = new JsonPatchDocumentResolverForCustomFactory(); + + JsonPatchDocument deserialized = JsonConvert.DeserializeObject>(serialized, serializerSettings); + + // Assert + Assert.Equal(typeof(TestAdapterFactory) ,deserialized.AdapterFactory.GetType()); + } } } diff --git a/test/Microsoft.AspNetCore.JsonPatch.Test/TestAdapterFactory.cs b/test/Microsoft.AspNetCore.JsonPatch.Test/TestAdapterFactory.cs new file mode 100644 index 0000000..a88bf2c --- /dev/null +++ b/test/Microsoft.AspNetCore.JsonPatch.Test/TestAdapterFactory.cs @@ -0,0 +1,80 @@ +using Microsoft.AspNetCore.JsonPatch.Adapters; +using Microsoft.AspNetCore.JsonPatch.Converters; +using Microsoft.AspNetCore.JsonPatch.Internal; +using Microsoft.AspNetCore.JsonPatch.Operations; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.JsonPatch.Test +{ + public class TestAdapterFactory : IAdapterFactory + { + public IAdapter Create(object target, IContractResolver contractResolver) + { + return new TestAdapter(target, contractResolver); + } + } + + public class TestAdapter : IAdapter + { + public Object Target { get; } + public IContractResolver ContractResolver { get; } + public TestAdapter(object target, IContractResolver contractResolver) + { + Target = target; + ContractResolver = contractResolver; + } + public bool TryAdd(object target, string segment, IContractResolver contractResolver, object value, out string errorMessage) + { + throw new NotImplementedException(); + } + + public bool TryGet(object target, string segment, IContractResolver contractResolver, out object value, out string errorMessage) + { + throw new NotImplementedException(); + } + + public bool TryRemove(object target, string segment, IContractResolver contractResolver, out string errorMessage) + { + throw new NotImplementedException(); + } + + public bool TryReplace(object target, string segment, IContractResolver contractResolver, object value, out string errorMessage) + { + throw new NotImplementedException(); + } + + public bool TryTest(object target, string segment, IContractResolver contractResolver, object value, out string errorMessage) + { + throw new NotImplementedException(); + } + + public bool TryTraverse(object target, string segment, IContractResolver contractResolver, out object nextTarget, out string errorMessage) + { + throw new NotImplementedException(); + } + } + + public class JsonPatchDocumentResolverForCustomFactory:DefaultContractResolver + { + protected override JsonConverter ResolveContractConverter(Type objectType) + { + return new JsonPatchDocumentConverterWithCustomFactory(); + } + } + + public class JsonPatchDocumentConverterWithCustomFactory: TypedJsonPatchDocumentConverter + { + public JsonPatchDocumentConverterWithCustomFactory() + { + } + + protected override object CreateTypedContainer(Type objectType, object operations) + { + return Activator.CreateInstance(objectType, operations, new DefaultContractResolver(), new TestAdapterFactory()); + } + } +}