3 Stimmen

Wie gibt man ein DTO mit einer TimeZoneInfo-Eigenschaft in ServiceStack in JSON zurück

Ich habe diese kleine ServiceStack-Nachricht:

[Route("/server/time", "GET")]
public class ServerTime : IReturn
{
    public DateTimeOffset DateTime { get; set; }
    public TimeZoneInfo TimeZone { get; set; }
}

Und der entsprechende Service-Handler sieht wie folgt aus:

public object Get(ServerTime request)
{
    return new ServerTime
    {
        DateTime = DateTimeOffset.Now,
        TimeZone = TimeZoneInfo.Local,
    };
}

Der Client-Testcode sieht so aus:

var client = new JsonServiceClient("http://localhost:54146/");
var response = client.Get(new ServerTime());

Aber response.TimeZoneInfo ist IMMER leer...

Auch die Metadaten für den Service (JSON) zeigen es nicht an:

(Beispiel-Anfrage in der JSON-Metadaten-Seite)

POST /json/reply/ServerTime HTTP/1.1 
Host: localhost 
Content-Type: application/json
Content-Length: length

{"DateTime":"\/Date(-62135596800000)\/"}

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: length

{"DateTime":"\/Date(-62135596800000)\/"}

Die XML- und CSV-Formate hingegen scheinen es richtig zu verarbeiten:

POST /xml/reply/ServerTime HTTP/1.1 
Host: localhost 
Content-Type: application/xml
Content-Length: length

    0001-01-01T00:00:00Z
    0

HTTP/1.1 200 OK
Content-Type: application/xml
Content-Length: length

    0001-01-01T00:00:00Z
    0

Warum frage ich das, anstatt den "XML-Client" zu verwenden?

Das geht um die Konsistenz. Wenn die API nicht über alle möglichen Clients konsistent ist, kann man sich nicht darauf verlassen! Ich müsste entweder den JSON-Formatter fallen lassen (was ich nicht tun kann, weil ich ihn in JavaScript verwenden möchte) oder ich müsste die vielen TimeZoneInfo-Felder einzeln aufteilen... oder einen Weg finden, damit der JSON-Serializer damit umgehen kann!

Und tatsächlich funktioniert auch XML nicht. Der XmlServiceClient gibt mir diesen Fehler:

{"Fehler in Zeile 1 Position 290. Element ':AdjustmentRules' enthält Daten von einem Typ, der dem Namen 'http://schemas.datacontract.org/2004/07/System:ArrayOfTimeZoneInfo.AdjustmentRule' entspricht. Der Deserialisierer hat keine Kenntnis von einem Typ, der diesem Namen entspricht. Erwägen Sie die Verwendung eines DataContractResolver oder fügen Sie den Typen, der 'ArrayOfTimeZoneInfo.AdjustmentRule' entspricht, zur Liste der bekannten Typen hinzu - zum Beispiel, indem Sie das KnownTypeAttribute-Attribut verwenden oder ihn zur Liste der bekannten Typen hinzufügen, die an DataContractSerializer übergeben wird."}

Weiß jemand, warum es nicht standardmäßig behandelt wird?

3voto

Loudenvier Punkte 7957

Es scheint keine elegante Möglichkeit zu geben, TimeZoneInfo an den Client zu übergeben, daher habe ich einfach ein DTO dafür erstellt, das passenderweise TimeZoneInformation genannt wird.

[Serializable]
public class TimeZoneInformation
{
    public string Id { get; set; }
    public TimeSpan BaseUtcOffset { get; set; }
    public string DisplayName { get; set; }
    public string DaylightName { get; set; }
    public string StandardName { get; set; }
    public bool SupportsDST { get; set; }
    public static implicit operator TimeZoneInformation(TimeZoneInfo source)
    {
        return new TimeZoneInformation
        {
            Id = source.Id,
            DisplayName = source.DisplayName,
            BaseUtcOffset = source.BaseUtcOffset,
            DaylightName = source.DaylightName,
            StandardName = source.StandardName,
            SupportsDST = source.SupportsDaylightSavingTime,
        };
    }
    public static implicit operator TimeZoneInfo(TimeZoneInformation source)
    {
        return TimeZoneInfo.FindSystemTimeZoneById(source.Id);
    }
}

Und meine Service-Methode sieht jetzt so aus:

public object Get(ServerTime request)
{
    return new ServerTime
    {
        DateTime = DateTimeOffset.Now,
        TimeZoneInfo = TimeZoneInfo.Local,
    };
}

Und die Metadaten für den Aufruf sind:

POST /json/reply/ServerTime HTTP/1.1 
Host: localhost 
Content-Type: application/json
Content-Length: length

{"DateTime":"\/Date(-62135596800000)\/","TimeZoneInfo":{"Id":"String","BaseUtcOffset":"PT0S","DisplayName":"String","DaylightName":"String","StandardName":"String","SupportsDST":false}}

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: length

{"DateTime":"\/Date(-62135596800000)\/","TimeZoneInfo":{"Id":"String","BaseUtcOffset":"PT0S","DisplayName":"String","DaylightName":"String","StandardName":"String","SupportsDST":false}}

Ein tatsächlicher Aufruf der Methode liefert dieses JSON zurück:

{"DateTime":"\/Date(1371925432883-0300)\/","TimeZoneInfo":{"Id":"E. South America Standard Time","BaseUtcOffset":"-PT3H","DisplayName":"(UTC-03:00) Brasília","DaylightName":"Horário brasileiro de verão","StandardName":"Hora oficial do Brasil","SupportsDST":true}}

Und XML:

2013-06-22T21:24:22.2741641Z-180<_x003C_BaseUtcOffset_x003E_k__BackingField>-PT3H<_x003C_DaylightName_x003E_k__BackingField>Horário brasileiro de verão<_x003C_DisplayName_x003E_k__BackingField>(UTC-03:00) Brasília<_x003C_Id_x003E_k__BackingField>E. South America Standard Time<_x003C_StandardName_x003E_k__BackingField>Hora oficial do Brasil<_x003C_SupportsDST_x003E_k__BackingField>true

Und alle typisierten Clients können es perfekt deserialisieren.

Ich bin nicht zu 100% zufrieden, aber so glücklich wie möglich damit...

Manche fragen mich, warum ich nur die ID sende. Die Zeitzonen-ID ist spezifisch für Windows. Da ich mit unterschiedlichen Clients sprechen muss, kann ich nicht nur eine ID senden und hoffen, dass sie die Server-Zeitzonendaten entsprechend rekonstruieren können. Ich sende bereits den OFFSET auf dem DateTimeOffset, der an die Clients zurückgegeben wird, aber für einige Szenarien ist dies nicht ausreichend, da der Offset allein nicht ausreicht, um die Zeitzone zu bestimmen oder ob die Sommerzeit in Kraft ist. Daher war das Senden aller Informationen, die ein Client benötigen könnte, um das Datum und die Uhrzeit des Servers korrekt zu interpretieren, die beste Lösung für diese spezifische Anwendung.

2voto

Mike Mertsock Punkte 11600

Es gibt ein bekanntes Problem beim Serialisieren von TimeZoneInfo in XML: siehe diese Frage zum Serialisieren des Objekts in WCF und die verlinkte MSDN-Diskussion mit Workaround. Nicht sicher, ob der Workaround für ServiceStack geeignet ist.

Aber ich würde vorschlagen, das Problem ganz zu umgehen, indem man nur die Id oder eine andere einfache Kennung für die Zeitzone sendet.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X