Как сериализовать TimeSpan для XML
Я пытаюсь сериализовать объект .NET TimeSpan
в XML, и он не работает. Быстрый google предположил, что, хотя TimeSpan
является сериализуемым, XmlCustomFormatter
не предоставляет методы для преобразования объектов TimeSpan
в и из XML.
Один предложенный подход состоял в том, чтобы игнорировать TimeSpan
для сериализации и вместо этого сериализовать результат TimeSpan.Ticks
(и использовать new TimeSpan(ticks)
для десериализации). Пример этого следует:
[Serializable]
public class MyClass
{
// Local Variable
private TimeSpan m_TimeSinceLastEvent;
// Public Property - XmlIgnore as it doesn't serialize anyway
[XmlIgnore]
public TimeSpan TimeSinceLastEvent
{
get { return m_TimeSinceLastEvent; }
set { m_TimeSinceLastEvent = value; }
}
// Pretend property for serialization
[XmlElement("TimeSinceLastEvent")]
public long TimeSinceLastEventTicks
{
get { return m_TimeSinceLastEvent.Ticks; }
set { m_TimeSinceLastEvent = new TimeSpan(value); }
}
}
Пока это работает в моем кратком тестировании - это лучший способ достичь этого?
Есть ли лучший способ сериализации TimeSpan для и из XML?
Ответы
Ответ 1
То, как вы уже разместили, возможно, является самым чистым. Если вам не нравится дополнительное свойство, вы можете реализовать IXmlSerializable
, но тогда вам нужно сделать все, что в значительной степени побеждает точку. Я бы с радостью использовал подход, который вы опубликовали; это (например) эффективный (без сложного разбора и т.д.), независимые от культуры, недвусмысленные и временные числа типа легко и понятно.
В стороне, я часто добавляю:
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
Это просто скрывает его в пользовательском интерфейсе и в ссылках на DLL, чтобы избежать путаницы.
Ответ 2
Это лишь небольшая модификация подхода, предложенного в вопросе, но эта проблема с Microsoft Connect рекомендует использовать свойство для сериализации:
[XmlIgnore]
public TimeSpan TimeSinceLastEvent
{
get { return m_TimeSinceLastEvent; }
set { m_TimeSinceLastEvent = value; }
}
// XmlSerializer does not support TimeSpan, so use this property for
// serialization instead.
[Browsable(false)]
[XmlElement(DataType="duration", ElementName="TimeSinceLastEvent")]
public string TimeSinceLastEventString
{
get
{
return XmlConvert.ToString(TimeSinceLastEvent);
}
set
{
TimeSinceLastEvent = string.IsNullOrEmpty(value) ?
TimeSpan.Zero : XmlConvert.ToTimeSpan(value);
}
}
Это приведет к сериализации TimeSpan 0:02:45 как:
<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>
В качестве альтернативы DataContractSerializer
поддерживает TimeSpan.
Ответ 3
Что-то, что может работать в некоторых случаях, - предоставить вашему публичному свойству поле поддержки, которое является TimeSpan, но публичное свойство отображается как строка.
например:
protected TimeSpan myTimeout;
public string MyTimeout
{
get { return myTimeout.ToString(); }
set { myTimeout = TimeSpan.Parse(value); }
}
Это нормально, если значение свойства используется в основном в содержащем классе или наследует классы и загружается из конфигурации xml.
Другие предлагаемые решения лучше, если вы хотите, чтобы публичное свойство было полезным значением TimeSpan для других классов.
Ответ 4
Объединив ответ Сериализация цвета и это оригинальное решение (это само по себе), я получил это решение:
[XmlElement(Type = typeof(XmlTimeSpan))]
public TimeSpan TimeSinceLastEvent { get; set; }
где XmlTimeSpan
класс выглядит следующим образом:
public class XmlTimeSpan
{
private const long TICKS_PER_MS = TimeSpan.TicksPerMillisecond;
private TimeSpan m_value = TimeSpan.Zero;
public XmlTimeSpan() { }
public XmlTimeSpan(TimeSpan source) { m_value = source; }
public static implicit operator TimeSpan?(XmlTimeSpan o)
{
return o == null ? default(TimeSpan?) : o.m_value;
}
public static implicit operator XmlTimeSpan(TimeSpan? o)
{
return o == null ? null : new XmlTimeSpan(o.Value);
}
public static implicit operator TimeSpan(XmlTimeSpan o)
{
return o == null ? default(TimeSpan) : o.m_value;
}
public static implicit operator XmlTimeSpan(TimeSpan o)
{
return o == default(TimeSpan) ? null : new XmlTimeSpan(o);
}
[XmlText]
public long Default
{
get { return m_value.Ticks / TICKS_PER_MS; }
set { m_value = new TimeSpan(value * TICKS_PER_MS); }
}
}
Ответ 5
Более читаемым вариантом будет сериализация в виде строки и использование метода TimeSpan.Parse
для десериализации. Вы можете сделать то же, что и в вашем примере, но используя TimeSpan.ToString()
в getter и TimeSpan.Parse(value)
в настройщике.
Ответ 6
Вы можете создать световую оболочку вокруг структуры TimeSpan:
namespace My.XmlSerialization
{
public struct TimeSpan : IXmlSerializable
{
private System.TimeSpan _value;
public static implicit operator TimeSpan(System.TimeSpan value)
{
return new TimeSpan { _value = value };
}
public static implicit operator System.TimeSpan(TimeSpan value)
{
return value._value;
}
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
_value = System.TimeSpan.Parse(reader.ReadContentAsString());
}
public void WriteXml(XmlWriter writer)
{
writer.WriteValue(_value.ToString());
}
}
}
Пример сериализованного результата:
<Entry>
<StartTime>2010-12-06T08:45:12.5</StartTime>
<Duration>2.08:29:35.2500000</Duration>
</Entry>
Ответ 7
Другой вариант - сериализовать его, используя класс SoapFormatter
, а не класс XmlSerializer
.
Полученный XML файл выглядит немного по-другому... некоторые "SOAP" -прекрасные тэги и т.д., но он может это сделать.
Здесь SoapFormatter
сериализован временной интервал продолжительностью 20 часов и 28 минут для сериализации:
<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>
Чтобы использовать класс SOAPFormatter, нужно добавить ссылку на System.Runtime.Serialization.Formatters.Soap
и использовать пространство имен с тем же именем.
Ответ 8
Моя версия решения:)
[DataMember, XmlIgnore]
public TimeSpan MyTimeoutValue { get; set; }
[DataMember]
public string MyTimeout
{
get { return MyTimeoutValue.ToString(); }
set { MyTimeoutValue = TimeSpan.Parse(value); }
}
Изменить: при условии, что оно равно NULL...
[DataMember, XmlIgnore]
public TimeSpan? MyTimeoutValue { get; set; }
[DataMember]
public string MyTimeout
{
get
{
if (MyTimeoutValue != null)
return MyTimeoutValue.ToString();
return null;
}
set
{
TimeSpan outValue;
if (TimeSpan.TryParse(value, out outValue))
MyTimeoutValue = outValue;
else
MyTimeoutValue = null;
}
}
Ответ 9
Временной интервал, хранящийся в xml, как количество секунд, но, на мой взгляд, легко принять.
Временной интервал сериализован вручную (реализация IXmlSerializable):
public class Settings : IXmlSerializable
{
[XmlElement("IntervalInSeconds")]
public TimeSpan Interval;
public XmlSchema GetSchema()
{
return null;
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("IntervalInSeconds", ((int)Interval.TotalSeconds).ToString());
}
public void ReadXml(XmlReader reader)
{
string element = null;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
element = reader.Name;
else if (reader.NodeType == XmlNodeType.Text)
{
if (element == "IntervalInSeconds")
Interval = TimeSpan.FromSeconds(double.Parse(reader.Value.Replace(',', '.'), CultureInfo.InvariantCulture));
}
}
}
}
Существует более полный пример:
https://bitbucket.org/njkazakov/timespan-serialization
Посмотрите на Settings.cs.
И есть какой-то хитрый код для использования XmlElementAttribute.
Ответ 10
Не могу комментировать или ранжировать, но комментарий SoapDuration
[XmlElement, Type=SoapDuration]
public TimeSpan TimeSinceLastEvent
или
public SoapDuration TimeSinceLastEventTicks
Ответ 11
Для сериализации контрактов данных я использую следующее.
- Сохранение секретного свойства private сохраняет публичный интерфейс в чистоте.
- Использование имени публичной собственности для сериализации сохраняет XML в чистоте.
Public Property Duration As TimeSpan
<DataMember(Name:="Duration")>
Private Property DurationString As String
Get
Return Duration.ToString
End Get
Set(value As String)
Duration = TimeSpan.Parse(value)
End Set
End Property
Ответ 12
Если вам не нужны обходные пути, используйте класс DataContractSerializer из System.Runtime.Serialization.dll.
using (var fs = new FileStream("file.xml", FileMode.Create))
{
var serializer = new DataContractSerializer(typeof(List<SomeType>));
serializer.WriteObject(fs, _items);
}
Ответ 13
Попробуйте следующее:
//Don't Serialize Time Span object.
[XmlIgnore]
public TimeSpan m_timeSpan;
//Instead serialize (long)Ticks and instantiate Timespan at time of deserialization.
public long m_TimeSpanTicks
{
get { return m_timeSpan.Ticks; }
set { m_timeSpan = new TimeSpan(value); }
}