忽略在序列化为JSON时抛出异常的类成员

Ev.*_*Ev. 32 c# serialization json json.net

我正在使用Newtonsoft JSON序列化程序,它适用于大多数对象.

不幸的是,JsonSerializationException当我尝试序列化大型对象时,我得到了一个,其中一个成员抛出一个NullReferenceException.

反正有没有忽略违规成员并序列化对象的其余部分?

我想也许在JsonSerializerSettings

这是我想要做的简化版本:

private class TestExceptionThrowingClass
{
    public string Name { get { return "The Name"; } }
    public string Address { get { throw new NullReferenceException(); } }
    public int Age { get { return 30; } }
}

[Test]
public void CanSerializeAClassWithAnExceptionThrowingMember()
{ 
    // Arrange
    var toSerialize = new TestExceptionThrowingClass();

    // Act

    var serializerSettings = new Newtonsoft.Json.JsonSerializerSettings();
    serializerSettings.MaxDepth = 5;
    serializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    serializerSettings.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore;
    serializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
    serializerSettings.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Reuse;
    serializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore;

    var result = Newtonsoft.Json.JsonConvert.SerializeObject(toSerialize);

    // Assert
    Assert.That(result, Is.EqualTo(@"{""Name"":""The Name"",""Age"":30}"));
}
Run Code Online (Sandbox Code Playgroud)

这是堆栈跟踪:

at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target) 
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue) 
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) 
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) 
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType) 
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType) 
at Newtonsoft.Json.JsonConvert.SerializeObject(Object value, Type type, Formatting formatting, JsonSerializerSettings settings) 
at Newtonsoft.Json.JsonConvert.SerializeObject(Object value) 
at AspectsProject.Aspects.CachingPolicy.CachingPolicyCacheKeyCreatorTests.CanSerializeAClassWithAnExceptionThrowingMember() in D:\Dev\test.cs:line 169
    --NullReferenceException 
at AspectsProject.Aspects.CachingPolicy.CachingPolicyCacheKeyCreatorTests.TestExceptionThrowingClass.get_Address() in D:\Dev\test.cs:line 149 
at GetAddress(Object ) 
at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)
Run Code Online (Sandbox Code Playgroud)

我很高兴使用不同的JSON序列化程序,如果有人知道会这样做.

noa*_*hen 44

一种忽略错误的简单方法:

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Error = (serializer,err) => {
    err.ErrorContext.Handled = true;
}
Run Code Online (Sandbox Code Playgroud)

要么

settings.Error = (serializer,err) => err.ErrorContext.Handled = true;
Run Code Online (Sandbox Code Playgroud)

  • 我通常会同意你的观点,但我认为这是基于情况的判断.我正在尝试将对象转储到磁盘上,以便我可以轻松地对它们进行比较.我真的不想在接下来的几个小时内尝试使用自定义解析器来处理某些我只用于故障排除的代码.我正在使用的对象是在库中,我不能改变它的工作方式.如果我以单向调用它,则一组属性无效,如果我将其称为另一个属性,则另一组属性无效.所以它是这个或自定义解析器. (6认同)
  • 得到*任何类型*的错误?只是假装你没有得到错误!什么可能出错? (4认同)
  • 我发现这对于对象的任意日志记录很有用,我不在乎是否发生序列化失败。 (2认同)

Bri*_*ers 28

如果您不控制源代码,则可以使用自定义ContractResolver在序列化期间为有问题的属性注入"ShouldSerialize"方法.您可以让该方法始终返回false,或者可选地,实现一些逻辑,该逻辑将检测属性将抛出的情况,并且仅在该情况下返回false.

例如,假设您的类看起来像这样:

class Problematic
{
    public int Id { get; set; }
    public string Name { get; set; }
    public object Offender 
    {
        get { throw new NullReferenceException(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

显然,如果我们尝试序列化上述内容,它将无法工作,因为Offender当序列化程序尝试访问它时,属性将始终抛出异常.由于我们知道导致问题的类和属性名称,因此我们可以编写自定义的ContractResolver(派生自DefaultContractResolver)来禁止该特定成员的序列化.

class CustomResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, 
                                        MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.DeclaringType == typeof(Problematic) && 
            property.PropertyName == "Offender")
        {
            property.ShouldSerialize = instanceOfProblematic => false;
        }

        return property;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个演示如何使用它的演示:

class Program
{
    static void Main(string[] args)
    {
        Problematic obj = new Problematic
        {
            Id = 1,
            Name = "Foo"
        };

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.ContractResolver = new CustomResolver();

        string json = JsonConvert.SerializeObject(obj, settings);
        Console.WriteLine(json);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

{"Id":1,"Name":"Foo"}
Run Code Online (Sandbox Code Playgroud)

更通用的解决方案

在您的注释中,您指出在访问任何属性时,您有多种对象可能会引发异常.为此,我们需要更通用的东西.这是一个可能适用于该情况的解析器,但您需要在您自己的环境中进行广泛测试.它不依赖于任何特定的类或属性名称,但会为每个属性创建一个ShouldSerialize谓词.在该谓词中,它使用反射来获取try/catch中的属性值; 如果成功则返回true,否则返回false.

class CustomResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        property.ShouldSerialize = instance =>
        {
            try
            {
                PropertyInfo prop = (PropertyInfo)member;
                if (prop.CanRead)
                {
                    prop.GetValue(instance, null);
                    return true;
                }
            }
            catch
            {
            }
            return false;
        };

        return property;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个演示:

class Program
{
    static void Main(string[] args)
    {
        List<MightThrow> list = new List<MightThrow>
        {
            new MightThrow { Flags = ThrowFlags.None, Name = "none throw" },
            new MightThrow { Flags = ThrowFlags.A, Name = "A throws" },
            new MightThrow { Flags = ThrowFlags.B, Name = "B throws" },
            new MightThrow { Flags = ThrowFlags.Both, Name = "both throw" },
        };

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.ContractResolver = new CustomResolver();
        settings.Formatting = Formatting.Indented;

        string json = JsonConvert.SerializeObject(list, settings);
        Console.WriteLine(json);
    }
}

[Flags]
enum ThrowFlags
{
    None = 0,
    A = 1,
    B = 2,
    Both = 3
}

class MightThrow
{
    public string Name { get; set; }
    public ThrowFlags Flags { get; set; }

    public string A
    {
        get
        {
            if ((Flags & ThrowFlags.A) == ThrowFlags.A)
                throw new Exception();
            return "a";
        }
    }

    public string B
    {
        get
        {
            if ((Flags & ThrowFlags.B) == ThrowFlags.B)
                throw new Exception();
            return "b";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

[
  {
    "Name": "none throw",
    "Flags": 0,
    "A": "a",
    "B": "b"
  },
  {
    "Name": "A throws",
    "Flags": 1,
    "B": "b"
  },
  {
    "Name": "B throws",
    "Flags": 2,
    "A": "a"
  },
  {
    "Name": "both throw",
    "Flags": 3
  }
]
Run Code Online (Sandbox Code Playgroud)

  • 好的,我有一些似乎有效的东西(请参阅更新的答案)。你的旅费可能会改变。 (2认同)
  • 哈哈!很高兴我能帮助你。 (2认同)