Принимаем и парсим все

  • Михаил
  • 8 мин. на прочтение
  • 9
  • 05 Feb 2026
  • 05 Feb 2026

Чтобы обрабатывать сообщения с разными JSON-структурами в NetCore-приложении, принимаемые из RabbitMQ, можно использовать несколько подходов. Вот наиболее распространённые и эффективные решения:


1. Использование System.Text.Json с динамическим парсингом

Если структуры сообщений заранее неизвестны или их много, можно парсить JSON в динамический объект (JsonDocument или JsonElement), а затем анализировать его содержимое.

Пример:

using System.Text.Json;
public void ProcessMessage(string message)
{
   using JsonDocument document = JsonDocument.Parse(message);
   JsonElement root = document.RootElement;
   // Проверяем наличие ключей и обрабатываем в зависимости от структуры
   if (root.TryGetProperty("type", out JsonElement typeElement))
   {
       string type = typeElement.GetString();
       switch (type)
       {
           case "Order":
               // Парсим как Order
               var order = JsonSerializer.Deserialize<Order>(message);
               ProcessOrder(order);
               break;
           case "User":
               // Парсим как User
               var user = JsonSerializer.Deserialize<User>(message);
               ProcessUser(user);
               break;
           default:
               Console.WriteLine($"Unknown message type: {type}");
               break;
       }
   }
   else
   {
       Console.WriteLine("Message has no 'type' field.");
   }
}

2. Использование полиморфного десериализатора

Если сообщения имеют общий дискриминатор (например, поле type), можно использовать полиморфный десериализатор из System.Text.Json или библиотек вроде Newtonsoft.Json.

Пример с System.Text.Json:

public interface IMessage { }
public class Order : IMessage
{
   public string Id { get; set; }
   // ...
}
public class User : IMessage
{
   public string Name { get; set; }
   // ...
}
// Настройка десериализатора
var options = new JsonSerializerOptions
{
   Converters = { new MessageConverter() }
};
var message = JsonSerializer.Deserialize<IMessage>(jsonString, options);
// Реализация MessageConverter
public class MessageConverter : JsonConverter<IMessage>
{
   public override IMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
   {
       using JsonDocument doc = JsonDocument.ParseValue(ref reader);
       if (doc.RootElement.TryGetProperty("type", out JsonElement type))
       {
           return type.GetString() switch
           {
               "Order" => JsonSerializer.Deserialize<Order>(doc.RootElement.GetRawText()),
               "User" => JsonSerializer.Deserialize<User>(doc.RootElement.GetRawText()),
               _ => throw new JsonException($"Unknown message type: {type}")
           };
       }
       throw new JsonException("Message has no 'type' field.");
   }
   public override void Write(Utf8JsonWriter writer, IMessage value, JsonSerializerOptions options)
   {
       JsonSerializer.Serialize(writer, value, value.GetType(), options);
   }
}

3. Использование Newtonsoft.Json

Если вы используете Newtonsoft.Json, можно воспользоваться атрибутом [JsonConverter] и реализовать кастомный конвертер.

Пример:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class MessageConverter : JsonConverter
{
   public override bool CanConvert(Type objectType) => true;
   public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
   {
       JObject jo = JObject.Load(reader);
       if (jo["type"] != null)
       {
           switch (jo["type"].Value<string>())
           {
               case "Order":
                   return jo.ToObject<Order>();
               case "User":
                   return jo.ToObject<User>();
               default:
                   throw new JsonException($"Unknown message type: {jo["type"]}");
           }
       }
       throw new JsonException("Message has no 'type' field.");
   }
   public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
   {
       serializer.Serialize(writer, value);
   }
}
// Использование
var settings = new JsonSerializerSettings
{
   Converters = { new MessageConverter() }
};
var message = JsonConvert.DeserializeObject<IMessage>(jsonString, settings);

4. Обработка через try-catch

Если структуры сообщений известны, но не имеют общего дискриминатора, можно попробовать десериализовать в разные классы по очереди.

Пример:

public void ProcessMessage(string message)
{
   try
   {
       var order = JsonSerializer.Deserialize<Order>(message);
       ProcessOrder(order);
   }
   catch (JsonException)
   {
       try
       {
           var user = JsonSerializer.Deserialize<User>(message);
           ProcessUser(user);
       }
       catch (JsonException)
       {
           Console.WriteLine("Unknown message format.");
       }
   }
}

Рекомендации:

  • Дискриминатор: Если возможно, добавьте поле type или аналогичное в JSON-сообщения, чтобы упростить маршрутизацию.
  • Логирование: Всегда логируйте ошибки парсинга для отладки.
  • Производительность: Для высоконагруженных систем предпочтите System.Text.Json вместо Newtonsoft.Json.