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

发布于 2024-12-29 02:28:09 字数 911 浏览 6 评论 0原文

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

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 (原始枚举项)是无效。该对象不会被反序列化。

我能够解决此问题的一种方法是在序列化时为 Fruit 类中的 FruitType 属性指定一个不同的名称,如下所示:

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

超可爱的懒熊 2025-01-05 02:28:09

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

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

玻璃人 2025-01-05 02:28:09

我自己一直在寻找类似的答案,并编写此代码以在 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;
    }

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;
    }
清风疏影 2025-01-05 02:28:09

我发布了一个类似问题并且还没有找到一个简单的方法来捕获反序列化器在XML文件中遇到Apple时抛出的异常。我可以在反序列化期间捕获丢失属性或元素的一堆其他异常,但不能捕获无效的枚举值。无效的枚举值(在你的例子中是苹果)让我无法进行反序列化。

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

I posted a similar question and have not found a simple method to catch the exception thrown when the deserializer encounters Apple in the XML file. I can catch a bunch of other exceptions during deserialiation for missing attributes or elements, but not for an invalid enum value. The invalid enum value (in your case Apple) blows me out of the deserialization.

One possible solution is to implement IXMLSerializable on the Fruit class. When the IXMLSerailizable.ReadXML() method is called by the deserializer, you'll have to see what is being passed to you. When the value is "Apple" set the enum to GreenApple or RedApple based on some logic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文