ContractResolver can change the displayed property

I want every property that is not specified JsonPropertyAttribute

to match a custom contract. But if it is indicated, then this is exactly what it should be.

But if I have a property mapped and I am using a custom contract resolver, then the contract resolver can change the displayed property.

For example, when specified JsonProperty("hello")

, I should see hello

JSON in the output. Instead, I see hello_

. I filed an issue , but it was instead asked to override a higher method, but not the one.

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace DeserializeTest
{
    class Program
    {
        static void Main()
        {
            var json = new JObject(new JProperty("hello", "world"));

            var settings = new JsonSerializerSettings { ContractResolver = new CustomContractResolver() };
           var a = JsonConvert.DeserializeObject<Test>(json.ToString(), settings);
        }
    }

    public class Test
    {
        [JsonProperty("hello")]
        public string FooBar { get; set; }
    }

    public class CustomContractResolver : DefaultContractResolver
    {
        protected override string ResolvePropertyName(string propertyName)
        {
            return propertyName + "_";
        }
    }
}

      

So how can I get Json.NET to always use (and not modify) JsonProperty

when specified?

Real world example: I am using SnakeCamelCaseContractResolver . It places an underscore between text and numbers. This mimics the Rails serialization process. But in cases where the norm is not respected, for example address1

, I need to be able to prevent a property from changing SnakeCamelCaseContractResolver

.

+3


source to share


2 answers


@ Andy Whitaker has the right idea here. I'll add that if you wanted your example to SnakeCamelCaseContractResolver

work, you could change the implementation as follows. Note that ResolvePropertyName

it overrides instead CreateProperty

.

class SnakeCaseContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);

        // if the property does not have a JsonPropertyAttribute applied, use Snake Case
        if (!member.CustomAttributes.Any(att => att.AttributeType == typeof(JsonPropertyAttribute)))
        {
            prop.PropertyName = GetSnakeCase(prop.PropertyName);
        }

        return prop;
    }

    private string GetSnakeCase(string input)
    {
        if (string.IsNullOrEmpty(input))
            return input;

        var buffer = "";

        for (var i = 0; i < input.Length; i++)
        {
            var isLast = (i == input.Length - 1);
            var isSecondFromLast = (i == input.Length - 2);

            var curr = input[i];
            var next = !isLast ? input[i + 1] : '\0';
            var afterNext = !isSecondFromLast && !isLast ? input[i + 2] : '\0';

            buffer += char.ToLower(curr);

            if (!char.IsDigit(curr) && char.IsUpper(next))
            {
                if (char.IsUpper(curr))
                {
                    if (!isLast && !isSecondFromLast && !char.IsUpper(afterNext))
                        buffer += "_";
                }
                else
                    buffer += "_";
            }

            if (!char.IsDigit(curr) && char.IsDigit(next))
                buffer += "_";
            if (char.IsDigit(curr) && !char.IsDigit(next) && !isLast)
                buffer += "_";
        }

        return buffer;
    }
}

      



Demo : https://dotnetfiddle.net/Iqz9cA

+3


source


I can't be sure if JNK stands for "higher level method" exactly, but here hitting it overrides CreateProperty

:

public class CustomContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(
        MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (!property.HasMemberAttribute || 
            property.PropertyName == property.UnderlyingName)
        {
            property.PropertyName += "_";
        }

        return property;
    }
}

      



Basically, call the base class method CreateProperty

, then check if the property has an attribute. Even if the property has an attribute, there is no guarantee that the attribute specified a new property name, hence the comparison between PropertyName

and UnderlyingName

. If there is no attribute or the names are the same, add an underscore.

Again I'm not sure if this is the right place to do this, but it works and is simple enough.

+1


source







All Articles