Serializzatore JSON ServiceStack: come posso modificare il serializzatore predefinito a livello globale?

Aug 21 2020

Ho un caso in cui il serializzatore JSON di ServiceStack non riesce a deserializzare e in cui JSON.NET di Newtonsoft riesce a farlo. Non ho trovato un modo chiaro per sostituire il serializzatore predefinito con JSON.NET, quindi è globale e per tutte le classi.

JsConfig<T>.SerializeFnImmagino funzioni, ma è per classe e voglio per tutte le classi, a livello globale.

Come lo faccio?

Sfondo

Sto usando ServiceStack.Messaging.Redise Redis come soluzione MQ. Utilizziamo questo approccio per l'invio di messaggi:

using (var mqClient = MqClientFactory.Instance.CreateMessageQueueClient())
{

    var uniqueCallbackQ = "mq:c1" + ":" + Guid.NewGuid().ToString("N");
    var clientMsg = new Message<TRequest>(request)
    {
        ReplyTo = uniqueCallbackQ
    };

    mqClient.Publish(clientMsg);
    //Blocks thread on client until reply message is received
    responseMessage = mqClient.Get<TResponse>(uniqueCallbackQ, timeout);
}

e per ricevere messaggi, roba standard:

redisMqServer.RegisterHandler<TheRequest>(base.ExecuteMessage);

Semplificato, ho una classe Request che contiene un public object Result {get; set;}e questa proprietà è deserializzata in ServiceStack come Dictionary<string, object>e non nel tipo corretto.

Sono ben consapevole della raccomandazione di utilizzare oggetti fortemente tipizzati, ma il mio problema è che in realtà non abbiamo a che fare con DTO puliti per la comunicazione esterna, ma comunicazione interna di oggetti interni, facendo molto affidamento sul polimorfismo e sul suo codice legacy, non stiamo creando nuovi roba adesso.

Quindi, in alcuni casi, abbiamo le proprietà "oggetto" e non funziona per deserializzarlo con ServiceStack. Le informazioni sul tipo sono presenti, quindi ho pensato che avrebbe funzionato, ma non è stato così (ho rimosso gli spazi dei nomi di seguito per chiarezza):

string ssJson = ServiceStack.Text.JsonSerializer.SerializeToString(getTextMessageTemplateObject);

// above produces: 

{
    "_Type": "Deviation",
    "Result": [{
            "__type": "TextMessageTemplate, AlfaCommons",
            "_Message": "test",
            "_Type": "Deviation",
        }
    ],
    "Success": true,
}

// And then, deserialize the json above:

GetTextMessageTemplate ssGetTextMessageTemplate = ServiceStack.Text.JsonSerializer.DeserializeFromString< 
GetTextMessageTemplate>(ssJson);

Questo è ssGetTextMessageTemplate(si prega di notare che ho rimosso alcune proprietà per chiarezza):

Il prop Result non è stato deserializzato correttamente, come si può vedere sopra.

Quando si utilizza JSON.NET, il JSON serializzato è:

{
    "$type": "GetTextMessageTemplate, AlfaCommons",
    "_Type": 4,
    "Result": {
        "$type": "System.Collections.Generic.List`1[[TextMessageTemplate, AlfaCommons]], System.Private.CoreLib",
        "$values": [{
                "$type": "TextMessageTemplate, AlfaCommons",
                "_Message": "test",
                "_Type": 4,
            }
        ]
    }
}

e dopo la deserializzazione, funziona come previsto:

Quindi, mi piacerebbe provare invece il serializzatore JSON.NET nel caso sopra.

Risposte

mythz Aug 21 2020 at 17:18

ServiceStack.Redis ha una forte dipendenza da ServiceStack.Text per la serializzazione degli oggetti, non supporta la sostituzione globale per utilizzare un Serailizer JSON alternativo.

Una soluzione alternativa per l'invio di payload arbitrari nei messaggi consiste nell'inviare un JSON serializzato in una string Resultproprietà, che è possibile utilizzare JSON.NET per de/serializzare.

Un'altra potenziale soluzione consiste nell'utilizzare FromObjectDictionary Reflection Utils per riconvertire il dizionario degli oggetti nel DTO digitato, ad esempio:

var result = objDictionary.FromObjectDictionary(theResultType);