using System.Collections.Generic;
using System.Diagnostics;
""Title"": ""Startpage"",
""Head"": ""Latest news"",
PopulateObject(pages, initial);
Console.WriteLine("Initial:");
Console.WriteLine(pages);
PopulateObject(pages, update);
Console.WriteLine("Update:");
Console.WriteLine(pages);
public static void PopulateObject<T>(T target, string jsonSource) where T : class =>
PopulateObject(target, jsonSource, typeof(T));
public static void OverwriteProperty<T>(T target, JsonProperty updatedProperty) where T : class =>
OverwriteProperty(target, updatedProperty, typeof(T));
private static void PopulateObject(object target, string jsonSource, Type type)
var json = JsonDocument.Parse(jsonSource).RootElement;
foreach (var property in json.EnumerateObject())
OverwriteProperty(target, property, type);
private static void PopulateCollection(object target, string jsonSource, Type elementType)
var json = JsonDocument.Parse(jsonSource).RootElement;
var addMethod = target.GetType().GetMethod("Add", new[] { elementType });
var containsMethod = target.GetType().GetMethod("Contains", new[] { elementType });
Debug.Assert(addMethod is not null);
Debug.Assert(containsMethod is not null);
foreach (var property in json.EnumerateArray())
if (elementType.IsValueType || elementType == typeof(string))
element = JsonSerializer.Deserialize(jsonSource, elementType);
else if (IsCollection(elementType))
var nestedElementType = elementType.GenericTypeArguments[0];
element = Instantiate(elementType);
PopulateCollection(element, property.GetRawText(), nestedElementType);
element = Instantiate(elementType);
PopulateObject(element, property.GetRawText(), elementType);
var contains = containsMethod.Invoke(target, new[] { element });
addMethod.Invoke(target, new[] { element });
private static void OverwriteProperty(object target, JsonProperty updatedProperty, Type type)
var propertyInfo = type.GetProperty(updatedProperty.Name);
if (propertyInfo == null)
if (updatedProperty.Value.ValueKind == JsonValueKind.Null)
propertyInfo.SetValue(target, null);
var propertyType = propertyInfo.PropertyType;
if (propertyType.IsValueType || propertyType == typeof(string))
parsedValue = JsonSerializer.Deserialize(
updatedProperty.Value.GetRawText(),
else if (IsCollection(propertyType))
var elementType = propertyType.GenericTypeArguments[0];
parsedValue = propertyInfo.GetValue(target);
parsedValue ??= Instantiate(propertyType);
PopulateCollection(parsedValue, updatedProperty.Value.GetRawText(), elementType);
parsedValue = propertyInfo.GetValue(target);
parsedValue ??= Instantiate(propertyType);
updatedProperty.Value.GetRawText(),
propertyInfo.SetValue(target, parsedValue);
private static object Instantiate(Type type)
var ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Array.Empty<Type>());
throw new InvalidOperationException($"Type {type.Name} has no parameterless constructor.");
return ctor.Invoke(Array.Empty<object?>());
private static bool IsCollection(Type type) =>
type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>));
public string Title { get; set; }
public string Head { get; set; }
public List<Links> Links { get; set; }
public override string ToString() =>
$"Pages {{ Title = {Title}, Head = {Head}, Links = {string.Join(", ", Links)} }}";
public int Id { get; set; }
public string Text { get; set; }
public string Link { get; set; }
public override bool Equals(object obj) =>
obj is Links other && Id == other.Id;
public override int GetHashCode() => Id.GetHashCode();
public override string ToString() => $"Links {{ Id = {Id}, Text = {Text}, Link = {Link} }}";