Ответ 1
Если вы не контролируете исходный код, вы можете использовать пользовательский 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
}
]