Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Contract and Property Parameters to JsonConverter #2697

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions Src/Newtonsoft.Json.Tests/Converters/JsonConverter2Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Utilities;
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
using Assert = Newtonsoft.Json.Tests.XUnitAssert;
#else
using NUnit.Framework;

#endif

namespace Newtonsoft.Json.Tests.Converters
{
public class JsonConverter2Tests
{
[Test]
public void SerializeCollectionWithDifferentSubTypesWithTypeNameHandlingAuto()
{
var contacts = new List<ContactModelBase>();
var c1 = new ContactWithUSAddressModel();
c1.FullName = "John Doe";
c1.StreetAddress = "123 Main St";
c1.City = "New York";
c1.State = "New York";
c1.ZipCode = "12345";
c1.Country = "United States";

var c2 = new ContactWithCanadianAddressModel();
c2.FullName = "Jane Doe";
c2.StreetAddress = "123 Main St";
c2.City = "Toronto";
c2.Province = "Ontario";
c2.PostalCode = "J4K3N5";
c2.Country = "Canada";

var c3 = new ContactModelBase();
c3.FullName = "FullName";
c3.StreetAddress = "StreetAddress";
c3.City = "CityName";
c3.Country = "CountryName";

contacts.Add(c1);
contacts.Add(c2);
contacts.Add(c3);

var jsonActual = JsonConvert.SerializeObject(contacts, new JsonSerializerSettings
{
Converters = new List<JsonConverter>
{
new ContactConverter()
},
TypeNameHandling = TypeNameHandling.Auto
});

var jsonExpected = JsonConvert.SerializeObject(contacts, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});

StringAssert.AreEqual(jsonExpected, jsonActual);
}

public class ContactModelBase
{
public string FullName { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string Country { get; set; }
}

public class ContactWithUSAddressModel : ContactModelBase
{
public string ZipCode { get; set; }
public string State { get; set; }
}

public class ContactWithCanadianAddressModel : ContactModelBase
{
public string PostalCode { get; set; }
public string Province { get; set; }
}

public class ContactConverter : JsonConverter2
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotSupportedException();
}

public override bool CanConvert(Type objectType)
{
return objectType.IsSubclassOf(typeof(ContactModelBase));
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer, JsonContract contract, JsonContainerContract collectionContract, JsonProperty containerProperty)
{
writer.WriteStartObject();
if (collectionContract.ItemContract.UnderlyingType != value.GetType())
{
writer.WritePropertyName("$type");
writer.WriteValue(ReflectionUtils.GetTypeName(value.GetType(), serializer.TypeNameAssemblyFormatHandling, serializer.SerializationBinder));
}

if (value is ContactWithUSAddressModel usContact)
{
writer.WritePropertyName(nameof(ContactWithUSAddressModel.ZipCode));
serializer.Serialize(writer, usContact.ZipCode);
writer.WritePropertyName(nameof(ContactWithUSAddressModel.State));
serializer.Serialize(writer, usContact.State);
writer.WritePropertyName(nameof(ContactModelBase.FullName));
serializer.Serialize(writer, usContact.FullName);
writer.WritePropertyName(nameof(ContactModelBase.StreetAddress));
serializer.Serialize(writer, usContact.StreetAddress);
writer.WritePropertyName(nameof(ContactModelBase.City));
serializer.Serialize(writer, usContact.City);
writer.WritePropertyName(nameof(ContactModelBase.Country));
serializer.Serialize(writer, usContact.Country);
}
else if (value is ContactWithCanadianAddressModel canContact)
{
writer.WritePropertyName(nameof(ContactWithCanadianAddressModel.PostalCode));
serializer.Serialize(writer, canContact.PostalCode);
writer.WritePropertyName(nameof(ContactWithCanadianAddressModel.Province));
serializer.Serialize(writer, canContact.Province);
writer.WritePropertyName(nameof(ContactModelBase.FullName));
serializer.Serialize(writer, canContact.FullName);
writer.WritePropertyName(nameof(ContactModelBase.StreetAddress));
serializer.Serialize(writer, canContact.StreetAddress);
writer.WritePropertyName(nameof(ContactModelBase.City));
serializer.Serialize(writer, canContact.City);
writer.WritePropertyName(nameof(ContactModelBase.Country));
serializer.Serialize(writer, canContact.Country);
}

writer.WriteEndObject();
}
}
}
}
18 changes: 18 additions & 0 deletions Src/Newtonsoft.Json/JsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json.Serialization;

namespace Newtonsoft.Json
{
Expand Down Expand Up @@ -146,4 +147,21 @@ public sealed override bool CanConvert(Type objectType)
return typeof(T).IsAssignableFrom(objectType);
}
}

/// <summary>
/// Converts an object to and from JSON.
/// </summary>
public abstract class JsonConverter2 : JsonConverter
{
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
/// <param name="contract">Contract of the current value</param>
/// <param name="collectionContract">Contract of the parent container</param>
/// <param name="containerProperty">JsonProperty of the container property field</param>
public abstract void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer, JsonContract contract, JsonContainerContract? collectionContract, JsonProperty? containerProperty);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,14 @@ private void SerializeConvertable(JsonWriter writer, JsonConverter converter, ob
TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Started serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null);
}

converter.WriteJson(writer, value, GetInternalSerializer());
if (converter is JsonConverter2 converter2)
{
converter2.WriteJson(writer, value, GetInternalSerializer(), contract, collectionContract, containerProperty);
}
else
{
converter.WriteJson(writer, value, GetInternalSerializer());
}

if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
{
Expand Down