You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何根据属性反序列化Redis订阅返回的Success/Error两种对象?

Handling Polymorphic Redis Message Deserialization in C#

Great question! Let's walk through your options here because deserializing polymorphic JSON responses (like your Success and Error objects) without an explicit type field is a super common scenario.

Can you use Either<Success, Error> directly?

Short answer: Not out of the box with Json.NET (Newtonsoft.Json). The library doesn’t have a built-in Either type, so JsonConvert.DeserializeObject<Either<Success, Error>>(msg) will throw an error unless you build custom support for it. That said, you can absolutely implement an Either type and a corresponding JSON converter to make this work—we’ll cover that later.

Practical Alternatives (No Custom Either Required)

1. Parse to JObject first, then check properties

This is the simplest, most straightforward approach if you don’t want to add custom types or libraries. You’ll parse the raw JSON into a JObject, check for unique properties that distinguish Success from Error, then convert to the appropriate type:

subscription.OnMessage = (channel, msg) => {
    var jsonObject = JObject.Parse(msg);

    // Check for a property unique to Success
    if (jsonObject.ContainsKey("Token"))
    {
        var success = jsonObject.ToObject<Success>();
        // Handle your Success logic here
        Console.WriteLine($"Received success token: {success.Token}");
    }
    // Check for a property unique to Error
    else if (jsonObject.ContainsKey("ErrorMessage"))
    {
        var error = jsonObject.ToObject<Error>();
        // Handle your Error logic here
        Console.WriteLine($"Received error from {error.Name}: {error.ErrorMessage}");
    }
    else
    {
        // Handle unexpected message format
        Console.WriteLine("Received unknown message type");
    }
};

2. Use a base class with a custom JSON converter

If you want a more type-safe approach, create a shared base class for your messages and write a converter that determines which subclass to deserialize into. You can also use a library like JsonSubtypes (via NuGet) to simplify this, but here’s how to do it manually:

First, define your base class and subclasses:

public abstract class RedisMessage { }

public class Success : RedisMessage 
{ 
    public string Token { get; set; } 
    public object Message { get; set; } 
}

public class Error : RedisMessage 
{ 
    public string ErrorMessage { get; set; } 
    public string Name { get; set; } 
}

Then create a custom converter:

public class RedisMessageConverter : JsonConverter<RedisMessage>
{
    public override RedisMessage ReadJson(JsonReader reader, Type objectType, RedisMessage existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var jObj = JObject.Load(reader);
        
        // Use property presence to decide type
        if (jObj.ContainsKey("Token"))
            return jObj.ToObject<Success>(serializer);
        if (jObj.ContainsKey("ErrorMessage"))
            return jObj.ToObject<Error>(serializer);
        
        throw new JsonSerializationException("Unknown message type");
    }

    public override void WriteJson(JsonWriter writer, RedisMessage value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

Now use it in your subscription code:

var serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new RedisMessageConverter());

subscription.OnMessage = (channel, msg) => {
    var message = JsonConvert.DeserializeObject<RedisMessage>(msg, serializerSettings);
    
    switch (message)
    {
        case Success success:
            // Handle Success
            break;
        case Error error:
            // Handle Error
            break;
        default:
            // Handle unknown type
            break;
    }
};

Implementing the Either Approach

If you really want to use an Either type for this scenario, you’ll need to define the type and a converter to handle it. Here’s a minimal implementation:

Step 1: Define the Either type

public class Either<TLeft, TRight>
{
    public bool IsLeft { get; }
    public TLeft Left { get; }
    public TRight Right { get; }

    private Either(TLeft left)
    {
        IsLeft = true;
        Left = left;
        Right = default;
    }

    private Either(TRight right)
    {
        IsLeft = false;
        Left = default;
        Right = right;
    }

    // Implicit conversions for easier usage
    public static implicit operator Either<TLeft, TRight>(TLeft left) => new(left);
    public static implicit operator Either<TLeft, TRight>(TRight right) => new(right);
}

Step 2: Create the JSON converter for Either

public class EitherConverter<TLeft, TRight> : JsonConverter<Either<TLeft, TRight>>
{
    public override Either<TLeft, TRight> ReadJson(JsonReader reader, Type objectType, Either<TLeft, TRight> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var jObj = JObject.Load(reader);
        
        try
        {
            // Try deserializing to TLeft first (Success)
            var leftValue = jObj.ToObject<TLeft>(serializer);
            return leftValue;
        }
        catch (JsonSerializationException)
        {
            // Fall back to TRight (Error) if deserialization fails
            var rightValue = jObj.ToObject<TRight>(serializer);
            return rightValue;
        }
    }

    public override void WriteJson(JsonWriter writer, Either<TLeft, TRight> value, JsonSerializer serializer)
    {
        if (value.IsLeft)
            serializer.Serialize(writer, value.Left);
        else
            serializer.Serialize(writer, value.Right);
    }
}

Step 3: Use it in your code

var settings = new JsonSerializerSettings();
settings.Converters.Add(new EitherConverter<Success, Error>());

subscription.OnMessage = (channel, msg) => {
    var result = JsonConvert.DeserializeObject<Either<Success, Error>>(msg, settings);
    
    if (result.IsLeft)
    {
        var success = result.Left;
        // Handle Success logic
    }
    else
    {
        var error = result.Right;
        // Handle Error logic
    }
};

Final Recommendation

If you want simplicity and don’t mind a bit of manual property checking, the JObject approach is your best bet. If you prefer a more type-safe, scalable solution, go with the base class + converter method. The Either approach works but adds extra complexity unless you’re already using an Either type elsewhere in your codebase.

内容的提问来源于stack exchange,提问作者Gabriel Guedes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 13:57:36