c# - valid - Cómo serializar un TimeSpan a XML
xml document c# (13)
Algo que puede funcionar en algunos casos es darle a su propiedad pública un campo de respaldo, que es un TimeSpan, pero la propiedad pública está expuesta como una cadena.
p.ej:
protected TimeSpan myTimeout;
public string MyTimeout
{
get { return myTimeout.ToString(); }
set { myTimeout = TimeSpan.Parse(value); }
}
Esto está bien si el valor de la propiedad se usa principalmente con la clase contenedora o las clases heredadas y se carga desde la configuración xml.
Las otras soluciones propuestas son mejores si desea que la propiedad pública sea un valor de TimeSpan utilizable para otras clases.
Estoy intentando serializar un objeto .NET TimeSpan
a XML y no está funcionando. Google ha sugerido que, si bien TimeSpan
es serializable, XmlCustomFormatter
no proporciona métodos para convertir objetos TimeSpan
desde y hacia XML.
Un enfoque sugerido fue ignorar el TimeSpan
para la serialización y, en su lugar, serializar el resultado de TimeSpan.Ticks
(y usar el new TimeSpan(ticks)
para la deserialización). Un ejemplo de esto sigue:
[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); }
}
}
Si bien esto parece funcionar en mi breve prueba, ¿es esta la mejor manera de lograr esto?
¿Hay una mejor manera de serializar un TimeSpan desde y hacia XML?
Combinando una respuesta de la serialización de color y esta solución original (que es excelente por sí misma) obtuve esta solución:
[XmlElement(Type = typeof(XmlTimeSpan))]
public TimeSpan TimeSinceLastEvent { get; set; }
donde la clase XmlTimeSpan
es así:
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); }
}
}
Esto es solo una pequeña modificación en el enfoque sugerido en la pregunta, pero este problema de Microsoft Connect recomienda usar una propiedad para la serialización como esta:
[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);
}
}
Esto serializaría un TimeSpan de 0:02:45 como:
<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>
Alternativamente, el DataContractSerializer
soporta TimeSpan.
La forma en que ya has publicado es probablemente la más limpia. Si no le gusta la propiedad adicional, podría implementar IXmlSerializable
, pero luego tiene que hacer todo , lo que en gran parte anula el punto. Me encantaría usar el enfoque que has publicado; es (y por ejemplo) eficiente (sin análisis complejo, etc.), los números de tipo cultura, independientes, no ambiguos y de marca de tiempo se entienden fácil y comúnmente.
A un lado, a menudo agrego:
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
Esto simplemente lo oculta en la interfaz de usuario y en las dlls de referencia, para evitar confusiones.
Mi versión de la solución :)
[DataMember, XmlIgnore]
public TimeSpan MyTimeoutValue { get; set; }
[DataMember]
public string MyTimeout
{
get { return MyTimeoutValue.ToString(); }
set { MyTimeoutValue = TimeSpan.Parse(value); }
}
Edición: asumiendo que es nullable ...
[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;
}
}
No puedo comentar o subir de rango, pero el comentario SoapDuration
[XmlElement, Type=SoapDuration]
public TimeSpan TimeSinceLastEvent
o
public SoapDuration TimeSinceLastEventTicks
Otra opción sería serializarlo utilizando la clase SoapFormatter
lugar de la clase XmlSerializer
.
El archivo XML resultante se ve un poco diferente ... algunas etiquetas prefijadas "SOAP", etc ... pero puede hacerlo.
SoapFormatter
es lo que SoapFormatter
serializó un intervalo de tiempo de 20 horas y 28 minutos serializados para:
<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>
Para usar la clase SOAPFormatter, debe agregar una referencia a System.Runtime.Serialization.Formatters.Soap
y usar el espacio de nombres del mismo nombre.
Para la serialización del contrato de datos utilizo lo siguiente.
- Mantener la propiedad serializada privada mantiene limpia la interfaz pública.
- El uso del nombre de propiedad pública para la serialización mantiene el XML limpio.
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
Período de tiempo almacenado en xml como número de segundos, pero es fácil de adoptar, espero. Periodo de tiempo serializado manualmente (implementando 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));
}
}
}
}
Hay un ejemplo más completo: https://bitbucket.org/njkazakov/timespan-serialization
Mira en Settings.cs. Y hay un código complicado para usar XmlElementAttribute.
Podría crear una envoltura ligera alrededor de la estructura 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());
}
}
}
Ejemplo de resultado serializado:
<Entry>
<StartTime>2010-12-06T08:45:12.5</StartTime>
<Duration>2.08:29:35.2500000</Duration>
</Entry>
Prueba esto :
//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); }
}
Si no desea ninguna solución alternativa, use la clase DataContractSerializer de System.Runtime.Serialization.dll.
using (var fs = new FileStream("file.xml", FileMode.Create))
{
var serializer = new DataContractSerializer(typeof(List<SomeType>));
serializer.WriteObject(fs, _items);
}
Una opción más legible sería serializar como una cadena y usar el método TimeSpan.Parse
para deserializarlo. Podría hacer lo mismo que en su ejemplo pero utilizando TimeSpan.ToString()
en el getter y TimeSpan.Parse(value)
en el setter.