C# проблема логической сериализации

У меня есть значение, которое я хотел бы десериализовать как логическое, но десериализация не поддерживает указанный случай, а именно: ЛОЖЬ или ИСТИНА, и предоставленный формат было бы громоздко изменить, я получаю исключение:

System.FormatException: The string 'FALSE' is not a valid Boolean value.

Я знаю это потому, что сериализатор XML этого не поддерживает; разрешены только допустимые значения схемы XML, такие как «false» или «true» (1-й бит исследования, проверьте!).

Итак, первая альтернатива - создать строковое свойство для преобразования, примерно так:

public class MyExample
{
    [XmlIgnore] public bool _booleanField { get; set; }

    [XmlElement("BooleanField")]
    public string BooleanFieldString
    {
        get => _booleanField.ToString().ToLower();
        set => _booleanField = ConvertBooleanStringValue(value);
    }

    private bool ConvertBooleanStringValue(string booleanAsString)
    {
        switch (booleanAsString.ToUpper())
        {
            case "TRUE":
            case "T":
            case "1":
            case "Y":
            case "YES":
                return true;
            case "FALSE":
            case "F":
            case "0":
            case "N":
            case "NO":
                return false;
            default:
                return false;
        }
    }
}

Но мне это не нравится, потому что это портит чистые классы, которые я создал, и мне нужно было бы использовать это вокруг всех логических значений, которые у меня есть для 28 классов (2-й этап исследования, проверьте!).

XML, который я получаю, потенциально может передавать множество логических значений для всех видов параметров, поэтому, если я выберу синтаксический анализ исходных данных, мне нужно будет знать, какие все логические элементы являются. Ограничением здесь является XMLSerialiser, хотя я понимаю, что это не его вина.

Я мог бы реализовать интерфейс ISerializable и написать конкретную реализацию, но это большая работа для логического значения, и из проведенного мной исследования я не уверен, что есть способ сделать это только для определенного свойства, которое, очевидно, ограничить боль (3-е исследование !!!).

Существуют и другие структуры сериализации, которые могут решить эту проблему, например ExtendedXmlSerializer, но я бы предпочел придерживаться того, что я знаю, если это возможно.

ConvertBooleanStringValue возвращает логическое значение. Итак, эта строчка set => _booleanField = ConvertBooleanStringValue(value); сбивает с толку.
bolkay 31.10.2018 13:30

Вы можете значительно снизить затраты, просто преобразовав этот метод в статический класс утилит, который могут вызывать все ваши установщики свойств. Однако по-прежнему необходимо реализовать свойство и вызвать метод.

HimBromBeere 31.10.2018 13:36

Есть ли здесь вообще вопрос?

Kenneth K. 31.10.2018 13:52

Возможно, немного подвержен ошибкам, но в зависимости от приложения я бы, вероятно, добавил параметр «Разрешить устаревший формат xml» и использовать string.Replace для "FALSE" и >FALSE</ и других значений. Это, вероятно, не будет хорошей идеей для числовых значений, хотя

C.Evenhuis 31.10.2018 13:57

Является ли ты (единолично) ответственным за сериализация или вам нужно десериализовать данные, которые вы получаете откуда-то вне вашего контроля?

Corak 31.10.2018 14:20

Это полностью вне моего контроля, Корак. Я также обдумывал решение К. Эвенхейса, но мне оно показалось запутанным.

Stuie_M 31.10.2018 14:25

Кеннет К. Хорошее замечание. Думаю, вопрос в том; Как лучше всего это сделать? Вариантов много, но ни один из них не кажется достаточно чистым.

Stuie_M 31.10.2018 14:26
4
7
683
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

При условии, что вы можете идентифицировать все логические элементы, которым нужна эта альтернативная процедура синтаксического анализа, вы можете реализовать собственный XmlTextReader и передать его обычному XmlSerializer.

CustomXmlReader ниже принимает список имен элементов xml, требующих особого внимания.

public class CustomXmlReader : XmlTextReader
{
    private readonly IList<String> _booleanFieldNames;
    private Boolean _parseBooleanString;

    public CustomXmlReader(IList<String> booleanFieldNames, TextReader reader) : base(reader)
    {
        this._booleanFieldNames = booleanFieldNames;
    }        

    public override XmlNodeType MoveToContent()
    {            
        XmlNodeType nodeType = base.MoveToContent();            
        this._parseBooleanString = ((XmlNodeType.Element == nodeType)
            && this._booleanFieldNames.Contains(this.Name)
            );            
        return nodeType;
    }        

    public override String ReadString()
    {
        String value = base.ReadString();                        
        if (this._parseBooleanString)
        {                
            if (value.Equals("TRUE", StringComparison.OrdinalIgnoreCase)
                || value.Equals("T", StringComparison.OrdinalIgnoreCase)
                || value.Equals("1", StringComparison.OrdinalIgnoreCase)
                || value.Equals("YES", StringComparison.OrdinalIgnoreCase)
                || value.Equals("Y", StringComparison.OrdinalIgnoreCase)
                )
            { 
                return "true";                        
            }

            return "false";                
        }

        return value;
    }
}

Классы, соответствующие xml, не знают об этом пользовательском синтаксическом анализе.

public class MyExample
{
    public MyExample() {}

    [XmlElement("BooleanField")]
    public Boolean BooleanFieldString { get; set; }
}

Приведенный ниже код анализирует следующую структуру xml

const String XML = @"
    <MyExample>
        <BooleanField>T</BooleanField>
    </MyExample>";

using (var stringReader = new StringReader(XML))
using (var xmlReader = new CustomXmlReader(new List<String> { "BooleanField" }, stringReader))
{
    XmlSerializer serializer = new XmlSerializer(typeof(MyExample));
    MyExample mx = serializer.Deserialize(xmlReader) as MyExample;
    Console.WriteLine(mx.BooleanFieldString); // True
}

Другие вопросы по теме