Initial commit providing the AdapterFactory directly to the ObjectAdapter to all for customization of the Adapter selection and the ability to override the built in adapters to leverage their ability as much as possible.
This commit is contained in:
parent
d7ac5c57ef
commit
4d92d76b64
|
|
@ -0,0 +1,39 @@
|
||||||
|
using Microsoft.AspNetCore.JsonPatch.Internal;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The default AdapterFactory to be used for resolving <see cref="IAdapter"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class AdapterFactory : IAdapterFactory
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the operations used for loading an <see cref="IAdapter"/> based on the current object and ContractResolver.
|
||||||
|
/// </summary>
|
||||||
|
public interface IAdapterFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an <see cref="IAdapter"/> for the current object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The target object</param>
|
||||||
|
/// <param name="contractResolver">The current contract resolver</param>
|
||||||
|
/// <returns>The needed <see cref="IAdapter"/></returns>
|
||||||
|
IAdapter Create(object target, IContractResolver contractResolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,17 +18,37 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
||||||
/// <param name="logErrorAction">The <see cref="Action"/> for logging <see cref="JsonPatchError"/>.</param>
|
/// <param name="logErrorAction">The <see cref="Action"/> for logging <see cref="JsonPatchError"/>.</param>
|
||||||
public ObjectAdapter(
|
public ObjectAdapter(
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
Action<JsonPatchError> logErrorAction)
|
Action<JsonPatchError> logErrorAction):
|
||||||
|
this(contractResolver, logErrorAction, new AdapterFactory())
|
||||||
{
|
{
|
||||||
ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver));
|
|
||||||
LogErrorAction = logErrorAction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ObjectAdapter"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="contractResolver">The <see cref="IContractResolver"/>.</param>
|
||||||
|
/// <param name="logErrorAction">The <see cref="Action"/> for logging <see cref="JsonPatchError"/>.</param>
|
||||||
|
/// <param name="adapterFactory">The <see cref="IAdapterFactory"/> to use when creating adaptors.</param>
|
||||||
|
public ObjectAdapter(
|
||||||
|
IContractResolver contractResolver,
|
||||||
|
Action<JsonPatchError> logErrorAction,
|
||||||
|
IAdapterFactory adapterFactory)
|
||||||
|
{
|
||||||
|
ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver));
|
||||||
|
LogErrorAction = logErrorAction;
|
||||||
|
AdapterFactory = adapterFactory ?? throw new ArgumentNullException(nameof(adapterFactory));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="IContractResolver"/>.
|
/// Gets or sets the <see cref="IContractResolver"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IContractResolver ContractResolver { get; }
|
public IContractResolver ContractResolver { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="IAdapterFactory"/>
|
||||||
|
/// </summary>
|
||||||
|
public IAdapterFactory AdapterFactory { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Action for logging <see cref="JsonPatchError"/>.
|
/// Action for logging <see cref="JsonPatchError"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -75,7 +95,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsedPath = new ParsedPath(path);
|
var parsedPath = new ParsedPath(path);
|
||||||
var visitor = new ObjectVisitor(parsedPath, ContractResolver);
|
var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
|
||||||
|
|
||||||
var target = objectToApplyTo;
|
var target = objectToApplyTo;
|
||||||
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
||||||
|
|
@ -144,7 +164,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
||||||
private void Remove(string path, object objectToApplyTo, Operation operationToReport)
|
private void Remove(string path, object objectToApplyTo, Operation operationToReport)
|
||||||
{
|
{
|
||||||
var parsedPath = new ParsedPath(path);
|
var parsedPath = new ParsedPath(path);
|
||||||
var visitor = new ObjectVisitor(parsedPath, ContractResolver);
|
var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
|
||||||
|
|
||||||
var target = objectToApplyTo;
|
var target = objectToApplyTo;
|
||||||
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
||||||
|
|
@ -175,7 +195,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsedPath = new ParsedPath(operation.path);
|
var parsedPath = new ParsedPath(operation.path);
|
||||||
var visitor = new ObjectVisitor(parsedPath, ContractResolver);
|
var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
|
||||||
|
|
||||||
var target = objectToApplyTo;
|
var target = objectToApplyTo;
|
||||||
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
||||||
|
|
@ -239,7 +259,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsedPath = new ParsedPath(operation.path);
|
var parsedPath = new ParsedPath(operation.path);
|
||||||
var visitor = new ObjectVisitor(parsedPath, ContractResolver);
|
var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
|
||||||
|
|
||||||
var target = objectToApplyTo;
|
var target = objectToApplyTo;
|
||||||
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
||||||
|
|
@ -281,7 +301,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
||||||
propertyValue = null;
|
propertyValue = null;
|
||||||
|
|
||||||
var parsedPath = new ParsedPath(fromLocation);
|
var parsedPath = new ParsedPath(fromLocation);
|
||||||
var visitor = new ObjectVisitor(parsedPath, ContractResolver);
|
var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory);
|
||||||
|
|
||||||
var target = objectToGetValueFrom;
|
var target = objectToGetValueFrom;
|
||||||
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage))
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
{
|
{
|
||||||
public class DictionaryAdapter<TKey, TValue> : IAdapter
|
public class DictionaryAdapter<TKey, TValue> : IAdapter
|
||||||
{
|
{
|
||||||
public bool TryAdd(
|
public virtual bool TryAdd(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGet(
|
public virtual bool TryGet(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryRemove(
|
public virtual bool TryRemove(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryReplace(
|
public virtual bool TryReplace(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTest(
|
public virtual bool TryTest(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -177,7 +177,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTraverse(
|
public virtual bool TryTraverse(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
var conversionResult = ConversionResultProvider.ConvertTo(key, typeof(TKey));
|
||||||
if (conversionResult.CanBeConverted)
|
if (conversionResult.CanBeConverted)
|
||||||
|
|
@ -225,7 +225,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
var conversionResult = ConversionResultProvider.ConvertTo(value, typeof(TValue));
|
||||||
if (conversionResult.CanBeConverted)
|
if (conversionResult.CanBeConverted)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
{
|
{
|
||||||
public class DynamicObjectAdapter : IAdapter
|
public class DynamicObjectAdapter : IAdapter
|
||||||
{
|
{
|
||||||
public bool TryAdd(
|
public virtual bool TryAdd(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGet(
|
public virtual bool TryGet(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryRemove(
|
public virtual bool TryRemove(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryReplace(
|
public virtual bool TryReplace(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTest(
|
public virtual bool TryTest(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTraverse(
|
public virtual bool TryTraverse(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -155,7 +155,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetDynamicObjectProperty(
|
protected virtual bool TryGetDynamicObjectProperty(
|
||||||
object target,
|
object target,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
string segment,
|
string segment,
|
||||||
|
|
@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TrySetDynamicObjectProperty(
|
protected virtual bool TrySetDynamicObjectProperty(
|
||||||
object target,
|
object target,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
string segment,
|
string segment,
|
||||||
|
|
@ -227,7 +227,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
var conversionResult = ConversionResultProvider.ConvertTo(value, propertyType);
|
||||||
if (!conversionResult.CanBeConverted)
|
if (!conversionResult.CanBeConverted)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
{
|
{
|
||||||
public class ListAdapter : IAdapter
|
public class ListAdapter : IAdapter
|
||||||
{
|
{
|
||||||
public bool TryAdd(
|
public virtual bool TryAdd(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGet(
|
public virtual bool TryGet(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryRemove(
|
public virtual bool TryRemove(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryReplace(
|
public virtual bool TryReplace(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -152,7 +152,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTest(
|
public virtual bool TryTest(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -189,7 +189,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTraverse(
|
public virtual bool TryTraverse(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -224,7 +224,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryConvertValue(
|
protected virtual bool TryConvertValue(
|
||||||
object originalValue,
|
object originalValue,
|
||||||
Type listTypeArgument,
|
Type listTypeArgument,
|
||||||
string segment,
|
string segment,
|
||||||
|
|
@ -244,7 +244,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
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
|
// Arrays are not supported as they have fixed size and operations like Add, Insert do not make sense
|
||||||
var listType = list.GetType();
|
var listType = list.GetType();
|
||||||
|
|
@ -272,7 +272,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetPositionInfo(
|
protected virtual bool TryGetPositionInfo(
|
||||||
IList list,
|
IList list,
|
||||||
string segment,
|
string segment,
|
||||||
OperationType operationType,
|
OperationType operationType,
|
||||||
|
|
@ -318,7 +318,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct PositionInfo
|
protected struct PositionInfo
|
||||||
{
|
{
|
||||||
public PositionInfo(PositionType type, int index)
|
public PositionInfo(PositionType type, int index)
|
||||||
{
|
{
|
||||||
|
|
@ -330,7 +330,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
public int Index { get; }
|
public int Index { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum PositionType
|
protected enum PositionType
|
||||||
{
|
{
|
||||||
Index, // valid index
|
Index, // valid index
|
||||||
EndOfList, // '-'
|
EndOfList, // '-'
|
||||||
|
|
@ -338,7 +338,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
OutOfBounds
|
OutOfBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum OperationType
|
protected enum OperationType
|
||||||
{
|
{
|
||||||
Add,
|
Add,
|
||||||
Remove,
|
Remove,
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,38 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using Microsoft.AspNetCore.JsonPatch.Adapters;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.JsonPatch.Internal
|
namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
{
|
{
|
||||||
public class ObjectVisitor
|
public class ObjectVisitor
|
||||||
{
|
{
|
||||||
|
private readonly IAdapterFactory _adapterFactory;
|
||||||
private readonly IContractResolver _contractResolver;
|
private readonly IContractResolver _contractResolver;
|
||||||
private readonly ParsedPath _path;
|
private readonly ParsedPath _path;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ObjectVisitor"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path of the JsonPatch operation</param>
|
||||||
|
/// <param name="contractResolver">The <see cref="IContractResolver"/>.</param>
|
||||||
public ObjectVisitor(ParsedPath path, IContractResolver contractResolver)
|
public ObjectVisitor(ParsedPath path, IContractResolver contractResolver)
|
||||||
|
:this(path, contractResolver, new AdapterFactory())
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ObjectVisitor"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path of the JsonPatch operation</param>
|
||||||
|
/// <param name="contractResolver">The <see cref="IContractResolver"/>.</param>
|
||||||
|
/// <param name="adapterFactory">The <see cref="IAdapterFactory"/> to use when creating adaptors.</param>
|
||||||
|
public ObjectVisitor(ParsedPath path, IContractResolver contractResolver, IAdapterFactory adapterFactory)
|
||||||
|
{
|
||||||
_path = path;
|
_path = path;
|
||||||
_contractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver));
|
_contractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver));
|
||||||
|
_adapterFactory = adapterFactory ?? throw new ArgumentNullException(nameof(adapterFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryVisit(ref object target, out IAdapter adapter, out string errorMessage)
|
public bool TryVisit(ref object target, out IAdapter adapter, out string errorMessage)
|
||||||
|
|
@ -48,25 +67,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
|
|
||||||
private IAdapter SelectAdapter(object targetObject)
|
private IAdapter SelectAdapter(object targetObject)
|
||||||
{
|
{
|
||||||
var jsonContract = _contractResolver.ResolveContract(targetObject.GetType());
|
return _adapterFactory.Create(targetObject, _contractResolver);
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
{
|
{
|
||||||
public class PocoAdapter : IAdapter
|
public class PocoAdapter : IAdapter
|
||||||
{
|
{
|
||||||
public bool TryAdd(
|
public virtual bool TryAdd(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGet(
|
public virtual bool TryGet(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryRemove(
|
public virtual bool TryRemove(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryReplace(
|
public virtual bool TryReplace(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver
|
IContractResolver
|
||||||
|
|
@ -134,7 +134,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTest(
|
public virtual bool TryTest(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver
|
IContractResolver
|
||||||
|
|
@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTraverse(
|
public virtual bool TryTraverse(
|
||||||
object target,
|
object target,
|
||||||
string segment,
|
string segment,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
|
|
@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetJsonProperty(
|
protected virtual bool TryGetJsonProperty(
|
||||||
object target,
|
object target,
|
||||||
IContractResolver contractResolver,
|
IContractResolver contractResolver,
|
||||||
string segment,
|
string segment,
|
||||||
|
|
@ -220,7 +220,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||||
return false;
|
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);
|
var conversionResult = ConversionResultProvider.ConvertTo(value, propertyType);
|
||||||
if (!conversionResult.CanBeConverted)
|
if (!conversionResult.CanBeConverted)
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ namespace Microsoft.AspNetCore.JsonPatch
|
||||||
throw new ArgumentNullException(nameof(objectToApplyTo));
|
throw new ArgumentNullException(nameof(objectToApplyTo));
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction: null));
|
ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, null, new AdapterFactory()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -183,13 +183,28 @@ namespace Microsoft.AspNetCore.JsonPatch
|
||||||
/// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
|
/// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
|
||||||
/// <param name="logErrorAction">Action to log errors</param>
|
/// <param name="logErrorAction">Action to log errors</param>
|
||||||
public void ApplyTo(object objectToApplyTo, Action<JsonPatchError> logErrorAction)
|
public void ApplyTo(object objectToApplyTo, Action<JsonPatchError> logErrorAction)
|
||||||
|
{
|
||||||
|
ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction, new AdapterFactory()), logErrorAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply this JsonPatchDocument
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
|
||||||
|
/// <param name="adapter">IObjectAdapter instance to use when applying</param>
|
||||||
|
/// <param name="logErrorAction">Action to log errors</param>
|
||||||
|
public void ApplyTo(object objectToApplyTo, IObjectAdapter adapter, Action<JsonPatchError> logErrorAction)
|
||||||
{
|
{
|
||||||
if (objectToApplyTo == null)
|
if (objectToApplyTo == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(objectToApplyTo));
|
throw new ArgumentNullException(nameof(objectToApplyTo));
|
||||||
}
|
}
|
||||||
|
|
||||||
var adapter = new ObjectAdapter(ContractResolver, logErrorAction);
|
if (adapter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(adapter));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var op in Operations)
|
foreach (var op in Operations)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -697,7 +697,7 @@ namespace Microsoft.AspNetCore.JsonPatch
|
||||||
throw new ArgumentNullException(nameof(objectToApplyTo));
|
throw new ArgumentNullException(nameof(objectToApplyTo));
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction: null));
|
ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, null, new AdapterFactory()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -706,13 +706,28 @@ namespace Microsoft.AspNetCore.JsonPatch
|
||||||
/// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
|
/// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
|
||||||
/// <param name="logErrorAction">Action to log errors</param>
|
/// <param name="logErrorAction">Action to log errors</param>
|
||||||
public void ApplyTo(TModel objectToApplyTo, Action<JsonPatchError> logErrorAction)
|
public void ApplyTo(TModel objectToApplyTo, Action<JsonPatchError> logErrorAction)
|
||||||
|
{
|
||||||
|
ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction, new AdapterFactory()), logErrorAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply this JsonPatchDocument
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="objectToApplyTo">Object to apply the JsonPatchDocument to</param>
|
||||||
|
/// <param name="adapter">IObjectAdapter instance to use when applying</param>
|
||||||
|
/// <param name="logErrorAction">Action to log errors</param>
|
||||||
|
public void ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter, Action<JsonPatchError> logErrorAction)
|
||||||
{
|
{
|
||||||
if (objectToApplyTo == null)
|
if (objectToApplyTo == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(objectToApplyTo));
|
throw new ArgumentNullException(nameof(objectToApplyTo));
|
||||||
}
|
}
|
||||||
|
|
||||||
var adapter = new ObjectAdapter(ContractResolver, logErrorAction);
|
if (adapter == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(adapter));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var op in Operations)
|
foreach (var op in Operations)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
using Microsoft.AspNetCore.JsonPatch.Adapters;
|
||||||
|
using Microsoft.AspNetCore.JsonPatch.Internal;
|
||||||
|
using Moq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Dynamic;
|
||||||
|
using System.Text;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.JsonPatch.Test.Adapters
|
||||||
|
{
|
||||||
|
public class AdapterFactoryTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetListAdapterForListTargets()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
AdapterFactory factory = new AdapterFactory();
|
||||||
|
|
||||||
|
//Act:
|
||||||
|
IAdapter adapter = factory.Create(new List<string>(), new DefaultContractResolver());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(typeof(ListAdapter), adapter.GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDictionaryAdapterForDictionaryObjects()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
AdapterFactory factory = new AdapterFactory();
|
||||||
|
|
||||||
|
//Act:
|
||||||
|
IAdapter adapter = factory.Create(new Dictionary<string, string>(), new DefaultContractResolver());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(typeof(DictionaryAdapter<string, string>), adapter.GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PocoModel
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetPocoAdapterForGenericObjects()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
AdapterFactory factory = new AdapterFactory();
|
||||||
|
|
||||||
|
//Act:
|
||||||
|
IAdapter adapter = factory.Create(new PocoModel(), new DefaultContractResolver());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(typeof(PocoAdapter), adapter.GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDynamicAdapterForGenericObjects()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
AdapterFactory factory = new AdapterFactory();
|
||||||
|
|
||||||
|
//Act:
|
||||||
|
IAdapter adapter = factory.Create(new TestDynamicObject(), new DefaultContractResolver());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(typeof(DynamicObjectAdapter), adapter.GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Dynamic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.JsonPatch.Test.Adapters
|
||||||
|
{
|
||||||
|
public class TestDynamicObject : DynamicObject
|
||||||
|
{ }
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue