我有一個PersonDto
包含型別屬性實體的類AddressDto
。我正在建立一個ContractResolver
名為例如的自定義。ShouldSerializeContractResolver
使用Newtonsoft.Json
編組.NET lib,它將僅包含特定屬性到序列化中,這些屬性標記有我的自定義屬性,例如。[ShouldSerialize]
當CreateProperty
決議器的方法進入PersonDto
ie的復雜/自定義型別時,就會出現問題。它進入AddressDto
并且它不知道屬性實體被標記了[ShouldSerialize]
屬性。生成的序列化然后看起來像"Address": {}
而不是"Address": { "StreetNumber": 123 }
代碼如下所示:
class AddressDto
{
// PROBLEM 1/2: value does not get serialized, but I want it serialized as its property is [ShouldSerialize] attr tagged
public int StreetNumber { get; set; }
}
class PersonDto
{
public string Name { get; set; } // should not serialize as has not attr on it
[ShouldSerialize]
public string Id { get; set; }
[ShouldSerialize]
public AddressDto Address { get; set; }
}
// JSON contract resolver:
public class ShouldSerializeContractResolver: DefaultContractResolver
{
public ShouldSerializeContractResolver() { }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
var attr = member.GetCustomAttribute<ShouldSerializeContractResolver>(inherit: false);
// PROBLEM 2/2: here I need the code to access the member.DeclaringType instance somehow and then
// find its AddressDto property and its GetCustomAttribute<ShouldSerializeContractResolver>
if (attr is null)
{
property.ShouldSerialize = instance => { return false; };
}
return property;
}
}
// code invoked as:
PersonDto somePerson = IrrelevantSomePersonCreateNewFactoryFn();
var jsonSettings = new JsonSerializerSettings { ContractResolver = new ShouldSerializeContractResolver() };
var strJson = JsonConvert.SerializeObject(somePerson, jsonSettings);
串行器以“平面”模式作業,即。它使用決議器遍歷所有道具,并到達成員所在的位置,StreetNumber
我不知道如何訪問“父”成員資訊,這會很棒。
我發現這里的核心問題是我沒有“父”/DeclaringType 物件實體,需要找到一種獲取它的方法。
請注意,我無法通過等解決這個問題[JsonProperty]
,[JsonIgnore]
因為我的屬性很復雜,并且涉及到它自己的邏輯。
uj5u.com熱心網友回復:
您希望AddressDto
根據是否通過標有 的屬性遇到不同的序列化[ShouldSerialize]
,但是使用自定義合同決議器不容易做到這一點,因為 Json.NET 為每種型別創建一個合同,無論在序列化中的什么位置遇到它圖。即合約決議器將為AddressDto
以下兩種資料模型生成相同的合約:
class PersonDto
{
public string Name { get; set; } // should not serialize as has not attr on it
[ShouldSerialize]
public string Id { get; set; }
[ShouldSerialize]
public AddressDto Address { get; set; } // This and its properties should get serialized.
}
class SomeOtherDto
{
[ShouldSerialize]
public string SomeOtherValue { get; set; }
public AddressDto SecretAddress { get; set; } // Should not get serialized.
}
這就是為什么在為參考型別創建屬性時無法獲取參考屬性的屬性的原因。
相反,您需要在運行時跟蹤序列化程式何時開始和結束[ShouldSerialize]
屬性的序列化,并在內部設定一些執行緒安全的狀態變數。這可以通過使用你的合約決議器來注入一個自定義的 JsonConverter 來設定必要的狀態,暫時禁用自身以防止遞回呼叫,然后執行默認序列化:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ShouldSerializeAttribute : System.Attribute
{
}
public class ShouldSerializeContractResolver: DefaultContractResolver
{
static ThreadLocal<bool> inShouldSerialize = new (() => false);
static bool InShouldSerialize { get => inShouldSerialize.Value; set => inShouldSerialize.Value = value; }
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
var attr = member.GetCustomAttribute<ShouldSerializeAttribute>(inherit: false);
if (attr is null)
{
var old = property.ShouldSerialize;
property.ShouldSerialize = instance => InShouldSerialize && (old == null || old(instance));
}
else
{
var old = property.Converter;
if (old == null)
property.Converter = new InShouldSerializeConverter();
else
property.Converter = new InShouldSerializeConverterDecorator(old);
}
return property;
}
class InShouldSerializeConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var old = InShouldSerialize;
try
{
InShouldSerialize = true;
serializer.Serialize(writer, value);
}
finally
{
InShouldSerialize = old;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException();
public override bool CanRead => false;
public override bool CanConvert(Type objectType) => throw new NotImplementedException();
}
class InShouldSerializeConverterDecorator : JsonConverter
{
readonly JsonConverter innerConverter;
public InShouldSerializeConverterDecorator(JsonConverter innerConverter) => this.innerConverter = innerConverter ?? throw new ArgumentNullException(nameof(innerConverter));
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var old = InShouldSerialize;
try
{
InShouldSerialize = true;
if (innerConverter.CanWrite)
innerConverter.WriteJson(writer, value, serializer);
else
serializer.Serialize(writer, value);
}
finally
{
InShouldSerialize = old;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var old = InShouldSerialize;
try
{
InShouldSerialize = true;
if (innerConverter.CanRead)
return innerConverter.ReadJson(reader, objectType, existingValue, serializer);
else
return serializer.Deserialize(reader, objectType);
}
finally
{
InShouldSerialize = old;
}
}
public override bool CanConvert(Type objectType) => throw new NotImplementedException();
}
}
然后序列化如下:
IContractResolver resolver = new ShouldSerializeContractResolver(); // Cache statically & reuse for best performance
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
};
var json = JsonConvert.SerializeObject(person, Formatting.Indented, settings);
筆記:
Newtonsoft 建議快取和重用您的合約決議器以獲得最佳性能。
上面的實作有一個限制,如果你
[ShouldSerialize]
也用 標記JsonPropertyAttribute
,控制屬性值序列化的欄位,例如ItemConverterType
和IsReference
將被忽略。
演示小提琴在這里。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/473371.html
上一篇:將使用哪個版本的.NET類?