Альтернативное имя свойства при десериализации

В связи с этим вопросом:

Как изменить имена свойств при сериализации с помощью Json.net?

Конечно, здорово, но могу ли я пирог и съесть его?

То, что я ищу – это приятный взгляд, имеет альтернативное имя для свойства таким образом, что строка может содержать и.

Что-то вроде:

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

И то и другое

 { "FooBar": "yup" } 

а также

 { "foo_bar":"uhuh" } 

будет десериализоваться, как ожидалось.

Поскольку решение без атрибута будет работать или атрибут classа, например:

  [AllowCStylePropertyNameAlternatives] 

Один из способов добиться этого – создать пользовательский JsonConverter . Идея состоит в том, чтобы конвертер перечислил имена свойств JSON для интересующих нас объектов, разделил не-буквенно-цифровые символы от имен и затем попытался сопоставить их с фактическими свойствами объекта посредством отражения. Вот как это выглядит в коде:

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

Чтобы использовать пользовательский конвертер с определенным classом, вы можете украсить этот class атрибутом [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 list = JsonConvert.DeserializeObject>(json); foreach (MyClass mc in list) { Console.WriteLine(mc.MyProperty); Console.WriteLine(mc.MyOtherProperty); } } } 

Вывод:

 foo bar baz quux fizz bang 

Хотя это решение должно выполнять эту работу в большинстве случаев, есть еще более простое решение, если вы в порядке с идеей напрямую изменить исходный код Json.Net . Оказывается, вы можете сделать то же самое, добавив только одну строку кода в class Newtonsoft.Json.Serialization.JsonPropertyCollection . В этом classе существует метод GetClosestMatchProperty() который выглядит так:

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

В тот момент, когда этот метод вызывается десериализатором, JsonPropertyCollection содержит все свойства из десериализованного classа, а параметр propertyName содержит имя совпадающего имени свойства JSON. Как вы можете видеть, метод сначала пытается совместить точное имя, а затем пытается совпадение без учета регистра. Таким образом, мы уже имеем много-однозначное сопоставление между именами свойств JSON и classа.

Если вы измените этот метод, чтобы вычеркнуть все не-буквенно-цифровые символы из имени свойства до его соответствия, вы можете получить желаемое поведение без каких-либо специальных конвертеров или атрибутов. Вот модифицированный код:

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

Конечно, изменение исходного кода также имеет свои проблемы, но я подумал, что стоит упомянуть.

Другим способом достижения этого является перехват процесса сериализации / десериализации на раннем этапе, делая некоторые переопределения JsonReader и JsonWriter

 public class CustomJsonWriter : JsonTextWriter { private readonly Dictionary _backwardMappings; public CustomJsonWriter(TextWriter writer, Dictionary backwardMappings) : base(writer) { _backwardMappings = backwardMappings; } public override void WritePropertyName(string name) { base.WritePropertyName(_backwardMappings[name]); } } public class CustomJsonReader : JsonTextReader { private readonly Dictionary _forwardMappings; public CustomJsonReader(TextReader reader, Dictionary 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 { {"Property1", "Equivalent1"}, {"Property2", "Equivalent2"}, }; var builder = new StringBuilder(); JsonSerializer.Create().Serialize(new CustomJsonWriter(new StringWriter(builder), mappings), your_object); 

и десериализовать, делая

 var mappings = new Dictionary { {"Equivalent1", "Property1"}, {"Equivalent2", "Property2"}, }; var txtReader = new CustomJsonReader(new StringReader(jsonString), mappings); var your_object = JsonSerializer.Create().Deserialize(txtReader); 
  • C # WCF REST. Как вы используете сериализатор JSON.Net вместо стандартного DataContractSerializer?
  • JSON.NET Parser * кажется * будет двойной сериализацией моих объектов
  • Дезициализация свойств самореференции не работает
  • Заставить JSON.NET включать миллисекунды при сериализации DateTime (даже если компонент ms равен нулю)
  • Как можно десериализовать дочерний объект с динамическими (числовыми) именами клавиш?
  • JsonValueProviderFactory выбрасывает «запрос слишком большой»
  • Как обрабатывать как отдельный элемент, так и массив для одного и того же свойства с помощью JSON.net
  • Как написать Json-файл в C #?
  • Объединение двух массивов Json.NET путем конкатенации содержащихся элементов
  • как десериализовать JSON в IEnumerable с помощью Newtonsoft JSON.NET
  • Удалить дескриптор объекта json в динамический объект с помощью Json.net
  • Давайте будем гением компьютера.