.NET Как сериализовать TimeSpan для XML (не тот же вопрос!)

Мой вопрос является продолжением Как сериализовать TimeSpan для XML

У меня есть много объектов DTO, которые передают экземпляры TimeSpan. Использование взлома, описанного в оригинальной публикации, но требует от меня повторить ту же самую часть кода в каждом DTO для каждого свойства TimeSpan.

Итак, я пришел со следующим классом-оболочкой, который отлично сериализуется XML:

#if !SILVERLIGHT
[Serializable]
#endif
[DataContract]
public class TimeSpanWrapper
{
  [DataMember(Order = 1)]
  [XmlIgnore]
  public TimeSpan Value { get; set; }

  public static implicit operator TimeSpan?(TimeSpanWrapper o)
  {
    return o == null ? default(TimeSpan?) : o.Value;
  }

  public static implicit operator TimeSpanWrapper(TimeSpan? o)
  {
    return o == null ? null : new TimeSpanWrapper { Value = o.Value };
  }

  public static implicit operator TimeSpan(TimeSpanWrapper o)
  {
    return o == null ? default(TimeSpan) : o.Value;
  }

  public static implicit operator TimeSpanWrapper(TimeSpan o)
  {
    return o == default(TimeSpan) ? null : new TimeSpanWrapper { Value = o };
  }

  [JsonIgnore]
  [XmlElement("Value")]
  [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
  public long ValueMilliSeconds
  {
    get { return Value.Ticks / 10000; }
    set { Value = new TimeSpan(value * 10000); }
  }
}

Проблема заключается в том, что создаваемый XML выглядит так:

<Duration>
  <Value>20000</Value>
</Duration>

вместо естественного

<Duration>20000</Duration>

Мой вопрос: могу ли я "съесть торт и сделать его целым"? Смысл, наслаждайтесь описанным взломом, не загромождая все DTO с тем же повторяющимся кодом и все же обладаете естественным образом выглядящим XML?

Спасибо.

Ответ 1

Измените [XmlElement("Value")] на [XmlText]. Затем, если вы сериализуете что-то вроде этого:

[Serializable]
public class TestEntity
{
    public string Name { get; set; }
    public TimeSpanWrapper Time { get; set; }
}

Вы получите XML следующим образом:

<TestEntity>
    <Name>Hello</Name>
    <Time>3723000</Time>
</TestEntity>

Ответ 2

Вам нужно будет реализовать IXmlSerializable:

[Serializable,XmlSchemaProvider("TimeSpanSchema")]
public class TimeSpanWrapper : IXmlSerializable
{
    private TimeSpan _value;

    public TimeSpanWrapper()
    {
        _value = TimeSpan.Zero;
    }

    public TimeSpanWrapper(TimeSpan value)
    {
        _value = value;
    }

    public XmlSchema GetSchema() {
        return null;
    }

    public void ReadXml(XmlReader reader) {
        _value = XmlConvert.ToTimeSpan(reader.ReadElementContentAsString());
    }

    public void WriteXml(XmlWriter writer) {
        writer.WriteValue(XmlConvert.ToString(_value));
    }

    public static XmlQualifiedName TimeSpanSchema(XmlSchemaSet xs)
    {
        return new XmlQualifiedName("duration", "http://www.w3.org/2001/XMLSchema");
    }

    public static implicit operator TimeSpan?(TimeSpanWrapper o)
    {
        return o == null ? default(TimeSpan?) : o._value;
    }

    public static implicit operator TimeSpanWrapper(TimeSpan? o)
    {
        return o == null ? null : new TimeSpanWrapper { _value = o.Value };
    }

    public static implicit operator TimeSpan(TimeSpanWrapper o)
    {
        return o == null ? default(TimeSpan) : o._value;
    }

    public static implicit operator TimeSpanWrapper(TimeSpan o)
    {
        return o == default(TimeSpan) ? null : new TimeSpanWrapper { _value = o };
    }
}