在反序列化 MassTransit 中的多态属性期间如何避免不兼容?

How to avoid incompatible during deserializing polymorphic properties in MassTransit?

使用request/response 模式时,在响应的反序列化过程中,出现不兼容错误。显然,发布者序列化程序配置导致了这个问题,尽管消息符合预期。

使用 JsonProperty 功能可以隔离问题,但是,它并没有反映出预期的结果。

版本 .NET:6.0; MassTRAnsit:7.3.0; 牛顿软件:13.0.1

Type specified in JSON 'Messages.Models+CreditCard, Messages' 
is not compatible with 'GreenPipes.DynamicInternal.Models\+Messages.Models\+IPaymentMethod'

资源:

_bus.CreateRequestClient<TMessage>().GetResponse<TResponse>(message, cancellationToken);

发布者序列化器配置:

bus.ConfigureJsonSerializer(settings =>
{
    settings.TypeNameHandling = TypeNameHandling.Objects;
    return settings;
});

消息响应序列化:

{
  "message": {
    "$type": "Messages.Services.ShoppingCarts.Responses+CartDetails, Messages",
    "cartItems": [
      {
        "$type": "Messages.Models+Item, Messages",
        "productId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "productName": "string",
        "unitPrice": "10.0",
        "quantity": 30,
        "pictureUrl": "string"
      }
    ],
    "paymentMethods": [
      {
        "$type": "Messages.Models+CreditCard, Messages",
        "id": "be7c40ac-1cd1-4e35-bccf-a1a2f4efecfd",
        "expiration": "01/22",
        "holderName": "string",
        "number": "374245455400126",
        "securityNumber": "string"
      },
      {
        "$type": "Messages.Models+PayPal, Messages",
        "id": "9465cf12-a322-477d-94c8-116d03a8399e",
        "Password": "123",
        "UserName": "string"
      }
    ],

    ...

  }
}

消费者解串器配置:

bus.ConfigureJsonDeserializer(settings =>
{
    settings.TypeNameHandling = TypeNameHandling.Objects; // or Auto, All, etc...
    return settings;
});

错误:

System.Runtime.Serialization.SerializationException: A JSON serialization exception occurred while deserializing the message envelope
    ---> Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'Messages.Models+CreditCard, Messages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 
    is not compatible with 'GreenPipes.DynamicInternal.Models\+Messages.Models\+IPaymentMethod, MessagesGreenPipes.DynamicInternale7ccc67139ad479db488c4fa6310335a, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. 
    Path 'message.paymentMethods[0].$type', line 30, position 55.

JsonProperty 不起作用:

public record CartDetails : Response
{
    [JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
    public IEnumerable<Models.IPaymentMethod> PaymentMethods { get; init; }
}
{
  "message": {
    "cartItems": [
      {
        "productId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "productName": "string",
        "unitPrice": "10.0",
        "quantity": 30,
        "pictureUrl": "string"
      }
    ],
    "paymentMethods": [
      {
        "id": "be7c40ac-1cd1-4e35-bccf-a1a2f4efecfd",
        "expiration": "01/22",
        "holderName": "string",
        "number": "374245455400126",
        "securityNumber": "string"
      },
      {
        "id": "9465cf12-a322-477d-94c8-116d03a8399e",
        "Password": "123",
        "UserName": "string"
      }
    ],

    ...

  }
}

回应

public record CartDetails : Response
{
    // [JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
    public IEnumerable<Models.IPaymentMethod> PaymentMethods { get; init; }
    public IEnumerable<Models.Item> CartItems { get; init; }
    public Guid UserId { get; init; }
    public decimal Total { get; init; }
    public Guid Id { get; init; }
    public bool IsDeleted { get; init; }
}

类型:

public interface IPaymentMethod
{
    Guid Id { get; }
    decimal Amount { get; }
}

public record CreditCard : IPaymentMethod
{
    public Guid Id { get; init; }
    public decimal Amount { get; init; }
    [property: JsonConverter(typeof(ExpirationDateOnlyJsonConverter))]
    public DateOnly Expiration { get; init; }
    public string HolderName { get; init; }
    public string Number { get; init; }
    public string SecurityNumber { get; init; }
}

public record PayPal : IPaymentMethod
{
    public Guid Id { get; init; }
    public decimal Amount { get; init; }
    public string UserName { get; init; }
    public string Password { get; init; }
}

感谢 Wiktor Zychla,这个问题让我找到了 Nkot's 解决方法,这对我很有帮助:

TypeNameHandlingConverter:

internal class TypeNameHandlingConverter : JsonConverter
{
    private readonly TypeNameHandling _typeNameHandling;

    public TypeNameHandlingConverter(TypeNameHandling typeNameHandling)
    {
        _typeNameHandling = typeNameHandling;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
        => new JsonSerializer { TypeNameHandling = _typeNameHandling }.Serialize(writer, value);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
        => new JsonSerializer { TypeNameHandling = _typeNameHandling }.Deserialize(reader, objectType);

    public override bool CanConvert(Type objectType) 
        => IsMassTransitOrSystemType(objectType) is false;

    private static bool IsMassTransitOrSystemType(Type objectType)
        => objectType.Assembly == typeof(IConsumer).Assembly ||
           objectType.Assembly.IsDynamic ||
           objectType.Assembly == typeof(object).Assembly;
}

消费者解串器配置:

bus.ConfigureJsonDeserializer(settings =>
{
    settings.Converters.Add(new TypeNameHandlingConverter(TypeNameHandling.Objects));
    return settings;
});