2014-09-04 88 views
3

我希望每個沒有指定JsonPropertyAttribute的屬性遵循自定義合同。但是,如果它被指定,那就是它應該是。ContractResolver可以更改映射的屬性

但是,如果我有一個映射屬性並使用自定義合約解析器,那麼合約解析器可以更改映射屬性。

例如,當指定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更改屬性。

回答

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

我不能肯定什麼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之間進行了比較。如果沒有屬性或名稱相同,則追加下劃線。

再次我不確定這是否是正確的地方做到這一點,但它的工作原理並且很簡單。