2009-08-18 7 views
1

Я реализую IXMLSerializable в одном из моих классов. Он содержит несколько числовых свойств, которые являются нулевыми (int? Double? И т. Д.).Каков правильный способ сериализации типов с нулевым значением?

Каков правильный способ сериализации/сериализации данных через IXMLSerializable? Вот что я делаю сейчас, что работает, но, очевидно, не похоже на правильный способ сделать это.

void IXmlSerializable.WriteXml(XmlWriter writer) 
{ 
    ... 

    if (this._PropName == null) 
    { 
     writer.WriteElementString("PropName", "NULL"); 
    } 
    else 
    { 
     writer.WriteElementString("PropName", this._PropName.ToString()); 
    } 
    ... 
} 

void IXmlSerializable.ReadXml(XmlReader reader)  
{ 
    string tempStr; 
    ... 

    reader.ReadStartElement("PropName"); 

    if (tempStr != "NULL") 
    { 
     this._PropName = double.Parse(tempStr); 
    } 
    else 
    { 
     this._PropName = null; 
    } 
    ... 
} 

Update: Был задан вопрос о том, что я даю немного фона, почему я реализую IXmlSerializable. Я работаю над программой для архитектурного проектирования, где мне нужен класс, представляющий коллекцию Floors. Каждый Этаж имеет такие свойства, как Floor.Area, Floor.Height и т. Д. Высота пола, однако, определяется суммой высот пола под ним. Таким образом, всякий раз, когда изменяется свойство Floor.Height или изменяется размер FloorCollection, возвышения . Полы повторно заправлены.

Мой класс FloorCollection, который мне нужно для сериализации, наследуется от BindingList. Если я попытаюсь сериализовать этот класс напрямую, он сериализует сбор полов, но не какие-либо свойства или поля в классе. См. Мой previous post on this.

Теперь я пытаюсь добавить возможность ограничить максимальную высоту, максимальную высоту и минимальную высоту надстройки этажей здания в коллекции. Поэтому я использую nullable doubles для представления этих ограничений, где нулевое значение означает неограниченное. Свойства возвышения могут быть положительными, отрицательными или нулевыми. Таким образом, должно существовать альтернативное состояние, null, которое идентифицирует, когда нет ограничений.

Теперь, когда мне кажется, что проще всего иметь отдельное логическое значение, которое определяет, существует ли ограничение высоты/высоты, а затем регулярное двойное свойство, которое идентифицирует, что такое ограничение, если оно включено.

+0

Кажется мне хорошо? –

+0

Это не страшно, но рассмотрим случай, когда свойство является строкой, а не двойной. Что такое правильное значение флага для строки? –

+0

Реализация 'IXmlSerializable', как правило, является плохой ИМО. Вы теряете всю простоту и доброту схемы, что делает ее желаемой в первом месте. –

ответ

3

Вы хотите всегда писать XML для свойства, но если значение свойства равно null, вы хотите включить атрибут xsi: nil = "true".

void IXmlSerializable.WriteXml(XmlWriter writer) 
{ 
    ... 

    if (this._PropName == null) 
    { 
     writer.WriteStartElement("PropName"); 
     writer.WriteAttributeString("xsi", "nil", "http://www.w3.org/2001/XMLSchema-instance", "true"); 
     writer.WriteEndElement(); 
    } 
    else 
    { 
     writer.WriteElementString("PropName", this._PropName.ToString()); 
    } 
    ... 
} 

Вы также, вероятно, будете хотеть, чтобы написать XSI: тип = "XSD: тип данных" атрибут, где XSD является http://www.w3.org/2001/XMLSchema имен. Это позволит вам прочитать тип данных во время десериализации, чтобы узнать, как (и как) преобразовать значение.

+1

+1 для xsi: nil, -1 для сопротивления на xsi: type. Там, безусловно, место для полностью самоописывающих данных, но сериализация часто это не так. –

+0

@Steven Sudit: Я не согласен. Посмотрите на любой datacontract сериализованный XML, и вы почти наверняка увидите информацию о типе. –

+0

@Scott: Честно говоря, я действительно не уверен, что здесь намерение Эрика. Я исходил из предположения, что они хотят сгладить тип до строки для сохранения, как в базе данных. Если это так, то null = omitted - хороший ответ. Но если они используют класс в WCF, ваш ответ имеет больше смысла. Я спросил Эрика, каковы его цели; возможно, это поможет разобраться в этом. –

3

Опустите элемент, если он равен нулю.

редактировать

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

редактировать

Для получения информации о том, как сказать XmlSerializer опускать пустую собственность без написания пользовательского сериалайзера, проверьте this. Кроме того, учитывая исходную информацию, в то время как я по-прежнему предпочитаю упущение в xsi: nil, теперь я думаю, что последнее является приемлемым решением.

0
void IXmlSerializable.WriteXml(XmlWriter writer) 
{ 
    if (this._Foo == null) 
    { 
    writer.WriteStartElement("Foo"); 
    writer.WriteEndElement("Foo"); 
    } 
    else 
    { 
    writer.WriteElementString("Foo", this._Foo.ToString()); 
    } 
} 
1

Вы можете написать нуль:

writer.WriteElementString("Test", null); 

Это только приведет к элементу как <Test/>

Или быть немного более конкретно о значении нуля вы можете использовать атрибут xsi:nil :

const string xsiNs = "http://www.w3.org/2001/XMLSchema-instance"; 
using (XmlWriter writer = XmlWriter.Create(Console.Out)) 
{ 
    writer.WriteStartElement("Test"); 
    writer.WriteAttributeString("xsi", "nil", xsiNs, "true"); 
    writer.WriteEndElement(); 
} 

который приведет к <Test xsi:nil="true"/>

+0

+1 для воспитания xsi: ноль. Я, вероятно, не буду использовать его во внутренней сериализации, но я определенно использовал его как часть более формального внешнего обмена данными, особенно над SOAP. –

+0

Я думаю, что все в порядке. Но как я могу прочитать это как значение или нуль? –

+0

Вам понадобится специальный десериализатор, который звучит как слишком много проблем. –