如何避免异常反序列化无效的枚举项?

时间:2012-01-25 15:47:54

标签: c# serialization xml-serialization deserialization xml-deserialization

为简单起见,我将使用水果展示我的示例代码。实际上我正在做一些更有意义的事情(我们希望)。假设我们有一个枚举:

public enum FruitType
{
    Apple,
    Orange,
    Banana
}

一堂课:

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

我们可以对它进行序列化和反序列化。现在,让我们修改枚举,现在就是:

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

在反序列化以前序列化的对象时,会出现System.InvalidOperation异常,因为Apple(原始枚举项)无效。该对象不会被反序列化。

我能够解决这个问题的一种方法是在FruitType类的Fruit属性被序列化时给出一个不同的名称,如下所示:

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

现在,在反序列化期间,旧属性会被忽略,因为找不到它。我想知道是否有一种方法可以在反序列化期间忽略/跳过无效的枚举项,这样就不会抛出异常并且对象仍然会被反序列化。

3 个答案:

答案 0 :(得分:3)

退出Apple并使用ObsoleteAttribute进行标记。这样,使用Apple的任何代码都会生成编译器警告。

答案 1 :(得分:1)

我自己一直在寻找类似的答案,并且当XML包含无效的枚举值时,编写此代码以捕获异常。它删除该元素,并尝试再次反序列化。如果元素是必需的,您仍然会得到一个例外。这是不完美的,但应该让你大部分时间到达你想去的地方

    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;
    }

答案 2 :(得分:0)

我发布了一个similar question并且没有找到一个简单的方法来捕获反序列化器在XML文件中遇到Apple时抛出的异常。在反序列化期间,我可以在缺少属性或元素时捕获一堆其他异常,但不能用于无效的枚举值。无效的枚举值(在您的情况下是Apple)使我摆脱了反序列化。

一种可能的解决方案是在Fruit类上实现IXMLSerializable。当反序列化器调用IXMLSerailizable.ReadXML()方法时,您必须查看传递给您的内容。当值为“Apple”时,根据某些逻辑将枚举设置为GreenApple或RedApple。