Json.NET 反序列化或序列化 json 字符串并将属性映射到运行时定义的不同 属性 名称
Json.NET deserialize or serialize json string and map properties to different property names defined at runtime
我有以下 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 deserialize map to a different property name
上述问题的答案之一(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 反序列化为 JObject
(例如,如果 JSON 总是说 property1
你可以使用通用对象来表示它)。
获得通用对象后,接下来您需要翻译字段。任何不可改变的都可以直接完成,但对于其他任何你需要使用反射。
基本上它涉及获取类型(typeof(Details)
或 obj.GetType()
),然后搜索要更新的 属性。最后,您应该能够找到 setter 方法并调用它以从通用对象中提供原始值。
您可以使用自定义 ContractResolver
来执行此操作。基本上,这与在每个要映射到不同 JSON 属性 名称的 class 成员上放置一个 [JsonProperty]
属性是相同的想法,只是您以编程方式通过解析器。在反序列化之前设置它时,您可以将所需映射的字典传递给解析器。
自定义解析器代码可能如下所示:
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>>
。外部字典的键是要映射其属性的 class 类型;内部字典是 class 属性 名称到 JSON 属性 名称的映射。您只需为名称尚未与 JSON 匹配的属性提供映射。
因此,例如,如果您的 JSON 看起来像这样(请注意 details
对象中属性的更改名称)...
{
"values": {
"details": {
"foo": "94",
"bar": "47",
"baz": "32",
"quux": 1
},
count: 4
}
}
...并且您想将其映射到问题中的 classes,您可以这样创建字典:
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);
为什么要一步到位?为什么不反序列化为标准对象,然后使用 Automapper?
动态映射它们
类似于:
Mapper.Initialize(c =>
{
c.ReplaceMemberName("property1 ", "differen_property_name1");
});
如果您不想使用自定义 ContractResolver
来执行此操作。使用 [JsonProperty("")]
查找 属性 名称和 return 与另一个 属性 的不同变体,如下所示:
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;}
}
}
我有以下 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 deserialize map to a different property name
上述问题的答案之一(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 反序列化为 JObject
(例如,如果 JSON 总是说 property1
你可以使用通用对象来表示它)。
获得通用对象后,接下来您需要翻译字段。任何不可改变的都可以直接完成,但对于其他任何你需要使用反射。
基本上它涉及获取类型(typeof(Details)
或 obj.GetType()
),然后搜索要更新的 属性。最后,您应该能够找到 setter 方法并调用它以从通用对象中提供原始值。
您可以使用自定义 ContractResolver
来执行此操作。基本上,这与在每个要映射到不同 JSON 属性 名称的 class 成员上放置一个 [JsonProperty]
属性是相同的想法,只是您以编程方式通过解析器。在反序列化之前设置它时,您可以将所需映射的字典传递给解析器。
自定义解析器代码可能如下所示:
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>>
。外部字典的键是要映射其属性的 class 类型;内部字典是 class 属性 名称到 JSON 属性 名称的映射。您只需为名称尚未与 JSON 匹配的属性提供映射。
因此,例如,如果您的 JSON 看起来像这样(请注意 details
对象中属性的更改名称)...
{
"values": {
"details": {
"foo": "94",
"bar": "47",
"baz": "32",
"quux": 1
},
count: 4
}
}
...并且您想将其映射到问题中的 classes,您可以这样创建字典:
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);
为什么要一步到位?为什么不反序列化为标准对象,然后使用 Automapper?
动态映射它们类似于:
Mapper.Initialize(c =>
{
c.ReplaceMemberName("property1 ", "differen_property_name1");
});
如果您不想使用自定义 ContractResolver
来执行此操作。使用 [JsonProperty("")]
查找 属性 名称和 return 与另一个 属性 的不同变体,如下所示:
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;}
}
}