XSockets 5.8.0-preview001 Deserialization problem

41 views
Skip to first unread message

Gary Cuthbert

unread,
Apr 22, 2021, 9:56:14 AM4/22/21
to XSockets.NET Developer forum
Hello, I am currently looking at migrating from a (very) old version of XSockets (v3) to the latest .net core version using the 5.8.0-preview001 version available on nuget.

In our original code we define our IXSocketJsonSerializer and use the service stack json parser to provide the implementations behind the interface, we also add our own JsonConverter specialization to handle enums by decoding to the string form rather than integer.

If I use the default IXSocketJsonSerializer implementation my controller appears to function but predictably it sends any enum values embedded in any json objects as integer values.

If I use our implementation of IXSocketJsonSerializer it fails to fully decode to the 'XSockets.Core.XSocket.Model.Message' type from the json: 

"{\"C\":\"catxsocketsmain\",\"T\":\"1\"}", 

the object decode output is:

 {{"Id":0,"QoS":0,"Retain":false,"Blob":null,"Data":null,"MessageType":0,"Controller":null,"Topic":null}}

which, as far as I can tell, has failed to convert the "C" property of the json to "Controller" and the "T" property to "Topic"? the "T" value of 1 may also be an integer interpretation of a Topic name defined as an enumeration but I am not sure.

My first attempt as resolving this was to use the newtonsoft json parser to provide the overrides in our implementation of IXSocketJsonSerializer, (I also tried the .net System.Text.Json api)  e.g.

public T DeserializeFromString<T>(string json)
{
            // ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(json);
            //JsonConvert.DeserializeObject<T>(json);
            var objT = JsonSerializer.Deserialize<T>(json);
            return objT;
}

but all return the same object decode of:
{{"Id":0,"QoS":0,"Retain":false,"Blob":null,"Data":null,"MessageType":0,"Controller":null,"Topic":null}}

The calling code attempts to use the Controller member of the object which then fails as it is null.

Is there special handling of the 'XSockets.Core.XSocket.Model.Message' type in the default implementation of IXSocketJsonSerializer that I could add to my overrdidden implementation? 

or is there a way to add our enum conversion (JsonConverter specialization) to the default implementation of IXSocketJsonSerializer? 

We may still have to use the service stack json decoder, I haven't got that far yet so if possible I would rather try to get our custom IXSocketJsonSerializer implementation  working however this is just a proof of concept exercise at the moment so if i can get something that runs successfully with either approach that would be great.

Thanks for reading
Gary

Gary Cuthbert

unread,
Apr 22, 2021, 11:28:23 AM4/22/21
to XSockets.NET Developer forum
I have been debugging in both scenarios and I can see that in the situation where the default IXSocketJsonSerializer is used the same string is successfully converted to a Message with the Controller member set to "catxsocketsmain" and Topic set to "1" so it looks like it is a problem with my implementation of IXSocketJsonSerializer which decodes these to null. 
Can anyone provide an example of a simple override of IXSocketJsonSerializer for v5.8.0? 

I notice the interface has changed slightly from earlier versions (dropped the 'IDictionary<string, string> DeserializeFromString' method and added the methods object[] IXSocketJsonSerializer.DeserializeFromString and  bool IXSocketJsonSerializer.IsValidJson methods). I have added default 'not implemented' versions of the new methods but they are not being called in this scenario so I do not believe this is the issue. 

It looks like our original override of IXSocketJsonSerializer was very similar to the example given on the v4 api documentation.

Gary Cuthbert

unread,
Apr 23, 2021, 10:33:49 AM4/23/21
to XSockets.NET Developer forum
Got a bit further, if I define a new ISocketJsonSerializer using the NewtonSoft JsonConvert class as a basis and define a custom converter for the XSockets Message class i can intercept the read of the json string e.g.

public class XsMessageJsonConverter : JsonConverter<Message>
{
     public override bool CanWrite => false;

    public override Message ReadJson(JsonReader reader, Type objectType, Message existingValue, bool hasExistingValue,
            JsonSerializer serializer)
        {
            var jo = JObject.Load(reader);
            var targetObj = Activator.CreateInstance(typeof(Message));

            foreach (var prop in objectType.GetProperties()
                .Where(p => p.CanRead && p.CanWrite))
            {
                var att = prop.GetCustomAttributes(true).OfType<JsonPropertyAttribute>().FirstOrDefault();
                var jsonPath = (att != null ? att.PropertyName : prop.Name);
                JToken token;

                if (jsonPath == "Controller")
                {
                    token = jo.SelectToken("C");
                } else if (jsonPath == "Topic")
                {
                    token = jo.SelectToken("T");
                }
                else
                {
                    token = jo.SelectToken(jsonPath);
                }

                if (token != null && token.Type != JTokenType.Null)
                {
                    var value = token.ToObject(prop.PropertyType, serializer);
                    prop.SetValue(targetObj, value, null);
                }
            }

            return targetObj as Message;
        }

        public override void WriteJson(JsonWriter writer, Message value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
}

I can then include this in my custom serializer:

[Export(typeof(IXSocketJsonSerializer))]
public class CustomSerializer : IXSocketJsonSerializer
{
        readonly JsonSerializerSettings _settings = new JsonSerializerSettings();

        public CustomSerializer()
        {
            _settings.NullValueHandling = NullValueHandling.Ignore;
            _settings.Converters.Add(new XsMessageJsonConverter());
        }

        // other methods omitted for bervity
        public T DeserializeFromString<T>(string json)
        {
            var objT = JsonConvert.DeserializeObject<T>(json, _settings);
            return objT;
        }

        public object DeserializeFromString(string json, Type type)
        {
            var obj = JsonConvert.DeserializeObject(json, type, _settings);
            return obj;
        }
       
}

Now with the above in place my server side controller code successfully decodes the string:
"{\"C\":\"catxsocketsmain\",\"T\":\"1\"}"
To a Message object with its Controller and Topic properties decoded to 'catsocketsmain' and '1' respectively.

Of course as I have not yet attempted to implement the write operation when Message is serialized to Json the full names are used e.g.
{
"Id":0,
"QoS":"FireAndForget",
"Retain":false,
"Data":"{"OrderNumber":1),
"MessageType":"Text",
"Controller":"catxsocketsmain",
"Topic":"catgeneralsocket"
}

which causes problems on the client.


In another approach I have been able to add the attribute:
 [JsonConverter(typeof(StringEnumConverter))] to the enumerated type I rely on client side to be a string, with this in place I can get further by just using the native XSocket IXSocketJsonSerializer implementation, 

I have other problems to investigate but i suspect sooner or later I will need to use the ServiceStack json serializer for some of our data payloads so inevitably i will need a custom implementation of IXSocketJsonSerializer.

Is the default implementation of this interface using a custom serializer to read and write the Message class? If anyone can provide a working example of a custom IXSocketJsonSerializer for the v5.8.0 preview it would be greatly appreciated.

thanks for reading
Gary

Gary Cuthbert

unread,
Apr 27, 2021, 5:03:23 AM4/27/21
to XSockets.NET Developer forum
By implementing an override of Newtonsofts DefaultContractResolver and adding it to the JsonSerializerSettings ContractResolver property i can now serialize the Message class to json in the form that the client expects. 

I am now seeing differences between the controller object created by the client using this custom IXSocketJsonSerializer approach c/w the default  IXSocketJsonSerializer implementation around _controllerId and PersistentId which looks like it may be down to the serialization of 'clientInfo' data sent within the message.D parameter which may also require some custom serialization/deserialization logic.

I can see that the XSockets.Core.XSocket.Model class 'ClientInfo' has properties such as 'ConnectionId' which the client expects to be 'CI' so I will look at adding serializer overrides for ClientInfo to cover this.

Reply all
Reply to author
Forward
0 new messages