Search code examples
c#json.netserializationsystem.text.json

Serialize list with properties using .NET System.Text.Json


How can I let the JSON serializer ignore the enumeration of a class and serialize it as object and list its properties only?

Here is an example of my problem:

public class ObjectList<T> : IEnumerable<T> where T : class
{
    public ObjectList()
    {
        this.Items = new List<T>();
    }

    public string Group { get; set; }
    public int Count { get => this.Items.Count; set { } }
    public List<T> Items { get; set; }

    public IEnumerator<T> GetEnumerator() { return this.Items.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return this.Items.GetEnumerator(); }
}

public class ItemList : ObjectList<ListItem>
{
}

Assuming the following content:

var list = new ItemList() 
    {
        Group = "Group",
        Items = new List<ListItem>()
        {
            new ListItem() { Name = "Foo"},
            new ListItem() { Name = "Bar"},
        }
    };

Now when I serialize the content using this code:

var txt = System.Text.Json.JsonSerializer.Serialize(list, new JsonSerializerOptions()
                           { WriteIndented = true, });
Console.WriteLine(txt);

I get the following output

[ { "Name": "Foo" }, { "Name": "Bar" } ]

The properties of the list object (Group, Count, Items) are not listed. The result I expect would be as following:

{ 
    "Group": "Group", 
    "Count": 2, 
    "Items": [ { "Name": "Foo" }, { "Name": "Bar" } ]
}

Removing the enumeration from the main class is not an option. So I need to tell the serializer to handle the class ItemList as an object rather than enumeration.

Can you help me on that point?

Thank you in advance.


Solution

  • You should implement the custom JSON converter for the ItemList class.

    using System.Text.Json.Serialization;
    using System.Reflection;
    
    public class ItemListJsonConverter : JsonConverter<ItemList>
    {
        public override ItemList Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options) =>
               throw new JsonException("Not supported for deserialization");
    
        public override void Write(
            Utf8JsonWriter writer,
            ItemList value,
            JsonSerializerOptions options)
            {
                writer.WriteStartObject();
    
                PropertyInfo[] propInfos = typeof(ItemList).GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (var prop in propInfos)
                {
                    string propertyName = prop.Name.ToString();
                    writer.WritePropertyName
                        (options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName);
    
                    JsonSerializer.Serialize(writer, prop.GetValue(value), options);
                }
    
                writer.WriteEndObject();
            }
    }
    

    Next, register the ItemListJsonConverter instance into the Converters for the JsonSerializerOptions instance.

    var jsonSerializerOptions = new JsonSerializerOptions() { WriteIndented = true };
    jsonSerializerOptions.Converters.Add(new ItemListJsonConverter());
    var txt = System.Text.Json.JsonSerializer.Serialize(list, jsonSerializerOptions);