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

NewtonSoft Deserialize Same Object from multiple sources with different property names

想象一下 class

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 对象中,因为 属性 名称不匹配。

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

创建一些 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 演示 here.

我尝试使用 NamingStrategy,但似乎只能通过其他方式进行调整。我认为这个小技巧没有害处 - 仅通过设置,它们不会序列化。

使用自定义解决此问题 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 属性 class,以便在反序列化过程中使用您的自定义转换器...

[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);

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

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。至于骆驼案,有built-in CamelCasePropertyNamesContractResolver. Instead, for the snake case, you have to create a new DefaultContractResolver with the naming strategy set to SnakeCaseNamingStrategy.

请注意,在这种情况下,您可能会去掉 JsonPropertyAttributes,至少我还没有找到从 JsonSerializerSettings.

中覆盖它们的方法

Try the fiddle.