【问题标题】:JSON deserialization for multiple messages design pattern C#?多消息设计模式C#的JSON反序列化?
【发布时间】:2022-01-13 20:29:05
【问题描述】:

我有一个 .net 应用程序在后台运行并侦听 Kafka 消息的用例。我将收到来自 Kafka 的各种有效载荷/消息。每个有效负载具有不同的结构并包含不同的项目。对于每个有效负载,我们必须将其反序列化为自己的实体(并不总是一对一映射,在某些情况下有效负载到实体可能存在一些映射逻辑),每个实体在数据库中都有自己的表。所以,在反序列化之后,这些实体必须保存在数据库中,同时发送到另一个 Kafka 主题。

总的来说,我将整个流程分为维护 SRP 的 3 个部分。一是反序列化,二是数据库保存,三是Kafka。

我目前正在实现反序列化,如下所示:

第一个有效载荷示例:

{
   "type":"fuel",
   "data":{
      "fueltype":"petrol",
      "mileage":23.76,
      "tankcapacity":37
   }
}

{
   "type":"engine",
   "data":{
      "enginetype":"K series",
      "maxpower":88.50,
      "displacement":1197
   }
}

因此这些消息使用 type

进行区分

对于我想为每种类型使用单独的解析器的代码

public interface IJsonParser
    {
        Payload Parse(dynamic data);
    }

 public class FuelParser : IJsonParser
    {
        public Payload Parse(dynamic payload)
        {
            Fuel f = new Fuel();
            f.Mileage = (float)payload.data.mileage;
            return f;
        }
    }

    public class EngineParser : IJsonParser
    {
        public Payload Parse(dynamic data)
        {
            Engine e = new Engine();
            return e;
        }
    }



public class Payload
    {
        public int Id { get; set; }
    }

 public class Fuel : Payload
    {
        public string FuelType { get; set; }
        public float Mileage { get; set; }
        public int TankCapacity { get; set; }
    }

 public class Engine : Payload
    {
        public string EngineType { get; set; }
        public float MaxPower { get; set; }
        public int Displacement { get; set; }
    }
 


 public static class JsonParserFactory
    {
        public static IJsonParser GetJsonParser(string type)
        {
            switch (type)
            {
                case "fuel":
                    return new FuelParser();
                case "engine":
                    return new EngineParser();
                default:
                    return null;
            }
        }
    }

string message = "{\r\n\t\"type\": \"fuel\",\r\n\t\"data\":\r\n\t\t{\r\n\t\t\t\"fueltype\": \"petrol\",\r\n\t\t\t\"mileage\": 23.76,\r\n\t\t\t\"tankcapacity\": 37\r\n\t\t}\r\n}";
            dynamic data = JsonConvert.DeserializeObject<object>(message);

            IJsonParser parser = JsonParserFactory.GetJsonParser(data.type.ToString());
            var model = parser.Parse(data);// This model will then be saved in DB as well as sent to another Kafka topic which is the 2nd and 3rd part of the flow.

因此,根据我创建的类型,我创建了一个正在创建单个解析器的工厂。

如果这是一个好的设计,我只是想要建议。我唯一担心的是,未来会有多种类型的有效载荷出现,这将增加解析器的数量。

有什么建议吗?

【问题讨论】:

  • 我正在考虑将数据定义为动态的,然后使用 Automapper 根据类型将数据映射到每个实体
  • 在某些情况下,我需要在将有效负载数据映射到实体数据之前编写一些逻辑。我们可以在 automapper 中定义自定义映射逻辑吗?我创建了这些基于类型的解析器,只为每种解析器类型编写这些自定义逻辑。
  • 是的,你可以在地图定义codewithmukesh.com/blog/automapper-in-aspnet-core/…很多逻辑
  • 但是使用 automapper 会是更好的设计,因为我必须根据类型创建映射。因此,只需在类型上使用条件即可创建相应的映射。因为这将在一个类中创建如此多的条件和自动映射器代码。因为我真正关心的不是映射,而是如何以更好的方式构造代码,以便对将从 Kafka 收到的任何消息类型进行反序列化
  • 这是一个小细节,但我更愿意将刺痛解析为 JObject - JObject.Parse(message); 而不是使用 dynamicdynamic 并不总是邪恶的,但是一旦你介绍它,它就有一种传播方式。人们看到它并开始用它做坏事。实际上他们用JObject做同样的事情。

标签: c# json .net asp.net-core design-patterns


【解决方案1】:

也可以使用接受类型的DeserializeObject 方法。这样你就不需要编写任何类型特定的解析器,你只需要找出一个类型并将它提供给那个方法。像这样的:

public Payload GetPayload(string type, string message)
    {
        switch (type)
        {
            case "fuel":
                return JsonConvert.DeserializeObject(message, typeof(Fuel));
            case "engine":
                return JsonConvert.DeserializeObject(message, typeof(Engine));
            default:
                return null;
        }
    }

然后您可以使用 JsonProperties 属性(基本上是映射)来装饰您的实体类,如 here 所示(加上使用该转换器):

[JsonConverter(typeof(JsonPathConverter))]
class Fuel : Payload
{
   [JsonProperty("data.fueltype")]
   public string FuelType{ get; set; }

   [JsonProperty("data.mileage")]
   public float Mileage { get; set; }
}



string message = "{\r\n\t\"type\": \"fuel\",\r\n\t\"data\":\r\n\t\t{\r\n\t\t\t\"fueltype\": \"petrol\",\r\n\t\t\t\"mileage\": 23.76,\r\n\t\t\t\"tankcapacity\": 37\r\n\t\t}\r\n}";
var type = GetMessageType(); // get type from message i.e. with JObject
var payload = GetPayload(type, message);

【讨论】:

  • 在某些情况下,我需要在将有效负载数据映射到实体数据之前编写一些逻辑。这就是为什么我创建了这些基于类型的解析器来为每个解析器类型编写这些自定义逻辑。
  • @RickoK 这有帮助吗?
  • 不,因为负载和实体并非总是具有相同的结构,因此您不能直接反序列化。但我真正关心的不是反序列化,而是系统结构,它可以处理任何消息类型,创建实体并将实体保存到数据库中。
猜你喜欢
  • 1970-01-01
  • 2013-11-26
  • 2013-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-06
相关资源
最近更新 更多