如何在 json 序列化时使用 DefaultContractResolver 用字符串值覆盖复杂类型 属性
How to override complex type property with string value using DefaultContractResolver while json serialization
我有一个 class 有多个属性,其中一些属性是复杂类型,那些它自己有自己的多个属性,我正在寻找一种方法来只获得一个 属性 来序列化来自复杂类型。
比如我有这3个class
class Organization
{
public Int32 ID {get; set;}
public string Name {get; set;}
public Geography Location {get; set;}
}
class Geography
{
public Int32 GeoID {get; set;}
public string Country {get; set;}
public string City {get; set;}
public string State {get; set;}
}
这里我想序列化 Organization class 对象,它应该只从 属性 'Location' 中获取 'Country' 以及其他属性, json我期望使用 NewtonSoft 库中的 JsonConvert.SerializeObject
方法输出如下字符串。
{
"ID":1,
"Name":"Sales",
"Location":"India"
}
我不想使用匿名类型作为可序列化对象来执行此操作,因为我需要为每个使用此 属性 类型进行消毒的对象保持通用的实现。
我正在尝试使用 DefaultContractResolver
,因为我已经实现了从序列化中排除选定的 属性,在相同的实现中,我尝试通过覆盖 CreateProperty
方法,但它也无法转换我无法获取要使用新创建的 属性 设置的值,如下所示。这只是一个尝试,我不知道这是否是用于所需功能的正确方法!请建议。
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
JsonProperty newproperty = property;
if (property.PropertyType == typeof(Geography))
{
newproperty = new JsonProperty()
{
PropertyName = property.PropertyName,
PropertyType = typeof(string)
//How to get the value from parent property to set with newproperty?
};
}
return newproperty;
}
我为上述功能构建了这个解决方案,一切都按照我的预期工作,但不确定这样做是否正确。
合约解析器
public class DynamicContractResolver : DefaultContractResolver
{
private readonly Dictionary<Type, HashSet<string>> _serializableProperties;
public DynamicContractResolver()
{
_serializableProperties = new Dictionary<Type, HashSet<string>>();
public void SerializableProperty(Type type, params string[] jsonPropertyNames)
{
if (!_serializableProperties.ContainsKey(type))
_serializableProperties[type] = new HashSet<string>();
foreach (var prop in jsonPropertyNames)
_serializableProperties[type].Add(prop);
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
if (_serializableProperties.Any())
{
if (_serializableProperties.Any(o => o.Key == type))
{
properties = properties.Where(p => IsSerializable(type, p.PropertyName)).ToList();
}
}
return properties;
}
private bool IsSerializable(Type type, string jsonPropertyName)
{
if (!_serializableProperties.ContainsKey(type))
return false;
return _serializableProperties[type].Contains(jsonPropertyName);
}
}
自定义转换器
public class GeographyJsonConverter : JsonConverter
{
private readonly Type[] _types;
public GeographyJsonConverter ()
{
_types = new Type[] { typeof(Geography) };
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value != null && typeof(Geography) == value.GetType())
{
Geography obj = (Geography)value;
JToken t = JToken.FromObject(obj.Country);
t.WriteTo(writer);
}
else
{
JToken t = JToken.FromObject(value);
t.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return _types.Any(t => t == objectType);
}
}
以上自定义设置的使用
private void btnTestCode_Click(object sender, EventArgs e)
{
Organization org = new Organization
{
Name = "Finance",
ID= 1,
Location = new Geography(){ GeoID = 200, Country = "India", City = "Pune", State ="MH" }
};
var changedProps = "Name,Operation";
var dynamicContractResolver = new DynamicContractResolver();
dynamicContractResolver.SerializableProperty(typeof(Organization), changedProps.Split(',').ToArray());
var selected = new JsonSerializerSettings
{
ContractResolver = dynamicContractResolver,
Converters = { new GeographyJsonConverter() }
};
string json = JsonConvert.SerializeObject(org, selected);
Console.WriteLine(json);
}
我有一个 class 有多个属性,其中一些属性是复杂类型,那些它自己有自己的多个属性,我正在寻找一种方法来只获得一个 属性 来序列化来自复杂类型。
比如我有这3个class
class Organization
{
public Int32 ID {get; set;}
public string Name {get; set;}
public Geography Location {get; set;}
}
class Geography
{
public Int32 GeoID {get; set;}
public string Country {get; set;}
public string City {get; set;}
public string State {get; set;}
}
这里我想序列化 Organization class 对象,它应该只从 属性 'Location' 中获取 'Country' 以及其他属性, json我期望使用 NewtonSoft 库中的 JsonConvert.SerializeObject
方法输出如下字符串。
{
"ID":1,
"Name":"Sales",
"Location":"India"
}
我不想使用匿名类型作为可序列化对象来执行此操作,因为我需要为每个使用此 属性 类型进行消毒的对象保持通用的实现。
我正在尝试使用 DefaultContractResolver
,因为我已经实现了从序列化中排除选定的 属性,在相同的实现中,我尝试通过覆盖 CreateProperty
方法,但它也无法转换我无法获取要使用新创建的 属性 设置的值,如下所示。这只是一个尝试,我不知道这是否是用于所需功能的正确方法!请建议。
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
JsonProperty newproperty = property;
if (property.PropertyType == typeof(Geography))
{
newproperty = new JsonProperty()
{
PropertyName = property.PropertyName,
PropertyType = typeof(string)
//How to get the value from parent property to set with newproperty?
};
}
return newproperty;
}
我为上述功能构建了这个解决方案,一切都按照我的预期工作,但不确定这样做是否正确。
合约解析器
public class DynamicContractResolver : DefaultContractResolver
{
private readonly Dictionary<Type, HashSet<string>> _serializableProperties;
public DynamicContractResolver()
{
_serializableProperties = new Dictionary<Type, HashSet<string>>();
public void SerializableProperty(Type type, params string[] jsonPropertyNames)
{
if (!_serializableProperties.ContainsKey(type))
_serializableProperties[type] = new HashSet<string>();
foreach (var prop in jsonPropertyNames)
_serializableProperties[type].Add(prop);
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
if (_serializableProperties.Any())
{
if (_serializableProperties.Any(o => o.Key == type))
{
properties = properties.Where(p => IsSerializable(type, p.PropertyName)).ToList();
}
}
return properties;
}
private bool IsSerializable(Type type, string jsonPropertyName)
{
if (!_serializableProperties.ContainsKey(type))
return false;
return _serializableProperties[type].Contains(jsonPropertyName);
}
}
自定义转换器
public class GeographyJsonConverter : JsonConverter
{
private readonly Type[] _types;
public GeographyJsonConverter ()
{
_types = new Type[] { typeof(Geography) };
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value != null && typeof(Geography) == value.GetType())
{
Geography obj = (Geography)value;
JToken t = JToken.FromObject(obj.Country);
t.WriteTo(writer);
}
else
{
JToken t = JToken.FromObject(value);
t.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return _types.Any(t => t == objectType);
}
}
以上自定义设置的使用
private void btnTestCode_Click(object sender, EventArgs e)
{
Organization org = new Organization
{
Name = "Finance",
ID= 1,
Location = new Geography(){ GeoID = 200, Country = "India", City = "Pune", State ="MH" }
};
var changedProps = "Name,Operation";
var dynamicContractResolver = new DynamicContractResolver();
dynamicContractResolver.SerializableProperty(typeof(Organization), changedProps.Split(',').ToArray());
var selected = new JsonSerializerSettings
{
ContractResolver = dynamicContractResolver,
Converters = { new GeographyJsonConverter() }
};
string json = JsonConvert.SerializeObject(org, selected);
Console.WriteLine(json);
}