将属性访问异常序列化为 JSON。

huangapple go评论68阅读模式
英文:

Serialize property access exception to json

问题

我有一个简单的类,其中一个属性是:

public TValue Value => IsSuccess
    ? _value
    : throw new InvalidOperationException("无法访问失败结果的值。");

所以它的工作原理是,当某个操作成功时,将该操作的值分配给属性。但是现在,当我这样做时:

var result = new Result(someValue) { IsSuccess = false };
var serialized = JsonConvert.SerializeObject(result); // 当然,结果类型标记为可序列化

所以在这里,我将这样的结果设置为 false,然后我想将其序列化,问题是异常没有被序列化,我只得到了抛出的异常。我在这里漏掉了什么?

英文:

I have a simple class that one of properties is:

public TValue Value => IsSuccess
    ? _value
    : throw new InvalidOperationException("The value of a failure result can not be accessed.");

So it works like this, when some operation is a success assign value from this operation to property easy. But now I when I do sutch a thig:

var result = new Result (someValue) { IsSuccess = false }
var serialized =  JsonConvert.SerializeObject(result); // of course result type is marked as seriazable

So here I am setting such a result to false, and next I want to serilize it, problem is that exception is not serializing and I am getting just thrown exception. What am I missing here?

答案1

得分: 2

我在思考是否有更好的设计来实现你的目标。具体来说,将 IsSuccess 检查逻辑移到一个方法中可能会更好,这样在序列化过程中就不会受到影响。但如果你决定按照这种方式处理,你可以使用 JsonConverter 来捕获和序列化异常,示例代码如下:

public class ValueOrExceptionConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        if (value == null)
        {
            return;
        }

        try
        {
            serializer.Serialize(writer, ((dynamic)value).Value);
        }
        catch (Exception ex)
        {
            serializer.Serialize(writer, new { Value = new { ex.Message } });
        }
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType) => true;
}

我假设你的 Result 类是泛型的,使用这个转换器的方式如下:

[JsonConverter(typeof(ValueOrExceptionConverter))]
public class Result<TValue>

这一切都是基于你使用的是 Newtonsoft.Json。使用该转换器的结果类似于:

{"Value":"Message":"The value of a failure result can not be accessed."}}

有关转换器代码的一些注意事项:

  • 你可以考虑使用接口或抽象类,而不是进行动态类型转换,如果你有的话。或者,如果你知道可能用于 TValue 的确切泛型类型,那么你可以进行更具体的类型转换。
  • 我假设你不想序列化整个异常。因此,如果你需要的不仅仅是 Message,还可以添加其他属性,例如:new { ex.Message, InnerMessage = ex.InnerException.Message }
  • 我假设 Result 类只有一个名为 Value 的属性。你可以以相同的方式添加更多属性,就像你可以添加其他异常属性一样。
  • 我没有实现 ReadJson 方法,因为反序列化异常而不是值会使事情变得相当复杂。但如果你需要反序列化 Result,我相信这并不是不可能的。
英文:

I'm wondering whether there isn't a better design for what you're trying to achieve. Namely, it might be better to move the IsSuccess check logic into a method so that it doesn't get hit during serialization. But if you really decide to do it this way, you could use a JsonConverter to catch and serialize the exception for you:

public class ValueOrExceptionConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        if (value == null)
        {
            return;
        }

        try
        {
            serializer.Serialize(writer, ((dynamic)value).Value);
        }
        catch (Exception ex)
        {
            serializer.Serialize(writer, new { Value = new { ex.Message } });
        }
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType) =&gt; true;
}

I'm assuming your Result class is generic so using this converter would look like this:

[JsonConverter(typeof(ValueOrExceptionConverter))]
public class Result&lt;TValue&gt;

This is all assuming that you're using Newtonsoft.Json. The result of using that converter is something like this:

{&quot;Value&quot;:&quot;Message&quot;:&quot;The value of a failure result can not be accessed.&quot;}}

A few notes about the converter code:

  • Instead of casting to dynamic, you might be better of using an interface or abstract class if you have it. Or if you know exactly which generics you might use for TValue then perhaps you can cast even more specifically.
  • I've assumed that you don't want to serialize the entire exception. So if you need more than just the Message then you can add those properties like so for example: new { ex.Message, InnerMessage = ex.InnerException.Message }.
  • I've assumed that the Result class only has one property, named Value. You can add more properties in the same way you can add additional Exception properties.
  • I haven't implemented the ReadJson because deserializing the exception instead of the value makes that quite tricky. But I'm sure it's not impossible if you need to also deserialize the Result.

huangapple
  • 本文由 发表于 2023年6月1日 19:25:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76381363.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定