TimeSpan을 XML로 직렬화하는 방법
.NET TimeSpan
객체를 XML 로 직렬화하려고하는데 작동하지 않습니다. 빠른 구글 TimeSpan
은 직렬화 가능 하지만 객체를 XML 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); }
}
}
이것이 내 간단한 테스트에서 작동하는 것처럼 보이지만 이것이 이것을 달성하는 가장 좋은 방법입니까?
XML과 TimeSpan을 직렬화하는 더 좋은 방법이 있습니까?
이미 게시 한 방법이 가장 깨끗할 것입니다. 추가 속성이 마음에 들지 않으면 구현할 수 IXmlSerializable
있지만 모든 것을 수행해야 하므로 요점을 크게 잃습니다. 나는 당신이 게시 한 접근법을 행복하게 사용합니다. (예를 들어) 효율적 (복잡한 구문 분석 등이 아님), 문화 독립적, 명확하고 타임 스탬프 유형 숫자는 쉽고 일반적으로 이해됩니다.
제쳐두고, 나는 종종 다음을 추가합니다 :
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
혼란을 피하기 위해 UI와 dll 참조에서 숨길 수 있습니다.
이것은 질문에서 제안한 접근 방식의 약간의 수정일 뿐이지 만이 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을 지원합니다.
경우에 따라 작동 할 수있는 것은 공용 속성에 백업 필드 (TimeSpan)를 제공하는 것이지만 공용 속성은 문자열로 노출됩니다.
예 :
protected TimeSpan myTimeout;
public string MyTimeout
{
get { return myTimeout.ToString(); }
set { myTimeout = TimeSpan.Parse(value); }
}
속성 값이 포함 클래스 또는 상속 클래스와 함께 주로 사용되고 XML 구성에서로드 된 경우에는 괜찮습니다.
제안 된 다른 솔루션은 공용 속성을 다른 클래스에 유용한 TimeSpan 값으로 사용하려는 경우 더 좋습니다.
의 응답을 결합 컬러 직렬화 및 이 원래의 솔루션을 (그 자체로 큰 인) 나는이 솔루션을 가지고 :
[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); }
}
}
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>
A more readable option would be to serialize as a string and use the TimeSpan.Parse
method to deserialize it. You could do the same as in your example but using TimeSpan.ToString()
in the getter and TimeSpan.Parse(value)
in the setter.
Another option would be to serialize it using the SoapFormatter
class rather than the XmlSerializer
class.
The resulting XML file looks a little different...some "SOAP"-prefixed tags, etc...but it can do it.
Here's what SoapFormatter
serialized a timespan of 20 hours and 28 minutes serialized to:
<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>
To use SOAPFormatter class, need to add reference to System.Runtime.Serialization.Formatters.Soap
and use the namespace of the same name.
My version of the solution :)
[DataMember, XmlIgnore]
public TimeSpan MyTimeoutValue { get; set; }
[DataMember]
public string MyTimeout
{
get { return MyTimeoutValue.ToString(); }
set { MyTimeoutValue = TimeSpan.Parse(value); }
}
Edit: assuming it is 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;
}
}
Timespan stored in xml as number of seconds, but it is easy to adopt, I hope. Timespan serialized manually (implementing 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));
}
}
}
}
There is more comprehensive example: https://bitbucket.org/njkazakov/timespan-serialization
Look at Settings.cs. And there is some tricky code to use XmlElementAttribute.
For data contract serialization I use the following.
- Keeping the serialized property private keeps the public interface clean.
- Using the public property name for serialization keeps the XML clean.
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
If you do not want any workarounds, use the DataContractSerializer class from System.Runtime.Serialization.dll.
using (var fs = new FileStream("file.xml", FileMode.Create))
{
var serializer = new DataContractSerializer(typeof(List<SomeType>));
serializer.WriteObject(fs, _items);
}
Try this :
//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); }
}
참고URL : https://stackoverflow.com/questions/637933/how-to-serialize-a-timespan-to-xml
'Programming' 카테고리의 다른 글
Java 8 스트림 : 다중 필터 및 복잡한 조건 (0) | 2020.05.08 |
---|---|
파일 이름? (0) | 2020.05.08 |
AngularJS는 단일 페이지 애플리케이션 (SPA) 전용입니까? (0) | 2020.05.08 |
Java에서 가짜 웨이크 업이 실제로 발생합니까? (0) | 2020.05.08 |
Android 앱의 GridView VS GridLayout (0) | 2020.05.08 |