Json.NET反序列化或序列化json字符串,并将属性映射到运行时定义的不同属性名称

本文关键字:属性 运行时 映射 定义 反序列化 NET 序列化 json 字符串 Json | 更新日期: 2023-09-27 17:53:39

我有以下JSON字符串:

{
    "values": {
        "details": {
            "property1": "94",
            "property2": "47",
            "property3": "32",
            "property4": 1
        },
        count: 4
    }
}     

我将把它映射到以下模型:

public class Details
{
    public string property1 { get; set; }
    public string property2 { get; set; }
    public string property3 { get; set; }
    public int property4 { get; set; }
}
public class Values
{
    public Details details { get; set; }
    public int count { get; set; }
}
public class RootObject
{
    public Values values { get; set; }
}

我希望能够在运行时反序列化JSON字符串时将这些属性名映射到不同的名称,如下所示:

JsonConvert.DeserializeObject<RootObject>(jsonString);

例如,在反序列化过程中,我希望将"property1"的名称反序列化为"differen_property_name1"或"differen_property_name2"或"differen_property_name3"。因为我在运行时选择了新名称(我将把"property1"的名称更改为新名称),所以我不能使用使用JsonPropertyAttribute的解决方案,如下所示:

。. NET NewtonSoft JSON反序列化映射到不同的属性名

上面问题的答案之一(Jack的答案)使用DefaultContractResolver的继承,但在这种情况下似乎不起作用。

之后,我需要序列化从反序列化中获得的对象,并将属性映射到运行时定义的不同属性名。我使用了Brian建议的相同方法来进行序列化:

我使用字典来映射我的新属性名:

var map = new Dictionary<Type, Dictionary<string, string>>
{
    { 
        typeof(Details), 
        new Dictionary<string, string>
        {
            {"property1", "myNewPropertyName1"},
            {"property2", "myNewPropertyName2"},
            {"property3", "myNewPropertyName3"},
            {"property4", "myNewPropertyName4"}
        }
    }
};    

然后我使用Brian的DynamicMappingResolver来序列化对象,像这样:

var settings = new JsonSerializerSettings
{
    ContractResolver = new DynamicMappingResolver(map)
};
var root = JsonConvert.SerializeObject(myObjectInstance, settings);            

Json.NET反序列化或序列化json字符串,并将属性映射到运行时定义的不同属性名称

您可以使用自定义的ContractResolver来完成此操作。基本上,这与将[JsonProperty]属性放在每个想要映射到不同JSON属性名的类成员上的想法是相同的,除了您通过解析器以编程方式完成它。在反序列化之前设置解析器时,可以将所需映射的字典传递给解析器。

以下是自定义解析器代码的样子:
class DynamicMappingResolver : DefaultContractResolver
{
    private Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap;
    public DynamicMappingResolver(Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap)
    {
        this.memberNameToJsonNameMap = memberNameToJsonNameMap;
    }
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        Dictionary<string, string> dict;
        string jsonName;
        if (memberNameToJsonNameMap.TryGetValue(member.DeclaringType, out dict) && 
            dict.TryGetValue(member.Name, out jsonName))
        {
            prop.PropertyName = jsonName;
        }
        return prop;
    }
}

要使用解析器,首先构造一个包含映射的Dictionary<Type, Dictionary<string, string>>。外部字典的键是要映射其属性的类类型;内部字典是类属性名到JSON属性名的映射。您只需要为那些名称与JSON不匹配的属性提供映射。

因此,例如,如果您的JSON看起来像这样(注意details对象内部属性的更改名称)…

{
    "values": {
        "details": {
            "foo": "94",
            "bar": "47",
            "baz": "32",
            "quux": 1
        },
        count: 4
    }
}

…如果你想把它映射到问题中的类,你可以这样创建字典:

var map = new Dictionary<Type, Dictionary<string, string>>
{
    { 
        typeof(Details), 
        new Dictionary<string, string>
        {
            {"property1", "foo"},
            {"property2", "bar"},
            {"property3", "baz"},
            {"property4", "quux"}
        }
    }
};

最后一步是用一个新的解析器实例设置序列化器设置,给它刚才构造的映射字典,然后将设置传递给JsonConvert.DeserializeObject()

var settings = new JsonSerializerSettings
{
    ContractResolver = new DynamicMappingResolver(map)
};
var root = JsonConvert.DeserializeObject<RootObject>(json, settings);

下面是一个演示:https://dotnetfiddle.net/ULkB0J

如果您不想使用自定义ContractResolver来完成此操作。使用[JsonProperty("")]查找属性名的不同变体,并返回另一个属性,如:

public class Details
{
    private string _property1;
    private string _property2;
    [JsonProperty("property1")]
    public string prop1 {get;set;}
    [JsonProperty("foo")]
    public string foo {get;set;}
    public string getProperty1 
    {
        get {_property1=prop1??foo;return _property1;}
        set{prop1=value;foo=value;}
    }
    [JsonProperty("property2")]
    public string prop2 {get;set;}
    [JsonProperty("bar")]
    public string bar {get;set;}
    public string getProperty2
    {
        get {_property2=prop2??bar;return _property2;}
        set {prop2=value;bar=value;}
    }
}

此处演示:https://dotnetfiddle.net/V17igc

为什么要一步完成呢?为什么不反序列化到标准对象,然后使用Automapper动态地映射它们呢?

类似:

Mapper.Initialize(c =>
{
    c.ReplaceMemberName("property1 ", "differen_property_name1");
});

我不相信JSON.net对你正在寻找的东西有任何支持。如果你不知道格式,你应该将JSON反序列化为JObject,如果你知道,也可以将其反序列化为通用格式(例如,如果JSON总是说property1,你可以使用通用对象来表示它)。

一旦你有了你的泛型对象,接下来你需要转换字段。任何不可更改的都可以直接完成,但对于其他任何内容,您都需要使用反射。

基本上它包括获取类型(typeof(Details)obj.GetType()),然后搜索您想要更新的属性。最后,您应该能够找到setter方法并调用它,提供泛型对象的原始值。