Skip to content

Commit b83c5bf

Browse files
Add custom AggregateException serializer. (#266)
* Add custom AggregateException serializer. * Exceptions does not serialize WatsonBuckets in linux systems * Fix WatsonBucket serialization code * Remove WatsonBuckets serialization Co-authored-by: Aaron Stannard <[email protected]>
1 parent 41ceaaa commit b83c5bf

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed

src/Hyperion.Tests/CustomObjectTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,44 @@ public void CanSerializeException()
8585
Assert.Equal(expected.Message, actual.Message);
8686
}
8787

88+
[Fact]
89+
public void CanSerializeAggregateException()
90+
{
91+
Exception ex1;
92+
Exception ex2;
93+
AggregateException expected;
94+
try
95+
{
96+
throw new Exception("hello wire 1");
97+
}
98+
catch (Exception e)
99+
{
100+
ex1 = e;
101+
}
102+
try
103+
{
104+
throw new Exception("hello wire 2");
105+
}
106+
catch (Exception e)
107+
{
108+
ex2 = e;
109+
}
110+
try
111+
{
112+
throw new AggregateException("Aggregate", ex1, ex2);
113+
}
114+
catch (AggregateException e)
115+
{
116+
expected = e;
117+
}
118+
Serialize(expected);
119+
Reset();
120+
var actual = Deserialize<AggregateException>();
121+
Assert.Equal(expected.StackTrace, actual.StackTrace);
122+
Assert.Equal(expected.Message, actual.Message);
123+
Assert.Equal(expected.InnerExceptions.Count, actual.InnerExceptions.Count);
124+
}
125+
88126
[Fact]
89127
public void CanSerializePolymorphicObject()
90128
{
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#region copyright
2+
// -----------------------------------------------------------------------
3+
// <copyright file="ExceptionSerializerFactory.cs" company="Akka.NET Team">
4+
// Copyright (C) 2015-2016 AsynkronIT <https://github.com/AsynkronIT>
5+
// Copyright (C) 2016-2016 Akka.NET Team <https://github.com/akkadotnet>
6+
// </copyright>
7+
// -----------------------------------------------------------------------
8+
#endregion
9+
10+
using System;
11+
using System.Collections;
12+
using System.Collections.Concurrent;
13+
using System.Reflection;
14+
using System.Runtime.Serialization;
15+
using Hyperion.Extensions;
16+
using Hyperion.ValueSerializers;
17+
18+
namespace Hyperion.SerializerFactories
19+
{
20+
internal sealed class AggregateExceptionSerializerFactory : ValueSerializerFactory
21+
{
22+
private static readonly TypeInfo ExceptionTypeInfo = typeof(Exception).GetTypeInfo();
23+
private static readonly TypeInfo AggregateExceptionTypeInfo = typeof(AggregateException).GetTypeInfo();
24+
private readonly FieldInfo _className;
25+
private readonly FieldInfo _innerException;
26+
private readonly FieldInfo _stackTraceString;
27+
private readonly FieldInfo _remoteStackTraceString;
28+
private readonly FieldInfo _message;
29+
private readonly FieldInfo _innerExceptions;
30+
31+
public AggregateExceptionSerializerFactory()
32+
{
33+
_className = ExceptionTypeInfo.GetField("_className", BindingFlagsEx.All);
34+
_innerException = ExceptionTypeInfo.GetField("_innerException", BindingFlagsEx.All);
35+
_message = AggregateExceptionTypeInfo.GetField("_message", BindingFlagsEx.All);
36+
_remoteStackTraceString = ExceptionTypeInfo.GetField("_remoteStackTraceString", BindingFlagsEx.All);
37+
_stackTraceString = ExceptionTypeInfo.GetField("_stackTraceString", BindingFlagsEx.All);
38+
_innerExceptions = AggregateExceptionTypeInfo.GetField("m_innerExceptions", BindingFlagsEx.All);
39+
}
40+
41+
public override bool CanSerialize(Serializer serializer, Type type) =>
42+
#if NETSTANDARD16
43+
false;
44+
#else
45+
AggregateExceptionTypeInfo.IsAssignableFrom(type.GetTypeInfo());
46+
#endif
47+
48+
public override bool CanDeserialize(Serializer serializer, Type type) => CanSerialize(serializer, type);
49+
50+
#if NETSTANDARD16
51+
// Workaround for CoreCLR where FormatterServices.GetUninitializedObject is not public
52+
private static readonly Func<Type, object> GetUninitializedObject =
53+
(Func<Type, object>)
54+
typeof(string).GetTypeInfo().Assembly.GetType("System.Runtime.Serialization.FormatterServices")
55+
.GetMethod("GetUninitializedObject", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)
56+
.CreateDelegate(typeof(Func<Type, object>));
57+
#else
58+
private static readonly Func<Type,object> GetUninitializedObject = System.Runtime.Serialization.FormatterServices.GetUninitializedObject;
59+
#endif
60+
61+
public override ValueSerializer BuildSerializer(Serializer serializer, Type type,
62+
ConcurrentDictionary<Type, ValueSerializer> typeMapping)
63+
{
64+
#if !NETSTANDARD1_6
65+
var exceptionSerializer = new ObjectSerializer(type);
66+
exceptionSerializer.Initialize((stream, session) =>
67+
{
68+
var info = new SerializationInfo(type, new FormatterConverter());
69+
70+
info.AddValue("ClassName", stream.ReadString(session), typeof (string));
71+
info.AddValue("Message", stream.ReadString(session), typeof (string));
72+
info.AddValue("Data", stream.ReadObject(session), typeof (IDictionary));
73+
info.AddValue("InnerException", stream.ReadObject(session), typeof (Exception));
74+
info.AddValue("HelpURL", stream.ReadString(session), typeof (string));
75+
info.AddValue("StackTraceString", stream.ReadString(session), typeof (string));
76+
info.AddValue("RemoteStackTraceString", stream.ReadString(session), typeof (string));
77+
info.AddValue("RemoteStackIndex", stream.ReadInt32(session), typeof (int));
78+
info.AddValue("ExceptionMethod", stream.ReadString(session), typeof (string));
79+
info.AddValue("HResult", stream.ReadInt32(session));
80+
info.AddValue("Source", stream.ReadString(session), typeof (string));
81+
info.AddValue("InnerExceptions", stream.ReadObject(session), typeof (Exception[]));
82+
83+
return Activator.CreateInstance(type, BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance, null, new object[]{info, new StreamingContext()}, null);
84+
}, (stream, exception, session) =>
85+
{
86+
var info = new SerializationInfo(type, new FormatterConverter());
87+
var context = new StreamingContext();
88+
((AggregateException)exception).GetObjectData(info, context);
89+
90+
var className = info.GetString("ClassName");
91+
var message = info.GetString("Message");
92+
var data = info.GetValue("Data", typeof(IDictionary));
93+
var innerException = info.GetValue("InnerException", typeof(Exception));
94+
var helpUrl = info.GetString("HelpURL");
95+
var stackTraceString = info.GetString("StackTraceString");
96+
var remoteStackTraceString = info.GetString("RemoteStackTraceString");
97+
var remoteStackIndex = info.GetInt32("RemoteStackIndex");
98+
var exceptionMethod = info.GetString("ExceptionMethod");
99+
var hResult = info.GetInt32("HResult");
100+
var source = info.GetString("Source");
101+
var innerExceptions = (Exception[]) info.GetValue("InnerExceptions", typeof(Exception[]));
102+
103+
StringSerializer.WriteValueImpl(stream, className, session);
104+
StringSerializer.WriteValueImpl(stream, message, session);
105+
stream.WriteObjectWithManifest(data, session);
106+
stream.WriteObjectWithManifest(innerException, session);
107+
StringSerializer.WriteValueImpl(stream, helpUrl, session);
108+
StringSerializer.WriteValueImpl(stream, stackTraceString, session);
109+
StringSerializer.WriteValueImpl(stream, remoteStackTraceString, session);
110+
Int32Serializer.WriteValueImpl(stream, remoteStackIndex, session);
111+
StringSerializer.WriteValueImpl(stream, exceptionMethod, session);
112+
Int32Serializer.WriteValueImpl(stream, hResult, session);
113+
StringSerializer.WriteValueImpl(stream, source, session);
114+
stream.WriteObjectWithManifest(innerExceptions, session);
115+
});
116+
if (serializer.Options.KnownTypesDict.TryGetValue(type, out var index))
117+
{
118+
var wrapper = new KnownTypeObjectSerializer(exceptionSerializer, index);
119+
typeMapping.TryAdd(type, wrapper);
120+
}
121+
else
122+
typeMapping.TryAdd(type, exceptionSerializer);
123+
return exceptionSerializer;
124+
#else
125+
return null;
126+
#endif
127+
}
128+
}
129+
}

src/Hyperion/SerializerOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ internal static List<Func<string, string>> DefaultPackageNameOverrides()
5757
new FSharpMapSerializerFactory(),
5858
new FSharpListSerializerFactory(),
5959
//order is important, try dictionaries before enumerables as dicts are also enumerable
60+
new AggregateExceptionSerializerFactory(),
6061
new ExceptionSerializerFactory(),
6162
new ImmutableCollectionsSerializerFactory(),
6263
new ExpandoObjectSerializerFactory(),

0 commit comments

Comments
 (0)