using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public bool Boolean { get; set; }
public List<Bar> List { get; set; }
[JsonIgnore] public int IgnoreMe { get => IgnoreMeAlso; set => IgnoreMeAlso = value; }
public int IgnoreMeAlso { private get; set; }
public string Value{ get; set; }
public class ItemListContainerConverter<TContainer, TItem> : JsonConverter<TContainer>
protected virtual string PropertyName => "property";
public override void WriteJson(JsonWriter writer, TContainer value, JsonSerializer serializer)
var contract = serializer.ContractResolver.ResolveContract(value.GetType()) as JsonObjectContract;
throw new JsonSerializationException("value is not a JSON object");
JsonProperty itemListProperty = null;
writer.WriteStartObject();
foreach (var property in contract.Properties.Where(p => ShouldSerialize(p, value)))
var propertyValue = property.ValueProvider.GetValue(value);
if (propertyValue == null && (serializer.NullValueHandling == NullValueHandling.Ignore || property.NullValueHandling == NullValueHandling.Ignore))
if (typeof(IEnumerable<TItem>).IsAssignableFrom(property.PropertyType))
itemListProperty = (itemListProperty == null ? property : throw new JsonSerializationException("Too many IEnumerable<Bar> properties"));
writer.WritePropertyName(property.PropertyName);
if (propertyValue == null)
else if (property.Converter != null && property.Converter.CanWrite)
property.Converter.WriteJson(writer, propertyValue, serializer);
serializer.Serialize(writer, propertyValue);
if (itemListProperty != null)
var itemList = itemListProperty.ValueProvider.GetValue(value) as IEnumerable<TItem>;
var itemContract = serializer.ContractResolver.ResolveContract(typeof(TItem));
var valueMethod = GetItemValueMethod(itemContract);
foreach (var item in itemList)
writer.WritePropertyName(PropertyName + (i++).ToString(CultureInfo.InvariantCulture));
serializer.Serialize(writer, valueMethod(item));
protected virtual Func<object, object> GetItemValueMethod(JsonContract itemContract)
if (!(itemContract is JsonObjectContract objectContract))
throw new JsonSerializationException("item contract is not a JsonObjectContract");
var property = objectContract.Properties.Single(p => ShouldSerialize(p));
return (o) => property.ValueProvider.GetValue(o);
protected virtual bool ShouldSerialize(JsonProperty property) => property.Readable && !property.Ignored;
protected virtual bool ShouldSerialize(JsonProperty property, object value) => ShouldSerialize(property) && (property.ShouldSerialize == null || property.ShouldSerialize(value));
public override TContainer ReadJson(JsonReader reader, Type objectType, TContainer existingValue, bool hasExistingValue, JsonSerializer serializer) =>
throw new NotImplementedException();
public static void Test()
new () { Value = "Foo1" },
new () { Value = "Bar2" },
new () { Value = "Foo3" },
var settings = new JsonSerializerSettings
Converters = { new ItemListContainerConverter<Foo, Bar>() }
var json = JsonConvert.SerializeObject(foo, Formatting.Indented, settings);
Assert.IsTrue(!json.Contains(nameof(Foo.IgnoreMe)));
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version);
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");