ContractResolver可以更改映射的属性

时间:2014-09-04 16:47:39

标签: c# json.net

我希望每个未指定JsonPropertyAttribute的属性都遵循自定义合同。但是,如果它被指定,那就是它应该是什么。

但是如果我有一个映射属性并使用自定义合约解析器,那么合约解析器可以改变映射属性。

例如,当指定JsonProperty("hello")时,我应该在JSON输出中看到hello。相反,我看到hello_。我提交了一个issue,但被告知要覆盖更高的方法,而不是哪一个。

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 + "_";
        }
    }
}

那么如何指定Json.NET在指定时始终使用(而不是更改)JsonProperty

真实世界的例子:我正在使用SnakeCamelCaseContractResolver。它在文本和数字之间加下划线。这模仿了Rails序列化的方式。但是,如果不遵循规范,例如address1,我需要能够阻止SnakeCamelCaseContractResolver更改属性。

2 个答案:

答案 0 :(得分:3)

@Andrew Whitaker在这里有正确的想法。我想补充一点,如果您希望让SnakeCamelCaseContractResolver示例正常工作,可以将实现更改为以下内容。请注意,它会覆盖CreateProperty而不是ResolvePropertyName

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;
    }
}

演示https://dotnetfiddle.net/Iqz9cA

答案 1 :(得分:1)

我无法确定JNK究竟是什么意思通过“更高级别的方法”,但是这里有一个超越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;
    }
}

基本上,调用基类“CreateProperty方法,然后检查属性是否具有属性。即使属性 具有属性,也无法保证该属性指定了新的属性名称,因此PropertyNameUnderlyingName之间进行了比较。如果没有属性或名称相同,则附加下划线。

我再次不确定这是否是正确的地方,但它很有效且很简单。