备用属性名称,而反序列化

在提到这个问题时:

如何在使用Json.net序列化时更改属性名称?

当然,太好了,但是我可以吃点蛋糕吗?

我正在寻找的是一个令人愉快的方式有一个属性的替代名称,这样的string可能包含任一方式。

就像是:

[BetterJsonProperty(PropertyName = "foo_bar")] public string FooBar { get; set; } 

 { "FooBar": "yup" } 

 { "foo_bar":"uhuh" } 

会按预期反序列化。

因为没有属性的解决scheme将工作或类的属性,如:

  [AllowCStylePropertyNameAlternatives] 

一种方法是创build一个自定义的JsonConverter 。 我们的想法是让转换器枚举我们感兴趣的对象的JSON属性名称,从名称中去除非字母数字字符,然后尝试通过reflection将它们与实际的对象属性进行匹配。 以下是代码中的外观:

 public class LaxPropertyNameMatchingConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType.IsClass; } public override bool CanWrite { get { return false; } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { object instance = objectType.GetConstructor(Type.EmptyTypes).Invoke(null); PropertyInfo[] props = objectType.GetProperties(); JObject jo = JObject.Load(reader); foreach (JProperty jp in jo.Properties()) { string name = Regex.Replace(jp.Name, "[^A-Za-z0-9]+", ""); PropertyInfo prop = props.FirstOrDefault(pi => pi.CanWrite && string.Equals(pi.Name, name, StringComparison.OrdinalIgnoreCase)); if (prop != null) prop.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer)); } return instance; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

要使用特定类的自定义转换器,可以使用如下的[JsonConverter]属性来修饰该类:

 [JsonConverter(typeof(LaxPropertyNameMatchingConverter))] public class MyClass { public string MyProperty { get; set; } public string MyOtherProperty { get; set; } } 

下面是转换器的一个简单演示:

 class Program { static void Main(string[] args) { string json = @" [ { ""my property"" : ""foo"", ""my-other-property"" : ""bar"", }, { ""(myProperty)"" : ""baz"", ""myOtherProperty"" : ""quux"" }, { ""MyProperty"" : ""fizz"", ""MY_OTHER_PROPERTY"" : ""bang"" } ]"; List<MyClass> list = JsonConvert.DeserializeObject<List<MyClass>>(json); foreach (MyClass mc in list) { Console.WriteLine(mc.MyProperty); Console.WriteLine(mc.MyOtherProperty); } } } 

输出:

 foo bar baz quux fizz bang 

虽然这个解决scheme在大多数情况下应该可以完成这个工作,但是如果您能够直接更改Json.Net源代码的话 ,还有更简单的解决scheme。 事实certificate,您可以通过向Newtonsoft.Json.Serialization.JsonPropertyCollection类添加一行代码来完成相同的操作。 在这个类中,有一个名为GetClosestMatchProperty()的方法,如下所示:

 public JsonProperty GetClosestMatchProperty(string propertyName) { JsonProperty property = GetProperty(propertyName, StringComparison.Ordinal); if (property == null) property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase); return property; } 

在反序列化器调用此方法的位置, JsonPropertyCollection包含被反序列化的类的所有属性, propertyName参数包含被匹配的JSON属性名称的名称。 如您所见,该方法首先尝试一个确切的名称匹配,然后尝试不区分大小写的匹配。 所以我们已经在JSON和类属性名称之间做了多对一的映射。

如果修改此方法以在匹配属性名称之前去除所有非字母数字字符,则可以获得所需的行为,而不需要任何特殊的转换器或属性。 这是修改的代码:

 public JsonProperty GetClosestMatchProperty(string propertyName) { propertyName = Regex.Replace(propertyName, "[^A-Za-z0-9]+", ""); JsonProperty property = GetProperty(propertyName, StringComparison.Ordinal); if (property == null) property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase); return property; } 

当然,修改源代码也有问题,但我觉得值得一提。

实现这个的另一种方法是尽早拦截序列化/反序列化过程,做一些重写JsonReaderJsonWriter

 public class CustomJsonWriter : JsonTextWriter { private readonly Dictionary<string, string> _backwardMappings; public CustomJsonWriter(TextWriter writer, Dictionary<string, string> backwardMappings) : base(writer) { _backwardMappings = backwardMappings; } public override void WritePropertyName(string name) { base.WritePropertyName(_backwardMappings[name]); } } public class CustomJsonReader : JsonTextReader { private readonly Dictionary<string, string> _forwardMappings; public CustomJsonReader(TextReader reader, Dictionary<string, string> forwardMappings ) : base(reader) { _forwardMappings = forwardMappings; } public override object Value { get { if (TokenType != JsonToken.PropertyName) return base.Value; return _forwardMappings[base.Value.ToString()]; } } } 

这样做后,你可以通过做序列化

 var mappings = new Dictionary<string, string> { {"Property1", "Equivalent1"}, {"Property2", "Equivalent2"}, }; var builder = new StringBuilder(); JsonSerializer.Create().Serialize(new CustomJsonWriter(new StringWriter(builder), mappings), your_object); 

并通过做反序列化

 var mappings = new Dictionary<string, string> { {"Equivalent1", "Property1"}, {"Equivalent2", "Property2"}, }; var txtReader = new CustomJsonReader(new StringReader(jsonString), mappings); var your_object = JsonSerializer.Create().Deserialize<Your_Type>(txtReader);