我当前正在处理WebAPI项目中使用的自定义JSON转换器。要求是 - 我有一个具有一些属性的DTO对象。API可以由多个客户消费。根据客户的不同,我的DTO实体中的几个可能还具有一些其他数据,除了DTO模型中已经存在的属性。我需要创建一个自定义的JSON转换器来序列化并进行序列化此数据。
//DTO
class AbcDTO
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<AdditionalProperty> AdditionalData { get; set; }
}
//AdditionalProperty class
class AdditionalProperty
{
public string Name { get; set; }
public object Value { get; set; }
}
//Request JSON Body
{
"Prop1": "Val1",
"Prop2": "Val2",
"AdditionalProp3": "Val3",
"AdditionalProp4": "Val4"
}
//After Deserialization the object should be as below
AbcDTO dto = {
Prop1 = "Val1",
Prop2 = "Val2",
AdditionalData = [
{ Name = "AdditionalProp3", Value = "Val3" },
{ Name = "AdditionalProp4", Value = "Val4" }]
}
//After Serialization of the above dto object the JSON should convert back to the Request JSON Body format
我们不想使用newtonsoft.json提供的 JsonExtensionData
属性,因为我们需要将属性保留为 Dictionary<string, JToken>
-但我们不想将 JToken
传递到下面的层。
创建一个自定义JSON转换器 -
class CustomJsonConverter : JsonConverter
{
bool _canWrite = true;
bool _canRead = true;
public override bool CanConvert(Type objectType)
{
return typeof(IEntity).IsAssignableFrom(objectType);
}
public override bool CanWrite
{
get
{
return _canWrite;
}
}
public override bool CanRead
{
get
{
return _canRead;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
PropertyInfo[] availablePropertyNames = objectType.GetProperties();
List<AdditionalProperties> additionalData = new List<AdditionalProperties>();
IEntity obj;
_canRead = false;
obj = (IEntity)jObject.ToObject(objectType);
_canRead = true;
IEnumerable<JProperty> properties = jObject.Properties();
foreach (JProperty prop in properties)
{
if (availablePropertyNames.Count(x => x.Name.Equals(prop.Name)) == 0)
{
AdditionalProperties addProp = new AdditionalProperties
{
Name = prop.Name,
Value = prop.Value.ToObject<object>(),
};
additionalData.Add(addProp);
}
}
obj.AdditionalData = additionalData;
return obj;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
IEntity obj = (IEntity)value;
List<AdditionalProperties> additionalData = obj.AdditionalData;
JObject jObj;
_canWrite = false;
jObj = (JObject)JToken.FromObject(obj);
_canWrite = true;
jObj.Remove("AdditionalData");
foreach (AdditionalProperties data in additionalData)
{
jObj.Add(data.Name, JToken.FromObject(data.Value));
}
jObj.WriteTo(writer);
}
}
Webapi违规器每个实体创建1个JSON转换器。现在问题是_ canread,_ Canwrite不是线程安全。需要使用它们使用Newtonsoft提供的基本实现。如果我们不使用它们,则会再次调用内部定制转换器方法,从而导致无限递归。将它们与日志一起降低性能。有什么办法可以使用newtonsoft的基本实现来创建一个自定义转换器。JSON序列化/delelialization,而无需使用CanRead和CanWrite flag?
?我也可以拥有参考类型的儿童属性 - 说人们包含地址。我想捕获父母和儿童实体的其他数据。其他数据将不包含参考类型的数据。
可以使用线程静态变量或ThreadLocal<T>
成员禁用转换器,如JSON.NET所示,在使用JSONCONCONVERT或修改JSON的通用方法之前,请在返回给客户端。但是,我想提出一种更简单的方法来解决您的问题。
您写了,我们不想使用newtonsoft.json提供的jsonextensiondata属性,因为我们需要将属性保留为字典,我们不想将jtoken传递给以下层。没有必要对于扩展数据字典,具有JToken
类型的值。类型object
的值支持扩展数据字典,例如:
class AbcDTO
{
public AbcDTO() { this.AdditionalData = new Dictionary<string, object>(); }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
[JsonExtensionData]
public Dictionary<string, object> AdditionalData { get; private set; }
}
当扩展数据字典是Dictionary<string, object>
类型时,JSON.NET将对JSON原始值进行对等效的.NET原始值 - string
,bool
,long
等 - 而不是JValue
对象。只有在遇到一个值为JSON对象或数组的附加属性时,JToken
才会添加到字典中,在这种情况下,您可以使用如何使用JSON.NET的答案将json.net从嵌套/递归词典和列表中进行验证?将JToken
转换为常规.NET类型。(但是,您的问题指出,其他数据将不包含参考类型的数据,因此不需要。)
以这种方式使用 [JsonExtensionData]
完全避免了对转换器的需求,同时又根据您的要求进行了典型化启动,因此似乎比问题所示的原始设计要简单得多。
样品.NET小提琴,证明了扩展属性可以被划分为AbcDTO
,并断言它们都不是类型JToken
。