Примусити XmlSerializer серіалізувати DateTime як 'РРРР-ММ-ДД год: мм: сс'


74

У мене є схема XSD для деякої служби RESTful. При використанні спільно з xsd.exeінструментом для генерації коду C # XSD xs:dateгенерує такий код:

[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="date")]
public System.DateTime time {
    get {
        return this.timeField;
    }
    set {
        this.timeField = value;
    }
}

При десеріалізації XML для об'єктів, що використовують XmlSerializerвсе, здається, добре. Проблема, з якою я стикаюся, полягає в тому, що служба очікує форматування дат як, YYYY-MM-DD hh:mm:ssа XSD-код, здається, видає лише YYYY-MM-DD.

Якщо я змінити XSD вручну xs:dateTimeтипу, згенерований C # код видає: 2010-08-20T20:07:03.915039Z.

В основному, як я змушую виробляти серіалізацію YYYY-MM-DD hh:mm:ss? Чи потрібно щось робити з XSD, чи я можу щось зробити, щоб змінити згенерований код C #?


Це помилка в XSD, тип xs:dateчітко описаний для посилання на дату , без часової частини!
skolima

Подивіться на stackoverflow.com/questions/101533/…
TNT

Відповіді:


145

Раніше для контролю серіалізації дати та часу я робив наступне:

  • Ігноруйте властивість DateTime.
  • Створіть фіктивну властивість рядка, яка серіалізує / десеріалізує, як я хочу

Ось приклад:

public class SomeClass
{
    [XmlIgnore]
    public DateTime SomeDate { get; set; }

    [XmlElement("SomeDate")]
    public string SomeDateString
    {
        get { return this.SomeDate.ToString("yyyy-MM-dd HH:mm:ss"); }
        set { this.SomeDate = DateTime.Parse(value); }
    }
}

4
+1, добре, я думав про подібне рішення, але це зажадає від мене модифікації створеного інструментом файлу для додавання [XmlIgnore]до властивості, що порушує. Незважаючи на те, що це гарне одноразове рішення, це не звучить добре, коли джерело XSD часто оновлюється новими функціями. Я думаю, що, можливо, було б найкраще змінити тип XSD з xs:dateна xs:stringта взяти його звідти.
wpfwannabe

Я згоден з вашим аналізом. Я стикався з подібними проблемами, оскільки формат SharePoint очікує дати. Альтернативним рішенням було б наполягати на тому, щоб служба була більш гнучкою щодо форматів дат; однак, як ви добре знаєте, це не завжди є альтернативою.
kbrimington

2
[XmlElement (DataType = "date")] - це те, що ви насправді хочете! :) (див. відповідь нижче).
Тод Томсон,

3
Хоча, [XmlElement(DataType="date")]здається, "правильний", він не форматує XML так, як вимагав OP. Я отримав yyyy-MM-ddзамість yyyy-MM-dd HH:mm:ss. Навіть у .NET 4.0, наведений вище спосіб є загальноприйнятим способом застосування нестандартного форматування до DateTimeоб’єктів у XML.
kbrimington

1
Це хороше рішення, але варто зазначити, що сетер необхідний. Я намагався зробити це лише за допомогою get (оскільки це стосувалось лише вихідних ситуацій), але без сеттера серіалізація SomeDateString не відбулася б.
Майкл Мерфі

64

Використовуйте [XmlElement(DataType = "date")]атрибут, щоб форматувати DateTimeзначення власності, як вам потрібно.

З MSDN :

Примітка:
Атрибут, який коментує поле публікації, має властивість DataType. У .NET Framework немає типу, який повністю відповідає типу xs: date. Найближчий збіг - System.DateTime, який зберігає дані про дату та час. Вказівка ​​властивості DataType як "дати" гарантує, що XmlSerializer буде серіалізувати лише частину дати об'єкта DateTime.


6
Це виглядає як найбільш просте рішення. Просто додайте атрибут [XmlElement(DataType="date")]до свого майна
MadBender

12
Стара публікація, але для серіалізації дати та часу правильним атрибутом для вашої власності буде: [XmlElement (DataType = "dateTime")]
devHead

1
Стара публікація, але ви також можете зробити те саме, [XmlAttribute(DataType="date")]щоб серіалізувати як атрибут Xml замість елемента
Simply Ged

Застереження: згідно з моїм тестуванням, DataTypeповністю ігнорується XmlSerializerпри використанні заміни атрибутів. XmlAttributeтакож ігнорується.
Suncat2000,

4

Якщо вам потрібно лише очистити мілісекундну частину. Відноситься до:

Як скоротити мілісекунди від .NET DateTime

І в основному зробити щось на зразок:

  startDateTimeToUse = startDateTimeToUse.AddTicks(-(startDateTimeToUse.Ticks % TimeSpan.TicksPerSecond));
  endDate = endDate.AddTicks(-(endDate.Ticks % TimeSpan.TicksPerSecond));

Я можу підтвердити, що це серіалізується, щоб:

            <startDate>2015-10-31T12:13:04</startDate>
            <endDate>2016-11-10T12:13:06</endDate>

Я також повинен сказати, що перед очищенням мілісекунд я роблю це:

            var startDateTimeToUse = ssStartDateTime.ToUniversalTime();
            var endDate = DateTime.Now.ToUniversalTime();
            startDateTimeToUse = DateTime.SpecifyKind(startDateTimeToUse, DateTimeKind.Unspecified);
            endDate = DateTime.SpecifyKind(endDate, DateTimeKind.Unspecified);

Що я не знаю, чи впливає це на серіалізацію чи ні на даний момент


1
+1 - це був хак, який мені потрібен, щоб уникнути дотику до нашого dal lib. DateTimeKind.Unspecified скине символ 'Z' при серіалізації.
Квінтон Сміт,

@JasonCapriotti: Я зробив це, щоб змінити спосіб серіалізації значення, тому це стосується серіалізації. Більш просте і власне виправлення, яке мені потрібно було взаємодіяти з тим, що, мабуть, було нестримним PHP-десериалізатором з іншого боку
Жоао Антунес

3

Я вважаю, що впровадження IXmlSerializableінтерфейсу зробить трюк. Потім ви можете контролювати спосіб серіалізації та десеріалізації об’єкта.


1
Звучить багатообіцяюче з точки зору можливості підключити його легко до всіх класів, що генеруються як partial. З іншого боку, я втрачаю всі добрі речі автоматичної серіалізації державного майна, тому я повинен винаходити колесо. Це не було б проблемою, якби клас мав лише кілька властивостей. Їх багато, і це звучить непросте завдання, коли доводиться підтримувати це, оскільки XSD зазнає змін.
wpfwannabe

1

дивіться відповіді вище, але додайте - якщо ви хочете вивести лише тоді, коли значення не є нульовим (наприклад, XML maxOccurs = 0), ви можете використовувати щось на зразок цього:

private System.DateTime? someDateField;

public string someDate
{
    get
    {
        return someDateField?.ToString("MM-dd-yyyy");
    }
    set
    {
        dobField = System.DateTime.Parse(value);
    }
}

0

У мене може бути інший варіант. При встановленні DateTime просто відніміть кількість галочок усього після секунд, наприклад:

    public DateTime Dt
        {
        get => _dt;
        set
            {
            _dt = value;
            long elapsedTicks = _dt.Ticks - new DateTime(_dt.Year, _dt.Month, _dt.Day, _dt.Hour, _dt.Minute, _dt.Second).Ticks;
            TimeSpan elapsedSpan = new TimeSpan(elapsedTicks);
            _dt = _dt.Subtract(elapsedSpan);
            }
        }
private DateTime _dt = default(DateTime);

Таким чином, коли ви серіалізуєте DateTime (Dt), мілісекунди не будуть використовуватися, і ви отримаєте значення hh: mm: ss, це принаймні те, що він мені дав. Таким чином не потрібно нічого змінювати всередині вашого визначення XML.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.