Чтобы внести ясность, я собираюсь представить рабочее решение этой проблемы с использованием Newtonsoft.Json 13.0.3. Указанное решение выглядит не очень хорошо, и я ищу лучшее.
В некотором контексте это Godot, поэтому файлы на диске имеют тип «Ресурс» и загружаются путем вызова ResourceLoader.Load(string path)
Идея состоит в том, чтобы сериализовать тип, содержащий загруженный ресурс в качестве параметра, и десериализовать его, загрузив ресурс вместо создания нового.
public class KnownTypesBinder : ISerializationBinder
{
public string CurrentResourcePath { get; private set; }
public Type BindToType(string assemblyName, string typeName)
{
Type prospectType = Type.GetType(typeName);
if (prospectType == null)
{
// When the typeName contains an '@' it means it is a resource type with its path encoded after the '@' character
int strIndex = typeName.Find('@');
Assert.IsTrue(strIndex != -1);
// Storing the path of the currently deserialized resource for future use by the contract
CurrentResourcePath = typeName.Substring(strIndex + 1);
string realTypeName = typeName.Substr(0, strIndex);
return Type.GetType(realTypeName);
}
else
{
return Type.GetType(typeName);
}
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
if (typeof(Resource).IsAssignableFrom(serializedType))
{
// What is done here is that for resource types we want to bake the ResourcePath after the resource type info
// like this: "$type": "Godot.Resource@res://MyPath/MyResource.tres"
// The "Godot.Resource" part is the type info
// The "res://MyPath/MyResource.tres" is the godot path of the file
// BindToName does not provide with the currently serialized object, so it is grabed from the contract resolver who is the one knowing that.
// It is then cast to Resource type
assemblyName = null;
Resource currentlySerializedResource = (CoreLogic.Instance.JsonSerializingSettings.ContractResolver as ResourceContractResolver).CurrentlySerializedObject as Resource;
Assert.IsTrue(currentlySerializedResource != null);
string path = currentlySerializedResource.ResourcePath;
typeName = serializedType.FullName + "@" + path;
}
else
{
string id = serializedType.FullName;
assemblyName = null; // That should probably be filled, but is outside the scope of this discussion
typeName = id;
}
}
}
class ResourceContractResolver : DefaultContractResolver
{
// Points to the object that is about to be serialized, as soon as it will start serializing properties
// within that object CurrentlySerializedObject will change to that property's value.
public object CurrentlySerializedObject { get; private set; }
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (typeof(Resource).IsAssignableFrom(objectType))
{
// Instance is not created right away:
contract.DefaultCreator = () =>
{
string currentResourcePath = (CoreLogic.Instance.JsonSerializingSettings.SerializationBinder as KnownTypesBinder).CurrentResourcePath;
return ResourceLoader.Load(currentResourcePath);
};
}
return contract;
}
protected override List GetSerializableMembers(Type objectType)
{
if (typeof(Resource).IsAssignableFrom(objectType))
{
// Do not serialized anything for resources
return new List();
}
else
{
return base.GetSerializableMembers(objectType);
}
}
protected override JsonContract CreateContract(Type objectType)
{
JsonContract contract = base.CreateContract(objectType);
contract.OnSerializingCallbacks.Add(OnSerializingCallback);
return contract;
}
private void OnSerializingCallback(object o, StreamingContext context)
{
CurrentlySerializedObject = o;
}
}
Чтобы этот пример был полным, мне нужно будет подключить оба этих класса к JsonSerializationSettings и т. д., но я надеюсь, что вы поняли идею.
очевидная проблема с этим решением заключается в том, что по сути это хак, основанный на знании того, что определенные вещи происходят в определенном порядке. Если я обновлю свою версию newtonsoft.json и усложню поддержку своего кода, это может привести к сбою.
Я был бы благодарен за более элегантное решение.
Редактирование 1: отредактированные комментарии к коду конкатенации типа+пути для ясности.
Редактирование 2: нижний комментарий также указывает на потенциальную проблему безопасности потоков в этом решении.
Редактирование 3: добавление. как выглядит сериализованный json этот пример:
public class ContainerType
{
public int ValueInt = -1;
public float ValueFloat = 3.14f;
public Resource Resource = null; // set to loaded resource file at path res://MyPath/MyResource.tres before serialization
}
{
"$type": "ContainerType",
"ValueInt": -1,
"ValueFloat": 3.14,
"Resource": {
"$type": "Godot.Resource@res://MyPath/MyResource.tres"
},
}
Подробнее здесь: https://stackoverflow.com/questions/792 ... ialization
Загрузка файла с диска и использование его в качестве значения во время десериализации json ⇐ C#
Место общения программистов C#
1732270325
Anonymous
Чтобы внести ясность, я собираюсь представить рабочее решение этой проблемы с использованием Newtonsoft.Json 13.0.3. Указанное решение выглядит не очень хорошо, и я ищу лучшее.
В некотором контексте это Godot, поэтому файлы на диске имеют тип «Ресурс» и загружаются путем вызова ResourceLoader.Load(string path)
Идея состоит в том, чтобы сериализовать тип, содержащий загруженный ресурс в качестве параметра, и десериализовать его, загрузив ресурс вместо создания нового.
public class KnownTypesBinder : ISerializationBinder
{
public string CurrentResourcePath { get; private set; }
public Type BindToType(string assemblyName, string typeName)
{
Type prospectType = Type.GetType(typeName);
if (prospectType == null)
{
// When the typeName contains an '@' it means it is a resource type with its path encoded after the '@' character
int strIndex = typeName.Find('@');
Assert.IsTrue(strIndex != -1);
// Storing the path of the currently deserialized resource for future use by the contract
CurrentResourcePath = typeName.Substring(strIndex + 1);
string realTypeName = typeName.Substr(0, strIndex);
return Type.GetType(realTypeName);
}
else
{
return Type.GetType(typeName);
}
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
if (typeof(Resource).IsAssignableFrom(serializedType))
{
// What is done here is that for resource types we want to bake the ResourcePath after the resource type info
// like this: "$type": "Godot.Resource@res://MyPath/MyResource.tres"
// The "Godot.Resource" part is the type info
// The "res://MyPath/MyResource.tres" is the godot path of the file
// BindToName does not provide with the currently serialized object, so it is grabed from the contract resolver who is the one knowing that.
// It is then cast to Resource type
assemblyName = null;
Resource currentlySerializedResource = (CoreLogic.Instance.JsonSerializingSettings.ContractResolver as ResourceContractResolver).CurrentlySerializedObject as Resource;
Assert.IsTrue(currentlySerializedResource != null);
string path = currentlySerializedResource.ResourcePath;
typeName = serializedType.FullName + "@" + path;
}
else
{
string id = serializedType.FullName;
assemblyName = null; // That should probably be filled, but is outside the scope of this discussion
typeName = id;
}
}
}
class ResourceContractResolver : DefaultContractResolver
{
// Points to the object that is about to be serialized, as soon as it will start serializing properties
// within that object CurrentlySerializedObject will change to that property's value.
public object CurrentlySerializedObject { get; private set; }
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (typeof(Resource).IsAssignableFrom(objectType))
{
// Instance is not created right away:
contract.DefaultCreator = () =>
{
string currentResourcePath = (CoreLogic.Instance.JsonSerializingSettings.SerializationBinder as KnownTypesBinder).CurrentResourcePath;
return ResourceLoader.Load(currentResourcePath);
};
}
return contract;
}
protected override List GetSerializableMembers(Type objectType)
{
if (typeof(Resource).IsAssignableFrom(objectType))
{
// Do not serialized anything for resources
return new List();
}
else
{
return base.GetSerializableMembers(objectType);
}
}
protected override JsonContract CreateContract(Type objectType)
{
JsonContract contract = base.CreateContract(objectType);
contract.OnSerializingCallbacks.Add(OnSerializingCallback);
return contract;
}
private void OnSerializingCallback(object o, StreamingContext context)
{
CurrentlySerializedObject = o;
}
}
Чтобы этот пример был полным, мне нужно будет подключить оба этих класса к JsonSerializationSettings и т. д., но я надеюсь, что вы поняли идею.
очевидная проблема с этим решением заключается в том, что по сути это хак, основанный на знании того, что определенные вещи происходят в определенном порядке. Если я обновлю свою версию newtonsoft.json и усложню поддержку своего кода, это может привести к сбою.
Я был бы благодарен за более элегантное решение.
Редактирование 1: отредактированные комментарии к коду конкатенации типа+пути для ясности.
Редактирование 2: нижний комментарий также указывает на потенциальную проблему безопасности потоков в этом решении.
Редактирование 3: добавление. как выглядит сериализованный json этот пример:
public class ContainerType
{
public int ValueInt = -1;
public float ValueFloat = 3.14f;
public Resource Resource = null; // set to loaded resource file at path res://MyPath/MyResource.tres before serialization
}
{
"$type": "ContainerType",
"ValueInt": -1,
"ValueFloat": 3.14,
"Resource": {
"$type": "Godot.Resource@res://MyPath/MyResource.tres"
},
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79212404/loading-a-file-from-disk-and-use-that-as-value-during-json-deserialization[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия