SerializationException can’t be caught when object provides a deserialization constructor
Well, this one was new to me, and I spent about ten minutes chasing my tail. Consider the following code (C#):
if (buffer != null) { using (MemoryStream ms = new MemoryStream(buffer)) using (GZipStream gs = new GZipStream(ms, CompressionMode.Decompress)) { try { userProfile = new BinaryFormatter().Deserialize(gs) as UserProfile; } catch (SerializationException) { this.Clear(); } catch (InvalidDataException) { this.Clear(); } } }
Looks pretty sane and cautious, right? This is part of one of my HTTP cookie classes, and it just takes a previously serialized object and attempts to deserialize it. If for some reason deserialization doesn’t work, big whoop, just clear the cookie and make the user log in again, because that probably means I just updated the software on the server anyway.
However, one of the constructors for the UserProfile class looks like this:
/// <summary> /// Initializes a new instance of the UserProfile class. /// </summary> /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> /// to populate with data.</param> /// <param name="context">The destination /// <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param> protected UserProfile(SerializationInfo info, StreamingContext context) { string mailAddress; string merchantCode; this.Identity = info.GetValue("Identity", typeof(ArmadilloIdentity)) as ArmadilloIdentity; this.Culture = new CultureInfo(info.GetInt32("Culture")); this.Currency = Currency.GetCurrency(info.GetString("Currency")); this.SetTimeZone(info.GetString("TimeZone")); mailAddress = info.GetString("MailAddress"); if (!string.IsNullOrEmpty(mailAddress)) { this.SetMailAddress(mailAddress); } merchantCode = info.GetString("MerchantCode"); if (!string.IsNullOrEmpty(merchantCode)) { this.merchant = new MerchantLookup(merchantCode); } this.revision = (byte[])info.GetValue("Revision", typeof(byte[])); }
This type of constructor allows me to work around the fact that some of the types that the UserProfile class is using aren’t marked as serializable, so I come up with my own way of serializing and deserializing them.
So what happens in my first code snippet when serialization fails? Well, the SerializationException doesn’t get caught. Why the hell not?
Well, the BinaryFormatter is obviously using reflection to new up an instance of my UserProfile. Typically, this is a non-issue because the constructor does not do anything interesting. But here, it does. So when my constructor bombs on a GetValue() call because I’ve changed the object since it was last deserialized, it tosses a SerializationException as we expect–but since the BinaryFormatter was using reflection to new up the object, that exception gets wrapped in a TargetInvocationException.
In other words, to be able to catch the exception, I need to
try { userProfile = new BinaryFormatter().Deserialize(gs) as UserProfile; } catch (TargetInvocationException) { this.Clear(); } catch (SerializationException) { this.Clear(); }
It’s the TargetInvocationException that gets caught. (I left the SerializationException in there because I’m paranoid.)
I too conceive therefore , perfectly written post! .