TimeSpan을 XML로 직렬화하는 방법


206

.NET TimeSpan객체를 XML 로 직렬화하려고하는데 작동하지 않습니다. 빠른 구글 TimeSpan은 직렬화가 가능하지만XmlCustomFormatterTimeSpan 객체를 XML 로 변환 하거나 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을 직렬화하는 더 좋은 방법이 있습니까?


4
아래의 Rory MacLeod의 답변은 실제로 Microsoft가 권장하는 방식입니다.
Jeff

2
XML의 지속 시간 유형이 정확히 일치하므로 TimeSpand에 긴 틱을 사용하지 않습니다. 이 문제는 2008 년에 Microsoft에 제기되었지만 해결 된 적이 없습니다. 다시 문서화 된 해결 방법이 있습니다 : kennethxu.blogspot.com/2008/09/...은
케네스 쑤

답변:


71

이미 게시 한 방법이 가장 깨끗할 것입니다. 추가 속성이 마음에 들지 않으면을 구현할 수 IXmlSerializable있지만 모든 작업을 수행해야합니다. 하므로 요점을 크게 잃습니다. 나는 당신이 게시 한 접근법을 행복하게 사용합니다. (예를 들어) 효율적 (복잡한 구문 분석 등이 아님), 문화 독립적, 명확하고 타임 스탬프 유형 숫자는 쉽고 일반적으로 이해됩니다.

제쳐두고, 나는 종종 다음을 추가합니다 :

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]

혼란을 피하기 위해 UI와 dll 참조에서 숨길 수 있습니다.


5
MyClass에서 구현하는 대신 System.TimeSpan을 래핑하는 구조체에서 인터페이스를 구현 하면 모든 것이 그렇게 나쁘지 않습니다. 그런 다음 MyClass.TimeSinceLastEvent 속성의 유형 만 변경하면됩니다
phoog

103

이것은 질문에서 제안한 접근 방식의 약간의 수정일 뿐이지 만이 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>

또는 DataContractSerializerTimeSpan을 지원합니다.


15
XmlConvert.ToTimeSpan ()의 경우 +1 PT2H15M과 같은 시간 범위에 대한 ISO 표준 기간 구문을 처리합니다. en.wikipedia.org/wiki/ISO_8601#Durations
yzorg

2
내가 틀렸다면 바로 잡으십시오. 그러나 세로 화 된 TimeSpan "PT2M45S"는 2:45:00이 아니라 00:02:45입니다.
Tom Pažourek

연결 링크가 끊어졌을 수 있습니다. connect.microsoft.com/VisualStudio/feedback/details/684819/… ? 이 기술은 또한 약간 다르게 보인다.
TJB

후속해야 할 이상한 질문입니다.이 값 PT2M45S를 SQL에서 Time으로 역 직렬화하는 방법이 있습니까?
Xander

28

경우에 따라 작동 할 수있는 것은 공용 속성에 백업 필드 (TimeSpan)를 제공하는 것이지만 공용 속성은 문자열로 노출됩니다.

예 :

protected TimeSpan myTimeout;
public string MyTimeout 
{ 
    get { return myTimeout.ToString(); } 
    set { myTimeout = TimeSpan.Parse(value); }
}

속성 값이 포함 클래스 또는 상속 클래스와 함께 주로 사용되고 XML 구성에서로드 된 경우에는 괜찮습니다.

제안 된 다른 솔루션은 공용 속성을 다른 클래스에 유용한 TimeSpan 값으로 사용하려는 경우 더 좋습니다.


지금까지 가장 쉬운 솔루션입니다. 나는 똑같은 것을 생각해 냈고 그것은 매력처럼 작동합니다. 구현하고 이해하기 쉽습니다.
wpfwannabe

1
이것이 가장 좋은 해결책입니다. 그것은 아주 좋은 직렬화! 친구를 입력 해 주셔서 감사합니다!
개발자

25

의 응답을 결합 컬러 직렬화이 원래의 솔루션을 (그 자체로 큰 인) 나는이 솔루션을 가지고 :

[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); }
    }
}

이 문제를 해결하는 가장 쉽고 쉬운 방법 ... 나를 위해
Moraru Viorel

이것은 절대적으로 독창적입니다-나는 매우 감동했습니다!
Jim

9

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>

XmlAttribute로 출력을 만드는 방법에 대한 아이디어가 있습니까?
ala

@ala, 귀하의 질문을 올바르게 이해하면 대답은 속성으로 표현하려는 속성에 XmlAttributeAttribute를 적용하는 것입니다. 물론 TimeSpan에만 국한된 것은 아닙니다.
phoog

+1 멋지다. 단, 문자열로 직렬화하지 않고 Ticks길게 하는 것을 제외하고 .
ChrisWue

@ChrisWue 사무실에서는 사람이 읽을 수있는 출력을 원할 때 xml 직렬화를 사용합니다. 시간 범위를 길게 직렬화하는 것은 해당 목표와 호환되지 않습니다. 물론 다른 이유로 XML 직렬화를 사용하는 경우 눈금을 직렬화하는 것이 더 합리적 일 수 있습니다.
phoog

8

보다 읽기 쉬운 옵션은 문자열 TimeSpan.Parse로 직렬화 하고 메소드를 사용하여 직렬화 해제하는 것입니다. 예와 동일하지만 TimeSpan.ToString()getter 및 TimeSpan.Parse(value)setter에서 사용할 수 있습니다.


2

또 다른 옵션은 SoapFormatter클래스가 아닌 클래스를 사용하여 직렬화하는 것 XmlSerializer입니다.

결과 XML 파일은 약간 다른 "SOAP"접두어 태그 등으로 보이지만 그렇게 할 수는 있습니다.

다음 SoapFormatter은 20 시간 28 분의 시간 범위를 직렬화 한 것입니다.

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

SOAPFormatter 클래스를 사용하려면 System.Runtime.Serialization.Formatters.Soap동일한 이름의 네임 스페이스에 대한 참조를 추가 하고 사용해야합니다.


이 .NET 4.0 직렬화하는 방법입니다
커크 Broadhurst

1

내 솔루션 버전 :)

[DataMember, XmlIgnore]
public TimeSpan MyTimeoutValue { get; set; }
[DataMember]
public string MyTimeout
{
    get { return MyTimeoutValue.ToString(); }
    set { MyTimeoutValue = TimeSpan.Parse(value); }
}

편집 : 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;
    }
}

1

Timespan은 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를 사용하는 까다로운 코드가 있습니다.


1
해당 링크에서 관련 정보를 인용하십시오. 답변에 필요한 모든 정보가이 사이트에 있어야하며 해당 링크를 출처로 인용 할 수 있습니다.
SuperBiasedMan

0

데이터 계약 직렬화에는 다음을 사용합니다.

  • 직렬화 된 속성을 비공개로 유지하면 공용 인터페이스가 깨끗하게 유지됩니다.
  • 직렬화에 공용 속성 이름을 사용하면 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

0

해결 방법을 원하지 않으면 System.Runtime.Serialization.dll의 DataContractSerializer 클래스를 사용하십시오.

        using (var fs = new FileStream("file.xml", FileMode.Create))
        {
            var serializer = new DataContractSerializer(typeof(List<SomeType>));
            serializer.WriteObject(fs, _items);
        }

-2

이 시도 :

//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); }
        }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.