Ответ 1
Вот мои мысли относительно решения проблемы:
Проблема:
Пользовательская десериализация Json.Net api не прозрачна, то есть влияет на мою иерархию классов.
На самом деле это не проблема в случае, если у вас 10-20 классов в вашем проекте, хотя, если у вас есть огромный проект с тысячами классов, вы не очень довольны тем фактом, что вам нужно выполнить свой проект ООП с помощью Json. Чистые требования.
Json.Net хорош с объектами POCO, которые заполняются (инициализируются) после их создания. Но это не правда во всех случаях, иногда вы получаете свои объекты, инициализированные внутри конструктора. И чтобы сделать эту инициализацию, вам нужно передать "правильные" аргументы. Эти "правильные" аргументы могут быть внутри сериализованного текста, или они могут быть уже созданы и инициализированы некоторое время раньше. К сожалению, Json.Net во время десериализации передает значения по умолчанию аргументам, которые он не понимает, и в моем случае он всегда вызывает ArgumentNullException.
Решение:
Вот подход, который позволяет создавать реальные пользовательские объекты во время десериализации с использованием любого набора аргументов, сериализованных или несериализованных, основная проблема заключается в том, что подход не оптимален, он требует двух фаз десериализации на каждый объект, который требует пользовательской десериализации, но он работает и позволяет десериализовать объекты так, как вам нужно, так что:
Сначала мы собираем класс CustomCreationConverter следующим образом:
public class FactoryConverter<T> : Newtonsoft.Json.JsonConverter
{
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException("CustomCreationConverter should only be used while deserializing.");
}
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>The object value.</returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
T value = CreateAndPopulate(objectType, serializer.Deserialize<Dictionary<String, String>>(reader));
if (value == null)
throw new JsonSerializationException("No object created.");
return value;
}
/// <summary>
/// Creates an object which will then be populated by the serializer.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns></returns>
public abstract T CreateAndPopulate(Type objectType, Dictionary<String, String> jsonFields);
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
/// <summary>
/// Gets a value indicating whether this <see cref="JsonConverter"/> can write JSON.
/// </summary>
/// <value>
/// <c>true</c> if this <see cref="JsonConverter"/> can write JSON; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite
{
get
{
return false;
}
}
}
Далее мы создаем класс factory, который создаст наш Foo:
public class FooFactory : FactoryConverter<Foo>
{
public FooFactory(Bar bar)
{
this.Bar = bar;
}
public Bar Bar { get; private set; }
public override Foo Create(Type objectType, Dictionary<string, string> arguments)
{
return new Foo(Bar, arguments["X"], arguments["Y"]);
}
}
Вот пример кода:
var bar = new Bar("BarObject");
var fooSrc = new Foo
(
bar,
"A", "B"
);
var str = JsonConvert.SerializeObject(fooSrc);
var foo = JsonConvert.DeserializeObject<Foo>(str, new FooFactory(bar));
Console.WriteLine(str);
В этом случае foo содержит аргумент, который нам нужно передать в конструктор Foo во время десериализации.