扩展DefaultContractResolver以将ExpandoObject子属性转换为PascalCase

时间:2016-12-17 21:07:24

标签: c# asp.net-mvc serialization json.net expandoobject

我正在尝试编写一个自定义合约解析程序,它在Newtonsoft.Json.Serialization中扩展DefaultContractResolver,目标是将ExpandoObject中的所有属性转换为具有PascalCase属性名称。

我的合同:

public class Fruit 
{
    public int Id { get; set; }
    public ExpandoObject FruitProperties { get; set; }
}

我传递了以下数据:

{
  "Id": "1234",
  "FruitProperties" : {
      "colour": "red",
      "Taste": "sweet
   }

  }

}

我期待的结果如下:

{
   "Id": "1234",
   "FruitProperties" : {
      "Colour": "red",
      "Taste": "sweet"
    }
}

我已经尝试在DefaultContractResolver中重写ResolvePropertyName和CreateProperty方法而没有运气。所有这些都跳过了expando对象中的子属性。有没有人知道DefaultContractResolver中我需要覆盖什么方法来将Expando中的子属性名称转换为PascalCase?

1 个答案:

答案 0 :(得分:2)

ExpandoObject未通过反射进行序列化,因此修改CreateProperty将无效。相反,它被序列化为IDictionary<string, object>。因此,您可以利用NamingStrategy中的新Json.NET 9.0.1类型为PascalCase 字典键创建自定义命名策略,而不是其他任何内容。 NamingStrategy有一个属性NamingStrategy.ProcessDictionaryKeys,当设置为true时,会导致Json.NET映射字典键名:

public class PascalCaseDictionaryKeyNamingStrategy : DefaultNamingStrategy
{
    public PascalCaseDictionaryKeyNamingStrategy() : base() { this.ProcessDictionaryKeys = true; }

    public override string GetDictionaryKey(string key)
    {
        if (ProcessDictionaryKeys && !string.IsNullOrEmpty(key))
        {
            if (char.ToUpperInvariant(key[0]) != key[0])
            {
                var builder = new StringBuilder(key);
                builder[0] = char.ToUpperInvariant(key[0]);
                return builder.ToString();
            }
        }
        return key;
    }
}

然后在DefaultContractResolver.NamingStrategy(或任何custom subclass of DefaultContractResolver上设置,如果您愿意):

var resolver = new DefaultContractResolver { NamingStrategy = new PascalCaseDictionaryKeyNamingStrategy() };
var json = JsonConvert.SerializeObject(fruit, Formatting.Indented, new JsonSerializerSettings { ContractResolver = resolver });

Console.WriteLine(json);

哪个输出:

{
  "Id": 1234,
  "FruitProperties": {
    "Colour": "red",
    "Taste": "sweet"
  }
}