Fix for issue #29 (Honor JsonProperty when serializing a JsonPatchDocument) (#35)

* Fix for issue #29 (Honor JsonProperty when serializing a JsonPatchDocument)
This commit is contained in:
Kevin Dockx 2016-09-28 18:37:21 +02:00 committed by Ryan Nowak
parent e72d4b4876
commit c88aa0042a
5 changed files with 214 additions and 2 deletions

View File

@ -1,9 +1,12 @@
// 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 Newtonsoft.Json;
using System;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Microsoft.AspNetCore.JsonPatch.Helpers
{
@ -51,11 +54,13 @@ namespace Microsoft.AspNetCore.JsonPatch.Helpers
if (ContinueWithSubPath(memberExpression.Expression.NodeType, false))
{
var left = GetPath(memberExpression.Expression, false);
return left + "/" + memberExpression.Member.Name;
// Get property name, respecting JsonProperty attribute
return left + "/" + GetPropertyNameFromMemberExpression(memberExpression);
}
else
{
return memberExpression.Member.Name;
// Get property name, respecting JsonProperty attribute
return GetPropertyNameFromMemberExpression(memberExpression);
}
case ExpressionType.Parameter:
// Fits "x => x" (the whole document which is "" as JSON pointer)
@ -65,6 +70,23 @@ namespace Microsoft.AspNetCore.JsonPatch.Helpers
}
}
private static string GetPropertyNameFromMemberExpression(MemberExpression memberExpression)
{
// if there's a JsonProperty attribute, we must return the PropertyName
// from the attribute rather than the member name
var jsonPropertyAttribute =
memberExpression.Member.GetCustomAttribute(
typeof(JsonPropertyAttribute), true);
if (jsonPropertyAttribute == null)
{
return memberExpression.Member.Name;
}
// get value
var castedAttribute = (JsonPropertyAttribute)jsonPropertyAttribute;
return castedAttribute.PropertyName;
}
private static bool ContinueWithSubPath(ExpressionType expressionType, bool firstTime)
{
if (firstTime)

View File

@ -0,0 +1,148 @@
// 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 Newtonsoft.Json;
using System.Linq;
using Xunit;
namespace Microsoft.AspNetCore.JsonPatch
{
public class JsonPatchDocumentJsonPropertyAttributeTest
{
[Fact]
public void Add_WithExpression_RespectsJsonPropertyName_ForModelProperty()
{
var patchDoc = new JsonPatchDocument<JsonPropertyDTO>();
patchDoc.Add(p => p.Name, "John");
var serialized = JsonConvert.SerializeObject(patchDoc);
// serialized value should have "AnotherName" as path
// deserialize to a JsonPatchDocument<JsonPropertyWithAnotherNameDTO> to check
var deserialized =
JsonConvert.DeserializeObject<JsonPatchDocument<JsonPropertyWithAnotherNameDTO>>(serialized);
// get path
var pathToCheck = deserialized.Operations.First().path;
Assert.Equal(pathToCheck, "/anothername");
}
[Fact]
public void Add_WithExpression_RespectsJsonPropertyName_WhenApplyingToDifferentlyTypedClassWithPropertyMatchingJsonPropertyName()
{
var patchDocToSerialize = new JsonPatchDocument<JsonPropertyDTO>();
patchDocToSerialize.Add(p => p.Name, "John");
// the patchdoc will deserialize to "anothername". We should thus be able to apply
// it to a class that HAS that other property name.
var doc = new JsonPropertyWithAnotherNameDTO()
{
AnotherName = "InitialValue"
};
var serialized = JsonConvert.SerializeObject(patchDocToSerialize);
var deserialized =
JsonConvert.DeserializeObject<JsonPatchDocument<JsonPropertyWithAnotherNameDTO>>
(serialized);
deserialized.ApplyTo(doc);
Assert.Equal(doc.AnotherName, "John");
}
[Fact]
public void Add_WithExpression_RespectsJsonPropertyName_WhenApplyingToSameTypedClassWithMatchingJsonPropertyName()
{
var patchDocToSerialize = new JsonPatchDocument<JsonPropertyDTO>();
patchDocToSerialize.Add(p => p.Name, "John");
// the patchdoc will deserialize to "anothername". As JsonPropertyDTO has
// a JsonProperty signifying that "Name" should be deseriallized from "AnotherName",
// we should be able to apply the patchDoc.
var doc = new JsonPropertyDTO()
{
Name = "InitialValue"
};
var serialized = JsonConvert.SerializeObject(patchDocToSerialize);
var deserialized =
JsonConvert.DeserializeObject<JsonPatchDocument<JsonPropertyDTO>>
(serialized);
deserialized.ApplyTo(doc);
Assert.Equal(doc.Name, "John");
}
[Fact]
public void Add_OnApplyFromJson_RespectsJsonPropertyNameOnJsonDocument()
{
var doc = new JsonPropertyDTO()
{
Name = "InitialValue"
};
// serialization should serialize to "AnotherName"
var serialized = "[{\"value\":\"Kevin\",\"path\":\"/AnotherName\",\"op\":\"add\"}]";
var deserialized =
JsonConvert.DeserializeObject<JsonPatchDocument<JsonPropertyDTO>>(serialized);
deserialized.ApplyTo(doc);
Assert.Equal("Kevin", doc.Name);
}
[Fact]
public void Remove_OnApplyFromJson_RespectsJsonPropertyNameOnJsonDocument()
{
var doc = new JsonPropertyDTO()
{
Name = "InitialValue"
};
// serialization should serialize to "AnotherName"
var serialized = "[{\"path\":\"/AnotherName\",\"op\":\"remove\"}]";
var deserialized =
JsonConvert.DeserializeObject<JsonPatchDocument<JsonPropertyDTO>>(serialized);
deserialized.ApplyTo(doc);
Assert.Equal(null, doc.Name);
}
[Fact]
public void Add_OnApplyFromJson_RespectsInheritedJsonPropertyNameOnJsonDocument()
{
var doc = new JsonPropertyWithInheritanceDTO()
{
Name = "InitialName"
};
// serialization should serialize to "AnotherName"
var serialized = "[{\"value\":\"Kevin\",\"path\":\"/AnotherName\",\"op\":\"add\"}]";
var deserialized =
JsonConvert.DeserializeObject<JsonPatchDocument<JsonPropertyWithInheritanceDTO>>(serialized);
deserialized.ApplyTo(doc);
Assert.Equal("Kevin", doc.Name);
}
[Fact]
public void Add_WithExpression_RespectsJsonPropertyName_ForInheritedModelProperty()
{
var patchDoc = new JsonPatchDocument<JsonPropertyWithInheritanceDTO>();
patchDoc.Add(p => p.Name, "John");
var serialized = JsonConvert.SerializeObject(patchDoc);
// serialized value should have "AnotherName" as path
// deserialize to a JsonPatchDocument<JsonPropertyWithAnotherNameDTO> to check
var deserialized =
JsonConvert.DeserializeObject<JsonPatchDocument<JsonPropertyWithAnotherNameDTO>>(serialized);
// get path
var pathToCheck = deserialized.Operations.First().path;
Assert.Equal(pathToCheck, "/anothername");
}
}
}

View File

@ -0,0 +1,13 @@
// 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 Newtonsoft.Json;
namespace Microsoft.AspNetCore.JsonPatch
{
public class JsonPropertyDTO
{
[JsonProperty("AnotherName")]
public string Name { get; set; }
}
}

View File

@ -0,0 +1,10 @@
// 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.
namespace Microsoft.AspNetCore.JsonPatch
{
public class JsonPropertyWithAnotherNameDTO
{
public string AnotherName { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.JsonPatch
{
public class JsonPropertyWithInheritanceDTO : JsonPropertyWithInheritanceBaseDTO
{
public override string Name { get; set; }
}
public abstract class JsonPropertyWithInheritanceBaseDTO
{
[JsonProperty("AnotherName")]
public abstract string Name { get; set; }
}
}