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

 男儿有志不言苦 发布于 2023-02-06 10:27

我正在使用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}"));
}

这是堆栈跟踪:

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)

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

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

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

    class Problematic
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public object Offender 
        {
            get { throw new NullReferenceException(); }
        }
    }
    

    显然,如果我们尝试序列化上述内容,它将无法工作,因为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;
        }
    }
    

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

    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);
        }
    }
    

    输出:

    {"Id":1,"Name":"Foo"}
    

    更通用的解决方案

    在您的注释中,您指出在访问任何属性时,您有多种对象可能会引发异常.为此,我们需要更通用的东西.这是一个可能适用于该情况的解析器,但您需要在您自己的环境中进行广泛测试.它不依赖于任何特定的类或属性名称,但会为每个属性创建一个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;
        }
    }
    

    这是一个演示:

    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";
            }
        }
    }
    

    输出:

    [
      {
        "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
      }
    ]
    

    2023-02-06 10:28 回答
  • 一种忽略错误的简单方法:

    JsonSerializerSettings settings = new JsonSerializerSettings();
    settings.Error = (serializer,err) => {
        err.ErrorContext.Handled = true;
    }
    

    要么

    settings.Error = (serializer,err) => err.ErrorContext.Handled = true;
    

    2023-02-06 10:28 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有