StockOverflowExceptionは、JObject.FromObject(value)
の呼び出しでWriteJson
メソッド内の以下のコードによって発生しています。それはWriteJson
メソッドを呼び出します。JObject.FromValueを使用する場合のStackOverflow
再帰的なスタックオーバーフローの問題を避けるためにAggregateEventConverterを書き直すにはどうすればよいですか?
イベントが永続的にストリームに書き込まれ、他のコーダーが古いイベントクラスの名前をリファクタリングしてから数年後に逆シリアル化できる必要があるため、コードはこのように記述されます。たとえば、class AppleFellOffTree
をclass AppleFellOffTree_v001
に変更する可能性がありますが、古いイベントを逆シリアル化する目的でアセンブリに保持します。 AggregateEventTypeId
属性は、イベントクラスをシフト/リファクタリングしながらコーダーがこれらの属性をそのまま維持する限り、jsonを正しいクラスにデシリアライズするのに役立ちます。
Newtonsoft独自のTypeNameHandling機能は、名前がリファクタリングされたクラスを正確に逆シリアル化するのに役立ちません。
class Program {
static void Main(string[] args) {
var e1 = new AppleFellOffTree {
At = TimeStamp.Now,
Id = Guid.NewGuid(),
VersionNumber = 21,
};
var json = JsonConvert.SerializeObject(e1);
var e2 = JsonConvert.DeserializeObject<AggregateEvent>(json);
}
}
[Serializable]
[JsonConverter(typeof(AggregateEventConverter))]
public class AggregateEvent {
public string EventName => GetType().Name;
public Guid Id;
public int VersionNumber;
public TimeStamp At;
}
[AggregateEventTypeId("{44B9114E-085F-4D19-A142-0AC76573602B}")]
public class AppleFellOffTree : AggregateEvent {
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class AggregateEventTypeIdAttribute : Attribute {
public readonly Guid Id;
public AggregateEventTypeIdAttribute(string guid) {
Id = Guid.Parse(guid);
}
}
public class AggregateEventConverter : JsonConverter {
public override bool CanRead => true;
public override bool CanWrite => true;
public override bool CanConvert(Type objectType) => objectType == typeof(AggregateEvent) || objectType.IsSubclassOf(typeof(AggregateEvent));
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
if (null == value) {
writer.WriteValue(value);
return;
}
var jObject = JObject.FromObject(value);
jObject.Add("$typeId", EventTypes.GetEventTypeId(value.GetType()));
jObject.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var jToken = JToken.ReadFrom(reader);
if (jToken.Type != JTokenType.Object) {
throw new NotImplementedException();
} else {
var jObject = (JObject)jToken;
var eventTypeId = (Guid)jObject.GetValue("$typeId");
var eventType = EventTypes.GetEventType(eventTypeId);
return JsonConvert.DeserializeObject(jToken.ToString(), eventType);
}
}
}
internal static class EventTypes {
static readonly Dictionary<Guid, Type> Data = new Dictionary<Guid, Type>();
static EventTypes() {
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var eventTypes = assemblies.SelectMany(a => a.GetTypes()
.Where(t => t.IsSubclassOf(typeof(AggregateEvent)))
.Where(t => !t.IsAbstract))
.ToArray();
// t is for eventType
foreach (var t in eventTypes) {
var id = GetEventTypeId(t);
if (Data.ContainsKey(id))
throw new Exception($"Duplicate {nameof(AggregateEventTypeIdAttribute)} value found on types '{t.FullName}' and '{Data[id].FullName}'");
Data[id] = t;
}
}
public static Type GetEventType(Guid eventTypeId) {
return Data[eventTypeId];
}
public static Guid GetEventTypeId(Type type) {
// a is for attribute
var a = type.GetCustomAttributes(typeof(AggregateEventTypeIdAttribute), false)
.Cast<AggregateEventTypeIdAttribute>()
.FirstOrDefault();
if (null == a)
throw new Exception($"{nameof(AggregateEventTypeIdAttribute)} attribute does not exist on type {type.FullName}.");
if (Guid.Empty == a.Id)
throw new Exception($"{nameof(AggregateEventTypeIdAttribute)} attribute was not set to a proper value on type {type.FullName}");
return a.Id;
}
public static IEnumerable<KeyValuePair<Guid, Type>> GetAll => Data;
}
(https://stackoverflow.com/q [ '[JsonConverter]'?を介してベースタイプに適用される方法JsonConvert.DeserializeObjectを呼び出し、JsonConverterを無効にする]の重複のように見えます/ 45547123/3744182)。 '[JsonConvert()]'](https://stackoverflow.com/q/29719509/3744182)を使用すると[JSON.NetからStackOverflowExceptionがスローされます。 – dbc
リンクされたソリューションがうまくいかない場合は、どこに問題があるか教えてください。質問を再開します。 – dbc
@dbcこれらのリンクは助けました。ありがとうございます。質問をもう一度開いて、私が使用したコードを投稿してください。 – bboyle1234