using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
public static partial class JsonExtensions
public static Action<JsonTypeInfo> AddAlternateNamingPolicy(JsonNamingPolicy namingPolicy) => typeInfo =>
if (typeInfo.Kind != JsonTypeInfoKind.Object)
var allNames = typeInfo.Properties.Select(p => p.Name).ToHashSet();
for (int i = 0, n = typeInfo.Properties.Count; i < n; i++)
var property = typeInfo.Properties[i];
if (property.Set == null || property.IsExtensionData || !(property.GetMemberName() is {} name))
var jsonpropertynameattribute = property.AttributeProvider?.GetCustomAttributes(typeof(JsonPropertyNameAttribute), true).Cast<JsonPropertyNameAttribute>().SingleOrDefault();
if (jsonpropertynameattribute != null && jsonpropertynameattribute.Name != null)
var alternateName = namingPolicy.ConvertName(name);
if (alternateName == property.Name || allNames.Contains(alternateName))
var alternateProperty = typeInfo.CreateJsonPropertyInfo(property.PropertyType, alternateName);
alternateProperty.Set = property.Set;
alternateProperty.ShouldSerialize = static (_, _) => false;
alternateProperty.AttributeProvider = property.AttributeProvider;
alternateProperty.CustomConverter = property.CustomConverter;
alternateProperty.IsRequired = false;
alternateProperty.NumberHandling = property.NumberHandling;
typeInfo.Properties.Add(alternateProperty);
allNames.Add(alternateName);
public static string? GetMemberName(this JsonPropertyInfo property) => (property.AttributeProvider as MemberInfo)?.Name;
public class SnakeCaseNamingPolicy : JsonNamingPolicy
public static SnakeCaseNamingPolicy Instance { get; } = new SnakeCaseNamingPolicy();
public override string ConvertName(string name) =>
public static class StringUtils
public static string ToSnakeCase(this string str) =>
string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
public string SomeName { get; set; }
public double SomeValue { get; set; }
public bool HasValue { get; set; }
[JsonIgnore] public string IgnoreMe { set => throw new Exception(); get => throw new Exception(); }
public static void Test()
foreach (var json in GetJson())
var options = new JsonSerializerOptions
PropertyNameCaseInsensitive = true,
TypeInfoResolver = new DefaultJsonTypeInfoResolver
Modifiers = { JsonExtensions.AddAlternateNamingPolicy(SnakeCaseNamingPolicy.Instance) },
NumberHandling = JsonNumberHandling.AllowReadingFromString,
Converters = { new BoolConverter() },
var demo = JsonSerializer.Deserialize<Demo>(json, options);
Assert.That(demo?.SomeName == "X" && demo.SomeValue == 5.0 && demo.HasValue == true);
var newJson = JsonSerializer.Serialize(demo,options);
Console.WriteLine("Re-serialized {0}: {1}", demo, newJson);
static IEnumerable<string> GetJson() => new []
{ "SomeName": "X", "SomeValue": "5.0", "HasValue": "true", "IgnoreMe" : "crash" }
{ "someName": "X", "someValue": "5.0", "hasValue": "true", "ignoreMe" : "crash" }
{ "some_name": "X", "some_value": "5.0", "has_value": "true", "ignore_me" : "crash" }
{ "SomeName": "X", "someValue": "5.0", "has_value": "true", "ignore_me" : "crash" }
public class BoolConverter : JsonConverter<bool>
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) =>
writer.WriteBooleanValue(value);
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.String => bool.TryParse(reader.GetString(), out var b) ? b : throw new JsonException(),
JsonTokenType.Number => reader.TryGetInt64(out long l) ? Convert.ToBoolean(l) : reader.TryGetDouble(out double d) ? Convert.ToBoolean(d) : false,
_ => throw new JsonException(),
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: ");