From 954057ad179a8b209c87e4ac37cd1906e4c8bdde Mon Sep 17 00:00:00 2001 From: TylerBrinkley Date: Thu, 15 Sep 2016 09:33:44 -0500 Subject: [PATCH] Allow readonly properties to serialize as references if they have corresponding creator parameters --- .../PreserveReferencesHandlingTests.cs | 60 +++++++++++++++++++ .../JsonSerializerInternalWriter.cs | 8 +-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs index 3ea9915d..52134c26 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs @@ -659,6 +659,66 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(employees[0], employees[1].Manager); } + [JsonObject(IsReference = true)] + private class Condition + { + public int Value { get; } + + public Condition(int value) + { + Value = value; + } + } + + private class ClassWithConditions + { + public Condition Condition1 { get; } + + public Condition Condition2 { get; } + + public ClassWithConditions(Condition condition1, Condition condition2) + { + Condition1 = condition1; + Condition2 = condition2; + } + } + + [Test] + public void SerializeIsReferenceReadonlyProperty() + { + Condition condition = new Condition(1); + ClassWithConditions value = new ClassWithConditions(condition, condition); + + string json = JsonConvert.SerializeObject(value, Formatting.Indented); + StringAssert.AreEqual(@"{ + ""Condition1"": { + ""$id"": ""1"", + ""Value"": 1 + }, + ""Condition2"": { + ""$ref"": ""1"" + } +}", json); + } + + [Test] + public void DeserializeIsReferenceReadonlyProperty() + { + string json = @"{ + ""Condition1"": { + ""$id"": ""1"", + ""Value"": 1 + }, + ""Condition2"": { + ""$ref"": ""1"" + } +}"; + + ClassWithConditions value = JsonConvert.DeserializeObject(json); + Assert.AreEqual(value.Condition1.Value, 1); + Assert.AreEqual(value.Condition1, value.Condition2); + } + [Test] public void SerializeCircularReference() { diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 6f2679e9..bf34f8ee 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -577,8 +577,8 @@ namespace Newtonsoft.Json.Serialization writer.WriteStartObject(); bool isReference = ResolveIsReference(contract, member, collectionContract, containerProperty) ?? HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Objects); - // don't make readonly fields the referenced value because they can't be deserialized to - if (isReference && (member == null || member.Writable)) + // don't make readonly fields that aren't creator parameters the referenced value because they can't be deserialized to + if (isReference && (member == null || member.Writable || ((collectionContract as JsonObjectContract)?.CreatorParameters.Contains(member.PropertyName) ?? false))) { WriteReferenceIdProperty(writer, contract.UnderlyingType, value); } @@ -804,8 +804,8 @@ namespace Newtonsoft.Json.Serialization private bool WriteStartArray(JsonWriter writer, object values, JsonArrayContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) { bool isReference = ResolveIsReference(contract, member, containerContract, containerProperty) ?? HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Arrays); - // don't make readonly fields the referenced value because they can't be deserialized to - isReference = (isReference && (member == null || member.Writable)); + // don't make readonly fields that aren't creator parameters the referenced value because they can't be deserialized to + isReference = (isReference && (member == null || member.Writable || ((containerContract as JsonObjectContract)?.CreatorParameters.Contains(member.PropertyName) ?? false))); bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Arrays, contract, member, containerContract, containerProperty); bool writeMetadataObject = isReference || includeTypeDetails;