Как избежать исключения десериализации недействительного элемента enum? - PullRequest
5 голосов
/ 25 января 2012

Для простоты здесь я покажу свой пример кода с использованием 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 (оригинальный элемент перечисления) недопустим.Объект не десериализован.

Один из способов, которым я смог решить эту проблему, - присвоить свойству FruitType в классе Fruit другое имя при сериализации следующим образом:

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

Теперь во время десериализации старое свойство игнорируется, так как оно не найдено.Я хотел бы знать, есть ли способ игнорировать / пропускать недопустимые элементы перечисления во время десериализации, чтобы не генерировалось исключение и объект по-прежнему десериализовался.

Ответы [ 3 ]

3 голосов
/ 25 января 2012

Оставьте Apple и пометьте его ObsoleteAttribute. Таким образом, любой код, использующий Apple, выдаст предупреждение компилятора.

1 голос
/ 30 января 2014

Я сам искал похожие ответы и написал это, чтобы поймать исключение, когда 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;
    }
0 голосов
/ 23 мая 2012

Я опубликовал аналогичный вопрос и не нашел простого способа отловить исключение, возникающее при обнаружении десериализатором Apple в файле XML.Я могу поймать кучу других исключений во время десериализации для отсутствующих атрибутов или элементов, но не для недопустимого значения перечисления.Неверное значение enum (в вашем случае Apple) выбивает меня из десериализации.

Одним из возможных решений является реализация IXMLSerializable для класса Fruit.Когда десериализатор вызывает метод IXMLSerailizable.ReadXML () , вам нужно будет увидеть, что вам передают.Когда значение равно «Apple», установите перечисление в GreenApple или RedApple на основе некоторой логики.

...