From 0f51c56c3fa120edeeaec51629117338c6265fd3 Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Tue, 5 Sep 2017 16:07:25 -0700 Subject: [PATCH] Combine ExpandoObjectAdapter and DictionaryAdapter (#106) Use JsonDictionaryContract in the DictionaryAdapter and combine with ExpandoObjectAdapter --- .../Internal/DictionaryAdapter.cs | 111 ------------ ...ectAdapter.cs => DictionaryAdapterOfTU.cs} | 115 ++++++++---- .../Internal/ObjectVisitor.cs | 27 +-- .../JsonPatchDocumentOfT.cs | 14 +- .../Properties/Resources.Designer.cs | 14 ++ .../Resources.resx | 3 + .../DictionaryAdapterTest.cs | 171 +++++++++++++++--- .../Dynamic/ReplaceOperationTests.cs | 60 ++---- .../ObjectVisitorTest.cs | 8 +- 9 files changed, 273 insertions(+), 250 deletions(-) delete mode 100644 src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapter.cs rename src/Microsoft.AspNetCore.JsonPatch/Internal/{ExpandoObjectAdapter.cs => DictionaryAdapterOfTU.cs} (52%) diff --git a/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapter.cs b/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapter.cs deleted file mode 100644 index 4c7651e475..0000000000 --- a/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapter.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections; -using Newtonsoft.Json.Serialization; - -namespace Microsoft.AspNetCore.JsonPatch.Internal -{ - public class DictionaryAdapter : IAdapter - { - public bool TryAdd( - object target, - string segment, - IContractResolver contractResolver, - object value, - out string errorMessage) - { - var dictionary = (IDictionary)target; - - // As per JsonPatch spec, if a key already exists, adding should replace the existing value - dictionary[segment] = value; - - errorMessage = null; - return true; - } - - public bool TryGet( - object target, - string segment, - IContractResolver contractResolver, - out object value, - out string errorMessage) - { - var dictionary = (IDictionary)target; - - value = dictionary[segment]; - errorMessage = null; - return true; - } - - public bool TryRemove( - object target, - string segment, - IContractResolver contractResolver, - out string errorMessage) - { - var dictionary = (IDictionary)target; - - // As per JsonPatch spec, the target location must exist for remove to be successful - if (!dictionary.Contains(segment)) - { - errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); - return false; - } - - dictionary.Remove(segment); - errorMessage = null; - return true; - } - - public bool TryReplace( - object target, - string segment, - IContractResolver contractResolver, - object value, - out string errorMessage) - { - var dictionary = (IDictionary)target; - - // As per JsonPatch spec, the target location must exist for remove to be successful - if (!dictionary.Contains(segment)) - { - errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); - return false; - } - - dictionary[segment] = value; - errorMessage = null; - return true; - } - - public bool TryTraverse( - object target, - string segment, - IContractResolver contractResolver, - out object nextTarget, - out string errorMessage) - { - var dictionary = target as IDictionary; - if (dictionary == null) - { - nextTarget = null; - errorMessage = null; - return false; - } - - if (dictionary.Contains(segment)) - { - nextTarget = dictionary[segment]; - errorMessage = null; - return true; - } - else - { - nextTarget = null; - errorMessage = null; - return false; - } - } - } -} diff --git a/src/Microsoft.AspNetCore.JsonPatch/Internal/ExpandoObjectAdapter.cs b/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapterOfTU.cs similarity index 52% rename from src/Microsoft.AspNetCore.JsonPatch/Internal/ExpandoObjectAdapter.cs rename to src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapterOfTU.cs index c35d1b702c..67fd33e231 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Internal/ExpandoObjectAdapter.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Internal/DictionaryAdapterOfTU.cs @@ -2,12 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using System.Dynamic; using Newtonsoft.Json.Serialization; namespace Microsoft.AspNetCore.JsonPatch.Internal { - public class ExpandoObjectAdapter : IAdapter + public class DictionaryAdapter : IAdapter { public bool TryAdd( object target, @@ -18,11 +17,20 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal { var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); var key = contract.DictionaryKeyResolver(segment); - var dictionary = (IDictionary)target; + var dictionary = (IDictionary)target; // As per JsonPatch spec, if a key already exists, adding should replace the existing value - dictionary[key] = ConvertValue(dictionary, key, value); + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + return false; + } + if (!TryConvertValue(value, out var convertedValue, out errorMessage)) + { + return false; + } + + dictionary[convertedKey] = convertedValue; errorMessage = null; return true; } @@ -36,10 +44,22 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal { var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); var key = contract.DictionaryKeyResolver(segment); - var dictionary = (IDictionary)target; + var dictionary = (IDictionary)target; - value = dictionary[key]; + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + value = null; + return false; + } + if (!dictionary.ContainsKey(convertedKey)) + { + value = null; + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + value = dictionary[convertedKey]; errorMessage = null; return true; } @@ -52,16 +72,21 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal { var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); var key = contract.DictionaryKeyResolver(segment); - var dictionary = (IDictionary)target; + var dictionary = (IDictionary)target; + + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + return false; + } // As per JsonPatch spec, the target location must exist for remove to be successful - if (!dictionary.ContainsKey(key)) + if (!dictionary.ContainsKey(convertedKey)) { errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); return false; } - dictionary.Remove(key); + dictionary.Remove(convertedKey); errorMessage = null; return true; @@ -76,16 +101,26 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal { var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); var key = contract.DictionaryKeyResolver(segment); - var dictionary = (IDictionary)target; + var dictionary = (IDictionary)target; + + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + return false; + } // As per JsonPatch spec, the target location must exist for remove to be successful - if (!dictionary.ContainsKey(key)) + if (!dictionary.ContainsKey(convertedKey)) { errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); return false; } - dictionary[key] = ConvertValue(dictionary, key, value); + if (!TryConvertValue(value, out var convertedValue, out errorMessage)) + { + return false; + } + + dictionary[convertedKey] = convertedValue; errorMessage = null; return true; @@ -98,21 +133,19 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal out object nextTarget, out string errorMessage) { - var expandoObject = target as ExpandoObject; - if (expandoObject == null) + var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); + var key = contract.DictionaryKeyResolver(segment); + var dictionary = (IDictionary)target; + + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) { - errorMessage = null; nextTarget = null; return false; } - var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); - var key = contract.DictionaryKeyResolver(segment); - var dictionary = (IDictionary)expandoObject; - - if (dictionary.ContainsKey(key)) + if (dictionary.ContainsKey(convertedKey)) { - nextTarget = dictionary[key]; + nextTarget = dictionary[convertedKey]; errorMessage = null; return true; } @@ -124,20 +157,38 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal } } - private object ConvertValue(IDictionary dictionary, string key, object newValue) + private bool TryConvertKey(string key, out TKey convertedKey, out string errorMessage) { - if (dictionary.TryGetValue(key, out var existingValue)) + var conversionResult = ConversionResultProvider.ConvertTo(key, typeof(TKey)); + if (conversionResult.CanBeConverted) { - if (existingValue != null) - { - var conversionResult = ConversionResultProvider.ConvertTo(newValue, existingValue.GetType()); - if (conversionResult.CanBeConverted) - { - return conversionResult.ConvertedInstance; - } - } + errorMessage = null; + convertedKey = (TKey)conversionResult.ConvertedInstance; + return true; + } + else + { + errorMessage = Resources.FormatInvalidPathSegment(key); + convertedKey = default(TKey); + return false; + } + } + + private bool TryConvertValue(object value, out TValue convertedValue, out string errorMessage) + { + var conversionResult = ConversionResultProvider.ConvertTo(value, typeof(TValue)); + if (conversionResult.CanBeConverted) + { + errorMessage = null; + convertedValue = (TValue)conversionResult.ConvertedInstance; + return true; + } + else + { + errorMessage = Resources.FormatInvalidValueForProperty(value); + convertedValue = default(TValue); + return false; } - return newValue; } } } diff --git a/src/Microsoft.AspNetCore.JsonPatch/Internal/ObjectVisitor.cs b/src/Microsoft.AspNetCore.JsonPatch/Internal/ObjectVisitor.cs index bbf6db4ff6..97f108c0a0 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Internal/ObjectVisitor.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Internal/ObjectVisitor.cs @@ -3,7 +3,9 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Dynamic; +using Microsoft.AspNetCore.JsonPatch.Adapters; using Newtonsoft.Json.Serialization; namespace Microsoft.AspNetCore.JsonPatch.Internal @@ -49,22 +51,21 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal private IAdapter SelectAdapater(object targetObject) { - if (targetObject is ExpandoObject) - { - return new ExpandoObjectAdapter(); - } - else if (targetObject is IDynamicMetaObjectProvider) - { - return new DynamicObjectAdapter(); - } - else if (targetObject is IDictionary) - { - return new DictionaryAdapter(); - } - else if (targetObject is IList) + 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(); diff --git a/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs b/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs index c81c4e4d6c..d669970407 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs @@ -37,18 +37,8 @@ namespace Microsoft.AspNetCore.JsonPatch // Create from list of operations public JsonPatchDocument(List> operations, IContractResolver contractResolver) { - if (operations == null) - { - throw new ArgumentNullException(nameof(operations)); - } - - if (contractResolver == null) - { - throw new ArgumentNullException(nameof(contractResolver)); - } - - Operations = operations; - ContractResolver = contractResolver; + Operations = operations ?? throw new ArgumentNullException(nameof(operations)); + ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver)); } /// diff --git a/src/Microsoft.AspNetCore.JsonPatch/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.JsonPatch/Properties/Resources.Designer.cs index cd99292061..896a3d2ad4 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.JsonPatch/Properties/Resources.Designer.cs @@ -206,6 +206,20 @@ namespace Microsoft.AspNetCore.JsonPatch internal static string FormatPatchNotSupportedForNonGenericLists(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("PatchNotSupportedForNonGenericLists"), p0); + /// + /// The provided path segment '{0}' cannot be converted to the target type. + /// + internal static string InvalidPathSegment + { + get => GetString("InvalidPathSegment"); + } + + /// + /// The provided path segment '{0}' cannot be converted to the target type. + /// + internal static string FormatInvalidPathSegment(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidPathSegment"), p0); + /// /// The target location specified by path segment '{0}' was not found. /// diff --git a/src/Microsoft.AspNetCore.JsonPatch/Resources.resx b/src/Microsoft.AspNetCore.JsonPatch/Resources.resx index 87ed5c0a17..0b67084233 100644 --- a/src/Microsoft.AspNetCore.JsonPatch/Resources.resx +++ b/src/Microsoft.AspNetCore.JsonPatch/Resources.resx @@ -144,6 +144,9 @@ Invalid JsonPatch operation '{0}'. + + The provided path segment '{0}' cannot be converted to the target type. + The provided string '{0}' is an invalid path. diff --git a/test/Microsoft.AspNetCore.JsonPatch.Test/DictionaryAdapterTest.cs b/test/Microsoft.AspNetCore.JsonPatch.Test/DictionaryAdapterTest.cs index 084aa57379..cfb99d61ce 100644 --- a/test/Microsoft.AspNetCore.JsonPatch.Test/DictionaryAdapterTest.cs +++ b/test/Microsoft.AspNetCore.JsonPatch.Test/DictionaryAdapterTest.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Moq; using Newtonsoft.Json.Serialization; using Xunit; @@ -15,33 +14,83 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal public void Add_KeyWhichAlreadyExists_ReplacesExistingValue() { // Arrange - var nameKey = "Name"; - var dictionary = new Dictionary(StringComparer.Ordinal); - dictionary[nameKey] = "Mike"; - var dictionaryAdapter = new DictionaryAdapter(); - var resolver = new Mock(MockBehavior.Strict); + var key = "Status"; + var dictionary = new Dictionary(StringComparer.Ordinal); + dictionary[key] = 404; + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); // Act - var addStatus = dictionaryAdapter.TryAdd(dictionary, nameKey, resolver.Object, "James", out var message); + var addStatus = dictionaryAdapter.TryAdd(dictionary, key, resolver, 200, out var message); // Assert Assert.True(addStatus); Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); Assert.Single(dictionary); - Assert.Equal("James", dictionary[nameKey]); + Assert.Equal(200, dictionary[key]); + } + + [Fact] + public void Add_IntKeyWhichAlreadyExists_ReplacesExistingValue() + { + // Arrange + var intKey = 1; + var dictionary = new Dictionary(); + dictionary[intKey] = "Mike"; + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var addStatus = dictionaryAdapter.TryAdd(dictionary, intKey.ToString(), resolver, "James", out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[intKey]); + } + + [Fact] + public void GetInvalidKey_ThrowsInvalidPathSegmentException() + { + // Arrange + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + var key = 1; + var dictionary = new Dictionary(); + + // Act + var addStatus = dictionaryAdapter.TryAdd(dictionary, key.ToString(), resolver, "James", out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[key]); + + // Act + var guidKey = new Guid(); + var getStatus = dictionaryAdapter.TryGet(dictionary, guidKey.ToString(), resolver, out var outValue, out message); + + // Assert + Assert.False(getStatus); + Assert.Equal( + string.Format("The provided path segment '{0}' cannot be converted to the target type.", guidKey.ToString()), + message); + Assert.Null(outValue); } [Fact] public void Get_UsingCaseSensitiveKey_FailureScenario() { // Arrange - var dictionaryAdapter = new DictionaryAdapter(); - var resolver = new Mock(MockBehavior.Strict); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); var nameKey = "Name"; var dictionary = new Dictionary(StringComparer.Ordinal); // Act - var addStatus = dictionaryAdapter.TryAdd(dictionary, nameKey, resolver.Object, "James", out var message); + var addStatus = dictionaryAdapter.TryAdd(dictionary, nameKey, resolver, "James", out var message); // Assert Assert.True(addStatus); @@ -50,11 +99,13 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal Assert.Equal("James", dictionary[nameKey]); // Act - addStatus = dictionaryAdapter.TryGet(dictionary, nameKey.ToUpper(), resolver.Object, out var outValue, out message); + var getStatus = dictionaryAdapter.TryGet(dictionary, nameKey.ToUpper(), resolver, out var outValue, out message); // Assert - Assert.True(addStatus); - Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.False(getStatus); + Assert.Equal( + string.Format("The target location specified by path segment '{0}' was not found.", nameKey.ToUpper()), + message); Assert.Null(outValue); } @@ -62,13 +113,13 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal public void Get_UsingCaseSensitiveKey_SuccessScenario() { // Arrange - var dictionaryAdapter = new DictionaryAdapter(); - var resolver = new Mock(MockBehavior.Strict); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); var nameKey = "Name"; var dictionary = new Dictionary(StringComparer.Ordinal); // Act - var addStatus = dictionaryAdapter.TryAdd(dictionary, nameKey, resolver.Object, "James", out var message); + var addStatus = dictionaryAdapter.TryAdd(dictionary, nameKey, resolver, "James", out var message); // Assert Assert.True(addStatus); @@ -77,7 +128,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal Assert.Equal("James", dictionary[nameKey]); // Act - addStatus = dictionaryAdapter.TryGet(dictionary, nameKey, resolver.Object, out var outValue, out message); + addStatus = dictionaryAdapter.TryGet(dictionary, nameKey, resolver, out var outValue, out message); // Assert Assert.True(addStatus); @@ -92,11 +143,11 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var nameKey = "Name"; var dictionary = new Dictionary(StringComparer.Ordinal); dictionary.Add(nameKey, "Mike"); - var dictionaryAdapter = new DictionaryAdapter(); - var resolver = new Mock(MockBehavior.Strict); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); // Act - var replaceStatus = dictionaryAdapter.TryReplace(dictionary, nameKey, resolver.Object, "James", out var message); + var replaceStatus = dictionaryAdapter.TryReplace(dictionary, nameKey, resolver, "James", out var message); // Assert Assert.True(replaceStatus); @@ -105,17 +156,58 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal Assert.Equal("James", dictionary[nameKey]); } + [Fact] + public void ReplacingExistingItem_WithGuidKey() + { + // Arrange + var guidKey = new Guid(); + var dictionary = new Dictionary(); + dictionary.Add(guidKey, "Mike"); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var replaceStatus = dictionaryAdapter.TryReplace(dictionary, guidKey.ToString(), resolver, "James", out var message); + + // Assert + Assert.True(replaceStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[guidKey]); + } + + [Fact] + public void ReplacingWithInvalidValue_ThrowsInvalidValueForPropertyException() + { + // Arrange + var guidKey = new Guid(); + var dictionary = new Dictionary(); + dictionary.Add(guidKey, 5); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var replaceStatus = dictionaryAdapter.TryReplace(dictionary, guidKey.ToString(), resolver, "test", out var message); + + // Assert + Assert.False(replaceStatus); + Assert.Equal( + string.Format("The value '{0}' is invalid for target location.", "test"), + message); + Assert.Equal(5, dictionary[guidKey]); + } + [Fact] public void Replace_NonExistingKey_Fails() { // Arrange var nameKey = "Name"; var dictionary = new Dictionary(StringComparer.Ordinal); - var dictionaryAdapter = new DictionaryAdapter(); - var resolver = new Mock(MockBehavior.Strict); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); // Act - var replaceStatus = dictionaryAdapter.TryReplace(dictionary, nameKey, resolver.Object, "Mike", out var message); + var replaceStatus = dictionaryAdapter.TryReplace(dictionary, nameKey, resolver, "Mike", out var message); // Assert Assert.False(replaceStatus); @@ -131,11 +223,11 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal // Arrange var nameKey = "Name"; var dictionary = new Dictionary(StringComparer.Ordinal); - var dictionaryAdapter = new DictionaryAdapter(); - var resolver = new Mock(MockBehavior.Strict); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); // Act - var removeStatus = dictionaryAdapter.TryRemove(dictionary, nameKey, resolver.Object, out var message); + var removeStatus = dictionaryAdapter.TryRemove(dictionary, nameKey, resolver, out var message); // Assert Assert.False(removeStatus); @@ -152,11 +244,30 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var nameKey = "Name"; var dictionary = new Dictionary(StringComparer.Ordinal); dictionary[nameKey] = "James"; - var dictionaryAdapter = new DictionaryAdapter(); - var resolver = new Mock(MockBehavior.Strict); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); // Act - var removeStatus = dictionaryAdapter.TryRemove(dictionary, nameKey, resolver.Object, out var message); + var removeStatus = dictionaryAdapter.TryRemove(dictionary, nameKey, resolver, out var message); + + //Assert + Assert.True(removeStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Empty(dictionary); + } + + [Fact] + public void Remove_RemovesFromDictionary_WithUriKey() + { + // Arrange + var uriKey = new Uri("http://www.test.com/name"); + var dictionary = new Dictionary(); + dictionary[uriKey] = "James"; + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var removeStatus = dictionaryAdapter.TryRemove(dictionary, uriKey.ToString(), resolver, out var message); //Assert Assert.True(removeStatus); diff --git a/test/Microsoft.AspNetCore.JsonPatch.Test/Dynamic/ReplaceOperationTests.cs b/test/Microsoft.AspNetCore.JsonPatch.Test/Dynamic/ReplaceOperationTests.cs index 88d1662e1d..5d1fa77d85 100644 --- a/test/Microsoft.AspNetCore.JsonPatch.Test/Dynamic/ReplaceOperationTests.cs +++ b/test/Microsoft.AspNetCore.JsonPatch.Test/Dynamic/ReplaceOperationTests.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Dynamic; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Xunit; namespace Microsoft.AspNetCore.JsonPatch.Internal @@ -26,11 +24,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("GuidValue", newGuid); - // serialize & deserialize - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserizalized = JsonConvert.DeserializeObject(serialized); - - deserizalized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(newGuid, doc.GuidValue); } @@ -46,11 +40,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("GuidValue", newGuid); - // serialize & deserialize - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserizalized = JsonConvert.DeserializeObject(serialized); - - deserizalized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(newGuid, doc.GuidValue); } @@ -71,11 +61,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("nestedobject/GuidValue", newGuid); - // serialize & deserialize - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserizalized = JsonConvert.DeserializeObject(serialized); - - deserizalized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(newGuid, doc.NestedObject.GuidValue); } @@ -99,11 +85,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("SimpleDTO", newDTO); - // serialize & deserialize - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserialized = JsonConvert.DeserializeObject(serialized); - - deserialized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(1, doc.SimpleDTO.DoubleValue); Assert.Equal(0, doc.SimpleDTO.IntegerValue); @@ -120,10 +102,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("IntegerList/0", 5); - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserialized = JsonConvert.DeserializeObject(serialized); - - deserialized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(new List() { 5, 2, 3 }, doc.IntegerList); } @@ -138,10 +117,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("IntegerList", new List() { 4, 5, 6 }); - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserialized = JsonConvert.DeserializeObject(serialized); - - deserialized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(new List() { 4, 5, 6 }, doc.IntegerList); } @@ -159,10 +135,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("SimpleDTOList/0/IntegerList/0", 4); - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserialized = JsonConvert.DeserializeObject(serialized); - - deserialized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(4, doc.SimpleDTOList[0].IntegerList[0]); } @@ -180,10 +153,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("SimpleDTOList/0/IntegerList/-", 4); - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserialized = JsonConvert.DeserializeObject(serialized); - - deserialized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(4, doc.SimpleDTOList[0].IntegerList[2]); } @@ -198,10 +168,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("IntegerList", new List() { 4, 5, 6 }); - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserialized = JsonConvert.DeserializeObject(serialized); - - deserialized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(new List() { 4, 5, 6 }, doc.IntegerList); } @@ -216,10 +183,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("IntegerList", new Collection() { 4, 5, 6 }); - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserialized = JsonConvert.DeserializeObject(serialized); - - deserialized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(new List() { 4, 5, 6 }, doc.IntegerList); } @@ -234,9 +198,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal var patchDoc = new JsonPatchDocument(); patchDoc.Replace("IntegerList/-", 5); - var serialized = JsonConvert.SerializeObject(patchDoc); - var deserialized = JsonConvert.DeserializeObject(serialized); - deserialized.ApplyTo(doc); + patchDoc.ApplyTo(doc); Assert.Equal(new List() { 1, 2, 5 }, doc.IntegerList); } diff --git a/test/Microsoft.AspNetCore.JsonPatch.Test/ObjectVisitorTest.cs b/test/Microsoft.AspNetCore.JsonPatch.Test/ObjectVisitorTest.cs index 5eddac8685..3384986c28 100644 --- a/test/Microsoft.AspNetCore.JsonPatch.Test/ObjectVisitorTest.cs +++ b/test/Microsoft.AspNetCore.JsonPatch.Test/ObjectVisitorTest.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. 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.Generic; using System.Dynamic; using Newtonsoft.Json.Serialization; @@ -87,7 +88,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal Assert.True(visitStatus); Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); Assert.Same(expectedTargetObject, targetObject); - Assert.IsType(adapter); + Assert.Equal(typeof(DictionaryAdapter), adapter.GetType()); } public static IEnumerable ReturnsExpandoAdapterData @@ -107,7 +108,8 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal public void Visit_ValidPathToExpandoObject_ReturnsExpandoAdapter(object targetObject, string path, object expectedTargetObject) { // Arrange - var visitor = new ObjectVisitor(new ParsedPath(path), new DefaultContractResolver()); + var contractResolver = new DefaultContractResolver(); + var visitor = new ObjectVisitor(new ParsedPath(path), contractResolver); // Act var visitStatus = visitor.TryVisit(ref targetObject, out var adapter, out var message); @@ -116,7 +118,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Internal Assert.True(visitStatus); Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); Assert.Same(expectedTargetObject, targetObject); - Assert.IsType(adapter); + Assert.Same(typeof(DictionaryAdapter), adapter.GetType()); } public static IEnumerable ReturnsPocoAdapterData