Надеюсь, вы поможете мне понять, как работают настройки TypeNameHandling
и TypeNameAssemblyFormatHandling
в Json.NET.
Сценарий
Предположим, есть следующие объекты:
namespace SerDesTest
{
public abstract class BaseClass
{
public int Int0 { get; set; }
public string String0 { get; set; }
}
public class Father : BaseClass
{
public Father() { Children = new List<BaseClass>(); }
public List<BaseClass> Children { get; set; }
}
public class Child : BaseClass
{
public Child(Father father) { Father = father; }
public Father Father { get; set; }
public int Int1 { get; set; }
public string String1 { get; set; }
}
public class Generic<T>
{
public int GenericInt { get; set; }
public string GenericString { get; set; }
public T GenericProperty { get; set; }
}
}
И использовать следующие вспомогательные методы:
public static class JsonHelper
{
public static string Serialize(object obj)
{
return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
SerializationBinder = new JsonManagerKnownTypesBinder()
{
KnownTypes = GetTypeList("SerDesTest")
}
});
}
public static TEntity Deserialize<TEntity>(string json)
{
return JsonConvert.DeserializeObject<TEntity>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
SerializationBinder = new JsonManagerKnownTypesBinder()
{
KnownTypes = GetTypeList("SerDesTest")
}
});
}
public static IList<Type> GetTypeList(string assemblyName)
{
List<Type> list = new List<Type>();
Assembly assembly = Assembly.LoadFrom(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{assemblyName}.dll"));
if (assembly != null)
{
list.AddRange(assembly.GetTypes());
}
return list.AsParallel().Distinct().ToList<Type>();
}
}
Чтобы запустить его следующим образом:
internal static class Program
{
private static void Main(string[] args)
{
try
{
Father father = new Father();
Generic<Child> generic = new Generic<Child>
{
GenericInt = 100,
GenericString = "100",
GenericProperty = new Child(father)
{
Int0 = 0,
Int1 = 1,
String0 = "0",
String1 = "1"
}
};
string genericJson = JsonHelper.Serialize(generic);
Console.WriteLine(genericJson);
Generic<Child> genericDes = JsonHelper.Deserialize<Generic<Child>>(genericJson);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}
Запуск тестовой программы
При запуске тестаВ программе я получаю следующий json:
{
"$type": "Generic`1",
"GenericInt": 100,
"GenericString": "100",
"GenericProperty": {
"$type": "Child",
"Father": {
"$type": "Father",
"Children": [],
"Int0": 0,
"String0": null
},
"Int1": 1,
"String1": "1",
"Int0": 0,
"String0": "0"
}
}
и следующее исключение:
Тип, указанный в JSON 'SerDesTest.Generic 1, SerDesTest,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not
compatible with 'SerDesTest.Generic
1 [[SerDesTest.Child, SerDesTest, Версия = 1.0.0.0, Культура = нейтральная, PublicKeyToken = null]], SerDesTest, Версия = 1.0.0.0, Культура = нейтральная, PublicKeyToken = null '.Путь '$ type', строка 2, позиция 22.
Вопросы
У меня есть два вопроса / проблемы:
1) Из-за конфигурации Json.NETя ожидаю, что во второй строке json будет указано «полное имя» для поля $type
, что-то вроде: «$ type»: «SerDesTest.Generic [SerDesTest, CHild]» , а нетолько "$ type": "Generic`1" ...
2) Есть ли у вас какие-либо идеи по поводу разрешения исключения?
Спасибо, Attilio
Обновление 1
По запросу Брайана Роджерса, следуя коду для JsonManagerKnownTypesBinder
:
public class JsonManagerKnownTypesBinder : ISerializationBinder
{
public IList<Type> KnownTypes { get; set; }
public Type BindToType(string assemblyName, string typeName)
{
return KnownTypes.SingleOrDefault(t => t.Name == typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
}
Обновление 2
Еще несколько тестов.Если я не установил SerializationBinder
, я использую эту версию класса JsonHelper
:
public static class JsonHelper
{
public static string Serialize(object obj)
{
return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
//SerializationBinder = new JsonManagerKnownTypesBinder()
//{
// KnownTypes = GetTypeList("SerDesTest")
//}
});
}
public static TEntity Deserialize<TEntity>(string json)
{
return JsonConvert.DeserializeObject<TEntity>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
//SerializationBinder = new JsonManagerKnownTypesBinder()
//{
// KnownTypes = GetTypeList("SerDesTest")
//}
});
}
}
Я решаю все свои проблемы: во-первых, у меня нет исключений в процессе десериализации;во-вторых, у меня есть следующий json в качестве вывода:
{
"$type": "SerDesTest.Generic`1[[SerDesTest.Child, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"GenericInt": 100,
"GenericString": "100",
"GenericProperty": {
"$type": "SerDesTest.Child, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Father": {
"$type": "SerDesTest.Father, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Children": {
"$type": "System.Collections.Generic.List`1[[SerDesTest.BaseClass, SerDesTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e",
"$values": []
},
"Int0": 0,
"String0": null
},
"Int1": 1,
"String1": "1",
"Int0": 0,
"String0": "0"
}
}
Вы можете поиграть с опцией TypeNameAssemblyFormatHandling
, чтобы настроить информацию, содержащуюся в поле $Type
.
Таким образом, возникает вопрос: какМогу ли я реализовать интерфейс ISerializationBinder
, чтобы получить более безопасное программное обеспечение?