skip to Main Content

I am working with System.Text.Json and need to map some elements of the json result with the properties of my entity. Some of these elements have the underscore character to separate words while my properties are in case pascal. I know I can use the JsonPropertyName attribute to map the json element to my property, but I’m looking for a way to do this without using the JsonPropertyName attribute.

2

Answers


  1. You can use a custom naming policy with a serializer.

    var options = new JsonSerializerOptions
    {
        PropertyNamingPolicy = new UnderscoreNamingPolicy()
    };
    
    var jsonString = "{"first_name":"John","last_name":"Doe"}";
    
    var myObject = JsonSerializer.Deserialize<MyType>(jsonString, options);
    

    And define the naming conversation according to your underscore logic. (code provided below may not be precise to your use case as you didn’t specify the casing besides the underscores, but you should be able to build on top of it)

    public class UnderscoreNamingPolicy : JsonNamingPolicy
    {
        public override string ConvertName(string name)
        {
            var sb = new StringBuilder();
    
            bool capitalizeNext = false;
    
            foreach (var c in name)
            {
                if (c == '_')
                {
                    capitalizeNext = true;
                }
                else if (capitalizeNext)
                {
                    sb.Append(char.ToUpper(c));
                    capitalizeNext = false;
                }
                else
                {
                    sb.Append(c);
                }
            }
    
            return sb.ToString();
        }
    }
    
    Login or Signup to reply.
  2. If you want to bind all Pascal-cased .NET properties to snake_cased JSON properties, you can use the PropertyNamingPolicy shown in the answer by Levan Goderdzishvili.

    If you only want to remap selected properties, in .NET 7 and later you may use a typeInfo modifier to customize your type’s contract.

    First, define the following static method returning an Action<JsonTypeInfo>:

    public static partial class JsonExtensions
    {
        public static Action<JsonTypeInfo> RemapNames(Type type, IEnumerable<KeyValuePair<string, string>> names)
        {
            // Snapshot the incoming map
            var dictionary = names.ToDictionary(p => p.Key, p => p.Value).AsReadOnly();
            return typeInfo =>
            {
                if (typeInfo.Kind != JsonTypeInfoKind.Object || !type.IsAssignableFrom(typeInfo.Type))
                    return;
                foreach (var property in typeInfo.Properties)
                    if (property.GetMemberName() is {} memberName && dictionary.TryGetValue(memberName, out var jsonName))
                        property.Name = jsonName;
            };
        }
    
        public static string? GetMemberName(this JsonPropertyInfo property) => (property.AttributeProvider as MemberInfo)?.Name;
    }
    

    And now if you have a model that looks like e.g.:

    public class Model
    {
        public string PascalCaseProperty { get; set; } = "";
        public string SnakeCaseProperty { get; set; } = "";
    }
    

    You will be able to serialize SnakeCaseProperty as "snake_case_property" as by customizing a DefaultJsonTypeInfoResolver as follows:

    var dictionary = new Dictionary<string, string>()
    {
        [nameof(Model.SnakeCaseProperty)] = "snake_case_property",
    };
    var options = new JsonSerializerOptions
    {
        TypeInfoResolver = new DefaultJsonTypeInfoResolver
        {
            Modifiers = { JsonExtensions.RemapNames(typeof(Model), dictionary) },
        },
        // Add other options as required:
        //PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        //WriteIndented = true,
    };
    
    var json = JsonSerializer.Serialize(model, options);
    

    E.g. for Model the resulting JSON will look like:

    {
      "PascalCaseProperty": "PascalCase Value",
      "snake_case_property": "snake_case value"
    }
    

    Demo fiddle here.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search