Загрузка файла с диска и использование его в качестве значения во время десериализации jsonC#

Место общения программистов C#
Ответить
Anonymous
 Загрузка файла с диска и использование его в качестве значения во время десериализации json

Сообщение 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"
},
}


Подробнее здесь: https://stackoverflow.com/questions/792 ... ialization
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»