Search code examples
c#jsonservicestackservicestack.redisservicestack-text

ServiceStack JSON serializer: How can I change the default serializer globally?


I have a case where the ServiceStack JSON serializer fails to deserialize, and where Newtonsoft's JSON.NET manages to do so. I have not found a clear-cut way to replace the default serializer with JSON.NET, so its global and for all classes.

JsConfig<T>.SerializeFn I guess works, but it is per class, and I want for all classes, globally.

How do I do that?

Background

I am using the ServiceStack.Messaging.Redis and Redis as an MQ solution. We use this approach to sending messages:

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

and for receiving message, standard stuff:

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

Simplified, I have a Request class that contains a public object Result {get; set;}, and this property is deserialized in ServiceStack as a Dictionary<string, object>, and not to the correct Type.

I am well aware of the recommendation to use strongly typed object, but my problem is we are not actually dealing with clean DTOs for external communication, but internal communication of internal objects, relying heavily on polymorphism and it legacy code, we are not creating new stuff right now.

So, in some cases, we have the "object" properties, and it doesn't work to deserialize it with ServiceStack. The Type info is there, so I assumed it would work, but it didn't (I have removed namespaces below for clarity):

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

This is the ssGetTextMessageTemplate (please note I removed some properties for clarity):

enter image description here

The Result prop was not deserialized correctly, as can be seen above.

When using JSON.NET, the serialized JSON is:

{
    "$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,
            }
        ]
    }
}

and after deserializing, it works as expected:

enter image description here

So, I'd like to try the JSON.NET serializer in the case above instead.


Solution

  • ServiceStack.Redis has a hard dependency on ServiceStack.Text for its object Serialization, it doesn't support global substitution to use an alternative JSON Serailizer.

    One workaround for sending arbitrary payloads in messages is to send a serialized JSON in a string Result property, which you could use JSON.NET to de/serialize.

    Another potential workaround is using the FromObjectDictionary Reflection Utils to convert the Object Dictionary back into the typed DTO, e.g:

    var result = objDictionary.FromObjectDictionary(theResultType);