2

I need to read published messages by SignalR from Redis backplane. But they have strange format for me. Example:

"\x92\x90\x81\xa4json\xc4\x83{\"type\":1,\"target\":\"ReceiveGroupMessage\",\"arguments\":[{\"senderId\":null,\"message\":\"hello\",\"sentAt\":\"2023-07-22T16:48:08.7001126Z\"}]}\x1e"

Most of content is JSON, but what about start and the end? What is this, what it means and how can I deserialize it?

I tried:

var result = MessagePackSerializer.Deserialize<object>(value);

where value is RedisValue from subscription callback. But it results with:result

so I don't know how can I extract the value from json key.

That's whole code:

public class RedisSaveMessageBackgroundService : BackgroundService
{
    private readonly IConfiguration _configuration;
    private readonly IConnectionMultiplexer _connectionMultiplexer;

    public RedisSaveMessageBackgroundService(IConfiguration configuration,
        IConnectionMultiplexer connectionMultiplexer)
    {
        _configuration = configuration;
        _connectionMultiplexer = connectionMultiplexer;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using (var connection = new NpgsqlConnection(_configuration.GetConnectionString("YugabyteDB")))
        {
            connection.Open();

            static async void Handler(RedisChannel channel, RedisValue value)
            {
                if (value == default)
                    return;

                var message = ReadMessage(value);
                await connection.ExecuteAsync(SQL_INSERT); // details omitted
            }

            var subscriber = _connectionMultiplexer.GetSubscriber();
            await subscriber.SubscribeAsync(new RedisChannel("*SignalRHub.MessagingHub:user:*", RedisChannel.PatternMode.Pattern), Handler);
            await subscriber.SubscribeAsync(new RedisChannel("*SignalRHub.MessagingHub:group:*", RedisChannel.PatternMode.Pattern), Handler);
        }

        await Task.Delay(Timeout.Infinite, stoppingToken);
    }

    private static Message ReadMessage(RedisValue value)
    {
        // What here
        throw new NotImplementedException();
    }
}
Szyszka947
  • 473
  • 2
  • 5
  • 21
  • You even tagged it: https://msgpack.org/ – Fildor Jul 22 '23 at 17:51
  • Yes because I saw `using MessagePack` in `RedisHubLifetimeManager<>` and the same tag here. While deserializing I always get exception if I want to deserialize it to something different than `object` or array of its. At least I would want to get something usable – Szyszka947 Jul 22 '23 at 18:51
  • Can you add an example of where you try to deserialize? – Fildor Jul 22 '23 at 18:53
  • I added example – Szyszka947 Jul 22 '23 at 19:20
  • I cannot experiment right now, but I think should be able to deserialize a mn object with an object property and a string property "json" and from that deserialize again? – Fildor Jul 23 '23 at 09:56
  • Did you meant something like this: https://pastebin.com/0LN1msMk ? It throws that `RedisMessage is not registered in resolver: MessagePack.Resolvers.StandardResolver` – Szyszka947 Jul 23 '23 at 10:21

2 Answers2

1

Please try to use below code.

    public IActionResult testmsg()
    {
        string input = "\x92\x90\x81\xa4json\xc4\x83{\"type\":1,\"target\":\"ReceiveGroupMessage\",\"arguments\":[{\"senderId\":null,\"message\":\"hello\",\"sentAt\":\"2023-07-22T16:48:08.7001126Z\"}]}\x1e";

        // Step 1: Remove the unwanted characters
        int startIndex = input.IndexOf('{'); // Find the starting index of the JSON part
        int endIndex = input.LastIndexOf('}'); // Find the ending index of the JSON part
        string jsonPart = input.Substring(startIndex, endIndex - startIndex + 1); // Extract the JSON part

        // Step 2: Convert the JSON to a C# object
        var result = JsonConvert.DeserializeObject<CustomMessage>(jsonPart);

        return Ok(result);
    }
    private class CustomMessage
    {
        public int type { get; set; }
        public string target { get; set; }
        public List<RedisMessage> arguments { get; set; }
    }
    private class RedisMessage
    {
        public string senderId { get; set; }
        public string message { get; set; }
        public DateTime sentAt { get; set; }
    }
Jason Pan
  • 15,263
  • 1
  • 14
  • 29
  • It should work but I'm interested in deserializing using `MessagePack`, due to it's performance and fact it's "true" way, not workaround. Or maybe I am wrong? – Szyszka947 Jul 24 '23 at 12:01
  • Hi @Szyszka947, pls share more details about how you are using in your project, if you could create a new sample project, it's easy for us to checking the issue. By the way, please don't forget to hide sensitive information. – Jason Pan Jul 24 '23 at 12:03
  • I added whole code – Szyszka947 Jul 24 '23 at 12:17
  • @Szyszka947 Please share your Program.cs file, or Startup.cs if you have. – Jason Pan Jul 24 '23 at 12:40
  • There is only `IConnectionMultiplexer` as singleton and the `RedisSaveMessageBackgroundService` as hosted service. If you mean Program.cs from SignalR hub project, its only added SignalR along with `AddStackExchangeRedis` and `app.MapHub` – Szyszka947 Jul 24 '23 at 12:46
0

The message is a MessagePack-encoded binary message that contains a JSON payload. The "\x92" and "\x90" bytes at the beginning of the message are part of the MessagePack header and indicate that the message is an array with two elements. The "\x81" byte indicates that the first element of the array is a map with one key-value pair. The key is the string "json" and the value is a MessagePack-encoded string that contains the JSON payload. The "\xc4\x83" bytes indicate that the string is 3 bytes long. for using it , you can install NuGet package MessagePack and then use MessagePackSerializer class like this :

var dictionary = MessagePackSerializer.Deserialize<Dictionary<object, object>>(yourInput);