NewtonSoft从具有不同属性名称的多个源反序列化同一对象



想象一下有以下类

public class Person
{
public int Id {get; set;}
[JsonProperty("first_name")]
public string FirstName {get; set;}
[JsonProperty("last_name")]
public string LastName {get; set;}
}

我有两个JSON源,我目前正在使用它们来JsonConvert.DeserializeObject<Person>(source)。这让我很为难。两个源必须具有相同的属性名称才能正常工作。为了进行此练习,让我们对source进行以下假设。

来源1:

{
"id" : 1,
"first_name": "Jon",
"last_name": "Doe"
} 

来源2:

{
"id" : 1,
"firstName": "Jon",
"lastName": "Doe"
} 

在这种情况下,第二个源不会被反序列化为Person对象,因为属性名称不匹配。

我不知道如何在不需要创建新对象的情况下将这两个源反序列化为同一个对象,这是我不想做的

要使用自定义JsonConverter解决此问题。。。

实现一个自定义JsonConverter,它定义了可以不同的字段名映射。。。

public class PersonConverter : JsonConverter
{
private Dictionary<string, string> propertyMappings { get; set; }
public PersonConverter()
{
this.propertyMappings = new Dictionary<string, string>
{
{"firstName","first_name"},
{"lastName","last_name"},
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object instance = Activator.CreateInstance(objectType);
var props = objectType.GetTypeInfo().DeclaredProperties.ToList();
JObject jo = JObject.Load(reader);
foreach (JProperty jp in jo.Properties())
{
if (!propertyMappings.TryGetValue(jp.Name, out var name))
name = jp.Name;
PropertyInfo prop = props.FirstOrDefault(pi =>
pi.CanWrite && pi.GetCustomAttribute<JsonPropertyAttribute>().PropertyName == name);
prop?.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
}
return instance;
}
public override bool CanConvert(Type objectType)
{
return objectType.GetTypeInfo().IsClass;
}
public override bool CanWrite => false;
}

在对象类上添加JsonConverter属性,以便在反序列化过程中使用自定义转换器。。。

[JsonConverter(typeof(PersonConverter))]
public class RootObject
{
[JsonProperty("first_name")]
public string FirstName{ get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
}

要使用自定义转换器。。。

string json_first_name = "{"id" : 1,"first_name": "Jon","last_name": "Doe"}";
string json_firstname = "{"id" : 1,"firstName": "Jon","lastName": "Doe"}";
var objFirst_Name = JsonConvert.DeserializeObject<RootObject>(json_first_name);
var objFirstName = JsonConvert.DeserializeObject<RootObject>(json_firstname);

创建一些仅限setter的代理属性(正如命名所暗示的那样,您可以忽略其他任何地方(:

public class Person
{
public int Id {get; set;}
[JsonProperty("firstName")]
public string FirstName {get; set;}
[JsonProperty("lastName")]
public string LastName {get; set;}

[JsonProperty("first_name")]
public string __Ignore1 { set { FirstName = value; } }
[JsonProperty("last_name")]
public string __Ignore2 { set { LastName = value; } }
}

Fiddle演示在这里。

我试过使用NamingStrategy,但似乎只能用另一种方式调整。我认为这个小黑客没有害处——只设置,它们不会序列化。

您不一定需要创建新的映射。可能有很多方法可以做到这一点,@TheSoftwareJedi提出了一个聪明的破解方法,这里有另一个解决方案:

public class Person
{
public int Id {get; set;}
//[JsonProperty("first_name")]
public string FirstName {get; set;}
//[JsonProperty("last_name")]
public string LastName {get; set;}
}
private static void PrintDeserializedObject(string obj, DefaultContractResolver resolver) 
{
var person = JsonConvert.DeserializeObject<Person>(obj, new JsonSerializerSettings
{
ContractResolver = resolver,

});
Console.WriteLine("{0}.{1}.{2}", person.Id, person.FirstName, person.LastName);
}
public static void Main()
{
var firstObj = @"{
""id"" : 1,
""first_name"": ""Jon"",
""last_name"": ""Doe""
}";

var secondObj = @"{
""id"" : 1,
""firstName"": ""Jon"",
""lastName"": ""Doe""
}"; 

DefaultContractResolver snakeResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
};
PrintDeserializedObject(firstObj, snakeResolver);
PrintDeserializedObject(secondObj, new CamelCasePropertyNamesContractResolver());
}

这个想法是使用ContractResolverNamingStrategy。至于camel案例,有一个内置的CamelCasePropertyNamesContractResolver。相反,对于snake情况,您必须创建一个新的DefaultContractResolver,并将命名策略设置为SnakeCaseNamingStrategy

注意,在这种情况下,您可能会去掉JsonPropertyAttributes,至少我还没有找到从JsonSerializerSettings覆盖它们的方法。

试试小提琴。

相关内容

  • 没有找到相关文章

最新更新