using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
public class JsonMissingMemberException : JsonException
readonly string? innerMessage;
public JsonMissingMemberException() : this(null) { }
public JsonMissingMemberException(string? innerMessage) : base(innerMessage) => this.innerMessage = innerMessage;
public JsonMissingMemberException(string? innerMessage, Exception? innerException) : base(innerMessage, innerException) => this.innerMessage = innerMessage;
protected JsonMissingMemberException(SerializationInfo info, StreamingContext context) : base(info, context) => this.innerMessage = (string?)info.GetValue("innerMessage", typeof(string));
public override string Message =>
: String.Format("{0} Path: {1} | LineNumber: {2} | BytePositionInLine: {3}.", innerMessage, Path, LineNumber, BytePositionInLine);
public override void GetObjectData(SerializationInfo info, StreamingContext context)
base.GetObjectData(info, context);
info.AddValue("innerMessage", innerMessage);
public static class JsonExtensions
class UnknownPropertyDictionary<TModel> : IDictionary<string, JsonElement>
static JsonException CreateException(string key, JsonElement value) =>
new JsonMissingMemberException(String.Format("Unexpected property \"{0}\" encountered while deserializing type {1}.", key, typeof(TModel).FullName));
public void Add(string key, JsonElement value) => throw CreateException(key, value);
public bool ContainsKey(string key) => false;
public ICollection<string> Keys => Array.Empty<string>();
public bool Remove(string key) => false;
public bool TryGetValue(string key, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out JsonElement value) { value = default; return false; }
public ICollection<JsonElement> Values => Array.Empty<JsonElement>();
public JsonElement this[string key]
get => throw new KeyNotFoundException(key);
set => throw CreateException(key, value);
public void Add(KeyValuePair<string, JsonElement> item) => throw CreateException(item.Key, item.Value);
public void Clear() => throw new NotImplementedException();
public bool Contains(KeyValuePair<string, JsonElement> item) => false;
public void CopyTo(KeyValuePair<string, JsonElement>[] array, int arrayIndex) { }
public bool IsReadOnly => false;
public bool Remove(KeyValuePair<string, JsonElement> item) => false;
public IEnumerator<KeyValuePair<string, JsonElement>> GetEnumerator() => Enumerable.Empty<KeyValuePair<string, JsonElement>>().GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
public static DefaultJsonTypeInfoResolver AddMissingMemberHandlingError(this DefaultJsonTypeInfoResolver resolver)
resolver.Modifiers.Add(typeInfo =>
if (typeInfo.Kind != JsonTypeInfoKind.Object)
if (typeInfo.Properties.Any(p => p.IsExtensionData))
var dictionaryType = typeof(UnknownPropertyDictionary<>).MakeGenericType(typeInfo.Type);
JsonPropertyInfo property = typeInfo.CreateJsonPropertyInfo(dictionaryType, "<>ExtensionData");
property.IsExtensionData = true;
property.Get = (obj) => Activator.CreateInstance(dictionaryType);
property.Set = static (obj, val) => { };
typeInfo.Properties.Add(property);
public string? Value { get; set; }
public Inner Inner { get; set; } = new ();
public Dictionary<string, object>? ExtensionData { get; set; }
public static void Test()
foreach (var json in GetBadJson())
foreach (var json in GetGoodJson())
static void TestGoodJson(string json)
var options = new JsonSerializerOptions
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.AddMissingMemberHandlingError(),
Assert.DoesNotThrow(() => JsonSerializer.Deserialize<Model>(json, options));
static void TestBadJson(string json)
var options = new JsonSerializerOptions
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.AddMissingMemberHandlingError(),
var ex = Assert.Throws(Is.InstanceOf(typeof(JsonException)), () => JsonSerializer.Deserialize<Model>(json, options));
Console.WriteLine("Caught expected exception:\n{0}", ex);
static IEnumerable<string> GetBadJson() => new []
static IEnumerable<string> GetGoodJson() => new []
public static void Main()
Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");