Deserializing received Json-Rpc data via Websocket API

Viewed 194

I'm trying to figure out a way to create a generic Websocket JsonRpc client.

After connecting I'm starting a loop to listen for data coming from the WebSocket API and sending that data to the event as a string. I'd like to return a generic JsonRpcResponse<T> object instead but not sure how or if it's possible. Where JsonRpcResponse has an Id and Method fields as defined in the spec and T is a specific type depending on the data received.

From what I've seen there's no way to use generics here since I'd have to call another event with a non-generic type or object to pass that data along.

public event Func<string, Task>? OnDataReceived;

public async Task ConnectAsync()
{
    Uri uri = new Uri(_url);
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(3000);
    await _client.ConnectAsync(uri, cancellationTokenSource.Token);

    // Start listening for data
    await Task.Factory.StartNew(async () =>
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        while (_client.State == WebSocketState.Open && !cancellationTokenSource.IsCancellationRequested)
        {
            var receivedData = await ReadStringAsync(cancellationTokenSource.Token);
            if (OnDataReceived != null)
            {
                await OnDataReceived.Invoke(receivedData);
            }

        }
    };
}
1 Answers

How about this:

namespace GenericJsonResponse {

    /// <summary>
    /// Desired returned type
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class JsonResponse<T>  {
        public T? Data { get; set; }
        public int Id { get; set; } = 0;
        public string Method { get; set; } = string.Empty;

        /// <summary>
        /// Convert string to anew instance
        /// </summary>
        /// <param name="json"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        /// <remarks>will have to add to each sub class</remarks>
        public static T FromJson(string json) {
            throw new NotImplementedException();
        }
    }

    /// <summary>
    /// Generic socket listener / sub class per type
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GenericSocketListener<T> {
        /// <summary>
        /// exposes event with generic response 
        /// </summary>
        public event Func<string, Task<JsonResponse<T>>>? OnDataReceived;

        /// <summary>
        /// Listen to socket
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public virtual async Task Listen( CancellationToken token) {
            string data = string.Empty;
            while (! token.IsCancellationRequested) {
                // listen... and get typed result, save string to data
                JsonResponse<T> result = await OnDataReceived.Invoke(data);
            }
        }
    }

    /// <summary>
    /// Dummy poco
    /// </summary>
    public class Person {
        public string FirstName { get; set; } = string.Empty;
        public string LastName { get; set; } = string.Empty;
    }

    /// <summary>
    /// Class that will listen to people
    /// </summary>
    public class PersonSocketListener : GenericSocketListener<Person> { 
    }
}
Related