Как я могу заставить XmlSerializer кодировать bools как yes/no?

Я отправляю xml в другую программу, которая ожидает, что логические флаги будут "да" или "нет", а не "истина" или "ложь".

У меня есть класс, определенный как:

[XmlRoot()]
public class Foo {
    public bool Bar { get; set; }
}

Когда я сериализую его, мой вывод выглядит следующим образом:

<Foo><Bar>true</Bar></Foo>

Но я хотел бы, чтобы это было так:

<Foo><Bar>yes</Bar></Foo>

Могу ли я сделать это во время сериализации? Я бы предпочел не прибегать к этому:

[XmlRoot()]
public class Foo {
    [XmlIgnore()]
    public bool Bar { get; set; }

    [XmlElement("Bar")]
    public string BarXml { get { return (Bar) ? "yes" : "no"; } }
}

Обратите внимание, что я также хочу иметь возможность десериализовать эти данные снова.

Ответы

Ответ 1

Хорошо, я изучал это еще немного. Вот что я придумал:

// use this instead of a bool, and it will serialize to "yes" or "no"
// minimal example, not very robust
public struct YesNo : IXmlSerializable {

    // we're just wrapping a bool
    private bool Value;

    // allow implicit casts to/from bool
    public static implicit operator bool(YesNo yn) {
        return yn.Value;
    }
    public static implicit operator YesNo(bool b) {
        return new YesNo() {Value = b};
    }

    // implement IXmlSerializable
    public XmlSchema GetSchema() { return null; }
    public void ReadXml(XmlReader reader) {
        Value = (reader.ReadElementContentAsString() == "yes");
    }
    public void WriteXml(XmlWriter writer) {
        writer.WriteString((Value) ? "yes" : "no");
    }
}

Затем я меняю класс Foo на это:

[XmlRoot()]
public class Foo {      
    public YesNo Bar { get; set; }
}

Обратите внимание, что поскольку YesNo неявно отбрасывается на bool (и наоборот), вы все равно можете сделать это:

Foo foo = new Foo() { Bar = true; };
if ( foo.Bar ) {
   // ... etc

Другими словами, вы можете рассматривать его как bool.

И w00t! Он сериализуется для этого:

<Foo><Bar>yes</Bar></Foo>

Он также десериализуется правильно.

Возможно, есть некоторый способ заставить мой XmlSerializer автоматически добавлять любые bool, которые он встречает в YesNo, как он есть, но я еще не нашел его. Кто-нибудь?

Ответ 2

Очень просто. Используйте суррогатное имущество. Примените XmlIgnore к фактическому свойству. Суррогат - это строка и должен использовать атрибут XmlElement, который принимает переопределение элемента. Укажите имя фактического свойства в переопределении. Суррогатное свойство сериализуется по-разному в зависимости от ценности фактического имущества. Вы также должны предоставить установщик для суррогата, и сеттер должен установить фактическое свойство соответствующим образом, независимо от того, какое значение оно сериализует. Другими словами, он должен идти в обоих направлениях.

Снип:

    public class SomeType 
    {

        [XmlElement]
        public int IntValue;

        [XmlIgnore]
        public bool Value;

        [XmlElement("Value")]
        public string Value_Surrogate {
            get { return (Value)? "Yes, definitely!":"Absolutely NOT!"; }
            set { Value= (value=="Yes, definitely!"); }
        }

    }

нажмите здесь полный компилируемый исходный пример.

Ответ 3

Создание значения bool сериализации как "да" или "нет" изменяет тип данных как булево. Вместо этого вы можете добавить отдельное свойство, которое оценивает логическое значение и возвращает "да" или "нет" , если это необходимо для типа данных? Возможно, вы могли бы даже заставить "да" или "нет" сделать тип возвращаемого значения перечислением, которое определяет только эти значения.

public YesOrNo DoYouLoveIt
{
    get { return boolToEvaluate ? YesOrNo.Yes : YesOrNo.No; }
}

Это может быть излишним, но может ответить на ваши потребности. Единственная причина, по которой я вызываю перечисление для такого простого значения, - это ограничение значений по сравнению с любой строкой.

Ответ 4

Я использую метод property, но вместо того, чтобы проверять, соответствует ли строка да или нет, я предпочитаю проверять, начинается ли строка с (без учета регистра) "YT1". Это позволяет файлу содержать true, True, t, T, y, Y, yes, Yes, 1 и т.д. Все, что будет оцениваться как true. Хотя я могу указать, что false - false, False, f, F, n, N, no, No, 0 и т.д., Все, что не соответствует истинному, по-прежнему оценивается как false.

Ответ 5

Пример вашей собственности, вероятно, самый простой способ сделать это. Если это помогает, я считаю, что вам не нужно делать это публичным свойством, поскольку атрибут реализует ISerializable для класса за вашей спиной. Чтобы включить десериализацию, вы должны просто реализовать set { Bar = value == "yes"; }

Ответ 6

@Blorgbeard: Если у вас есть более одного из этих классов YesNo в классе объектов, обязательно прочитайте весь элемент.

public void ReadXml(XmlReader reader)
{
    string element = reader.ReadOuterXml();
    int startIndex = element.IndexOf('>') + 1;
    int length = element.LastIndexOf('<') - startIndex;

    string text = (element.Substring(startIndex, length).ToLowerInvariant();

    Value = (text == "yes");
}

В противном случае это может вызвать проблемы.

Метод ReadXml должен восстановить объект, используя информацию, написанную с помощью метода WriteXml.

Когда этот метод вызывается, читатель позиционируется в начале элемента, который обертывает информацию для вашего типа. То есть, просто перед началом тега, который указывает начало сериализации объект. Когда этот метод возвращается, он должен прочитать весь элемент от начала до конца, включая все его содержимое. в отличие от WriteXml, структура не обрабатывает элемент оболочки автоматически. Ваша реализация должна это сделать. Несоблюдение эти правила позиционирования могут привести к тому, что код генерирует неожиданное время выполнения исключения или поврежденные данные.

Ответ 7

как реализовать методы OnSerializing и OnDeserializing?

Ответ 8

То, что вам нужно делать, больше похоже на проблему с дисплеем. Если ваше приложение позволяет, вам будет лучше сохранить тип данных как логическое и отобразить Yes/No в вашем пользовательском интерфейсе.