Ответ 1
Один из способов добиться этого - создать пользовательский 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();
}
}
Чтобы использовать пользовательский конвертер с определенным классом, вы можете украсить этот класс атрибутом [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
Хотя это решение должно выполнять эту работу в большинстве случаев, есть еще более простое решение, если вы в порядке с идеей напрямую изменить исходный код Json.Net. Оказывается, вы можете сделать то же самое, добавив только одну строку кода в класс 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;
}
Конечно, изменение исходного кода также имеет свои проблемы, но я подумал, что стоит упомянуть.