Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How avoid exception deserializing an invalid enum item?

For simplicity purposes here, I will show my sample code using fruit. In actuality I am doing something more meaningful (we hope). Let say we have an enum:

public enum FruitType
{
    Apple,
    Orange,
    Banana
}

And a class:

[Serializable]
public class Fruit
{
    public FruitType FruitType { get; set; }
    public Fruit(FruitType type)
    {
        this.FruitType = type;
    }
}

We can serialize and de-serialize it. Now, lets revise the enum, so that it is now:

public enum FruitType
{
    GreenApple,
    RedApple,
    Orange,
    Banana
}

When de-serializing previously serialized objects, you get a System.InvalidOperation exception as Apple (original enum item) is not valid. The object does not get de-serialized.

One way I was able to resolve this was to give the FruitType property in the Fruit class a different name when it gets serialized as follows:

    [XmlElement(ElementName = "Mode")]
    public FruitType FruitType { get; set; }

Now, during de-serialization the old property gets ignored as it is not found. I would like to know if there is a way to ignore/skip invalid enum items during de-serialization, so that no exception is thrown and the object still gets de-serialized.

like image 987
Elan Avatar asked Oct 09 '22 21:10

Elan


2 Answers

Leave Apple and mark it with the ObsoleteAttribute. That way, any code using Apple will generate a compiler warning.

like image 71
Daniel A. White Avatar answered Oct 13 '22 10:10

Daniel A. White


I've been looking for similar answers myself, and wrote this to catch the exception when the XML contains a value for an enum that is not valid. It removes that element, and tries to deserialize again. If the element is required, you still will get an exception. It's imperfect, but should get you most of the way to where you want to be

    private const string XmlError = "There is an error in XML document ";
    private const string InstanceValidationError = "Instance validation error:";
    private static readonly Regex XmlErrorRegex = new Regex("There is an error in XML document \\((\\d+), (\\d+)\\).");
    private static readonly Regex InstanceValidationErrorRegex = new Regex("Instance validation error: '(\\S+)' is not a valid value for (\\S+).");
    private const string TagFinderString = "\\>{0}\\</(\\S+)\\>";

    /// <summary>
    /// Helper method to deserialize xml message
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="message"></param>
    /// <returns></returns>
    public T Deserialize(string message)
    {
        var result = default(T);
        if (!string.IsNullOrEmpty(message))
        {
            using (var reader = new StringReader(message))
            {
                try
                {
                    result = (T)_serializer.Deserialize(reader);
                }
                catch (InvalidOperationException ex)
                {
                    if (ex.Message.StartsWith(XmlError))
                    {
                        if(ex.InnerException != null && ex.InnerException.Message.StartsWith(InstanceValidationError))
                        {
                            var instanceValidationErrorMatches = InstanceValidationErrorRegex.Matches(ex.InnerException.Message);
                            if (instanceValidationErrorMatches.Count > 0)
                            {
                                var locationMatches = XmlErrorRegex.Matches(ex.Message);
                                var startIndex = GetStartIndex(message, locationMatches);
                                var match = instanceValidationErrorMatches[0];
                                if(match.Groups.Count > 0)
                                {
                                    var toRemove = GetToRemove(message, match, startIndex);

                                    return Deserialize(message.Replace(toRemove, string.Empty));
                                }
                            }
                        }
                    }
                }
            }
        }
        return result;
    }

    private static string GetToRemove(string message, Match match, int startIndex)
    {
        var value = match.Groups[1];
        var tagFinder = new Regex(string.Format(TagFinderString, value));
        var tagFinderMatches = tagFinder.Matches(message.Substring(startIndex));
        var tag = tagFinderMatches[0].Groups[1];

        return string.Format("<{0}>{1}</{0}>", tag, value);
    }

    private static int GetStartIndex(string message, MatchCollection locationMatches)
    {
        var startIndex = 0;
        if (locationMatches.Count > 0)
        {
            var lineNumber = int.Parse(locationMatches[0].Groups[1].Value);
            var charIndex = int.Parse(locationMatches[0].Groups[2].Value);
            using (var locationFinder = new StringReader(message))
            {
                for (var i = 1; i < lineNumber; i++)
                {
                    startIndex += locationFinder.ReadLine().Length;
                }
            }
            startIndex += charIndex;
        }
        return startIndex;
    }
like image 33
Josh Winkler Avatar answered Oct 13 '22 12:10

Josh Winkler