Erzwingen Sie, dass XmlSerializer DateTime als 'JJJJ-MM-TT hh: mm: ss' serialisiert.

74

Ich habe ein XSD-Schema für einen RESTful-Service. In Verbindung mit dem xsd.exeTool zum Generieren von C # -Code xs:dategeneriert XSD den folgenden Code:

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

Beim Deserialisieren von XML in Objekte XmlSerializerscheint alles in Ordnung zu sein. Das Problem, mit dem ich konfrontiert bin, ist, dass der Dienst erwartet, dass Datumsangaben als formatiert werden YYYY-MM-DD hh:mm:ssund der von XSD generierte Code nur zu produzieren scheint YYYY-MM-DD.

Wenn ich XSD manuell zum xs:dateTimeTyp ändere , erzeugt der generierte C # -Code : 2010-08-20T20:07:03.915039Z.

Wie erzwinge ich grundsätzlich die Serialisierung YYYY-MM-DD hh:mm:ss? Gibt es etwas mit XSD zu tun oder kann ich etwas tun, um den generierten C # -Code zu ändern?

wpfwannabe
quelle
Dies ist ein Fehler in der XSD. Der Typ xs:datewird explizit so beschrieben, dass er sich auf ein Datum ohne den Zeitteil bezieht.
Skolima

Antworten:

145

In der Vergangenheit habe ich Folgendes getan, um die Serialisierung von Datum und Uhrzeit zu steuern:

  • Ignorieren Sie die DateTime-Eigenschaft.
  • Erstellen Sie eine Dummy-String-Eigenschaft, die die gewünschte Serialisierung / Deserialisierung durchführt

Hier ist ein Beispiel:

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); }
    }
}
kbrimington
quelle
4
+1, Ok, ich habe über eine ähnliche Lösung nachgedacht, aber ich müsste die vom Tool generierte Datei ändern, um sie [XmlIgnore]zu einer fehlerhaften Eigenschaft hinzuzufügen . Obwohl es sich um eine gute einmalige Lösung handelt, klingt es nicht gut, wenn Quell-XSD häufig mit neuen Funktionen aktualisiert wird. Ich denke, es ist vielleicht am besten, den XSD-Typ von xs:datebis zu ändern xs:stringund von dort zu übernehmen.
wpfwannabe
Ich stimme Ihrer Analyse zu. Aufgrund des Formats, das SharePoint für Daten erwartet, sind ähnliche Probleme aufgetreten. Eine alternative Lösung wäre, darauf zu bestehen, dass der Dienst mit Datumsformaten flexibler ist. Wie Sie wissen, ist dies jedoch nicht immer eine Alternative.
Kbrimington
2
[XmlElement (DataType = "date")] ist das, was Sie tatsächlich wollen! :) (siehe die Antwort unten).
Tod Thomson
3
Es [XmlElement(DataType="date")]scheint zwar "korrekt" zu sein, formatiert das XML jedoch nicht so, wie es vom OP angefordert wurde. Ich habe yyyy-MM-ddstatt yyyy-MM-dd HH:mm:ss. Selbst in .NET 4.0 ist die oben beschriebene Methode die allgemein akzeptierte Methode, um benutzerdefinierte Formatierungen auf DateTimeObjekte in XML anzuwenden .
Kbrimington
1
Dies ist eine gute Lösung, aber es ist erwähnenswert, dass der Setter erforderlich ist. Ich habe versucht, dies nur mit dem get zu tun (da dies nur für eine Ausgabesituation war), aber ohne den Setter würde die Serialisierung des SomeDateString nicht stattfinden.
Michael Murphy
64

Verwenden Sie das [XmlElement(DataType = "date")]Attribut, um Ihren DateTimeEigenschaftswert nach Bedarf zu formatieren .

Von MSDN :

Hinweis:
Das Attribut, das das Feld "Veröffentlichungsdatum" mit Anmerkungen versehen, verfügt über eine DataType-Eigenschaft. In .NET Framework gibt es keinen Typ, der vollständig dem Typ xs: date entspricht. Die engste Übereinstimmung ist System.DateTime, in der Datums- und Zeitdaten gespeichert sind. Durch Angabe der DataType-Eigenschaft als "Datum" wird sichergestellt, dass der XmlSerializer nur den Datumsteil des DateTime-Objekts serialisiert.

Bill Gates
quelle
6
Dies scheint die einfachste Lösung zu sein. [XmlElement(DataType="date")]
Fügen Sie
11
Alter Beitrag, aber um Datum und Uhrzeit zu serialisieren, wäre das richtige Attribut für Ihre Eigenschaft: [XmlElement (DataType = "dateTime")]
devHead
1
Alter Beitrag, aber Sie können auch das Gleiche tun, [XmlAttribute(DataType="date")]um als XML-Attribut anstelle eines Elements zu serialisieren
Simply Ged
Vorsichtsmaßnahme: Wird laut meinen Tests bei der Verwendung von Attributüberschreibungen DataTypevollständig ignoriert XmlSerializer. XmlAttributewird ebenfalls ignoriert.
Suncat2000
4

Wenn Sie nur den Millisekunden-Teil löschen müssen. Beziehen auf:

So schneiden Sie Millisekunden von einer .NET DateTime ab

Und im Grunde machen Sie so etwas wie:

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

Ich kann bestätigen, dass dies serialisiert wird zu:

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

Ich muss auch sagen, dass ich vor dem Löschen der Millisekunden Folgendes tue:

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

Was ich nicht weiß, ob es einen Einfluss auf die Serialisierung hat oder nicht

João Antunes
quelle
1
+1 War der Hack, den ich brauchte, um unsere Dal Lib nicht zu berühren. DateTimeKind.Unspecified löscht das 'Z'-Symbol, wenn es serialisiert wird.
Quinton Smith
@JasonCapriotti: Ich habe das getan, um die Art und Weise zu ändern, wie der Wert serialisiert wird, also hat es mit Serialisierung zu tun. Einfacher und tatsächlich das Update, das ich brauchte, um mit einem wahrscheinlich widerspenstigen PHP-Unserializer auf der anderen Seite zu interagieren
João Antunes,
3

Ich glaube, dass die Implementierung der IXmlSerializableSchnittstelle einen Trick machen wird. Sie können dann steuern, wie Sie Ihr Objekt serialisieren und deserialisieren.

Eric
quelle
1
Klingt vielversprechend, wenn es darum geht, es einfach anzuschließen, wenn alle Klassen als generiert werden partial. Auf der anderen Seite verliere ich all die guten Dinge der automatischen Serialisierung von öffentlichem Eigentum, also muss ich das Rad neu erfinden. Dies wäre kein Problem, wenn die Klasse nur eine Handvoll Eigenschaften hätte. Es hat viele von ihnen und es klingt nach einer entmutigenden Aufgabe, dies aufrechtzuerhalten, da XSD Änderungen erleidet.
wpfwannabe
1

Siehe Antworten oben, aber zum Hinzufügen: Wenn Sie nur eine Ausgabe wünschen, wenn der Wert nicht null ist (z. B. XML maxOccurs = 0), können Sie Folgendes verwenden:

private System.DateTime? someDateField;

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

Ich kann eine andere Option haben. Wenn Sie Ihre DateTime einstellen, subtrahieren Sie einfach die Anzahl der Ticks von allem nach den Sekunden, wie zum Beispiel:

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

Auf diese Weise werden beim Serialisieren Ihrer DateTime (Dt) die Millisekunden nicht verwendet und Sie haben einen Wert hh: mm: ss, das hat mir zumindest gegeben. Auf diese Weise müssen Sie nichts in Ihrer XML-Definition ändern.

philippe
quelle