WedX - журнал о программировании и компьютерных науках

Как перенаправить JSON-сериализацию класса на другой?

У меня есть ситуация, когда мне нужно сериализовать любой GameObject как что-то еще внутри (GameObjectMetadata), которое содержит данные, полезные для его повторной десериализации позже.

public struct GameObjectMetadata
{
    public string AssetFullPath;
    public string AssetBundle;
    public string GUID;
    public string UnityType;
}

public struct TestPrefabLink
{
    public GameObject Prefab;
}

Я действительно изо всех сил пытаюсь реализовать конвертеры JSON.NET, необходимые для этого. Я занимался этим некоторое время и придумал приведенный ниже код. В основном это работает, но я получаю следующую ошибку:

JsonSerializationException: неожиданный токен при десериализации объекта: EndObject. Путь 'Components.$values[0]', строка 14, позиция 7. Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (читатель Newtonsoft.Json.JsonReader, тип объекта System.Type, контракт Newtonsoft.Json.Serialization.JsonContract, контракт Newtonsoft. Член Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existsValue) (в C:/Development/Releases/Json/Working/Newtonsoft.Json/Working-Signed /Src/Newtonsoft.Json/Сериализация/JsonSerializerInternalReader.cs:336)

Сериализатор:

data = JsonConvert.SerializeObject(blueprint, Formatting.Indented,
            new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All,
                NullValueHandling = NullValueHandling.Include,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                Converters = new List<JsonConverter>() { new GameObjectMetadataJsonConverter(), new UnityObjectJsonConverter() }
            });

Используемые преобразователи:

public class GameObjectMetadataJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {       
        var jobj = JObject.FromObject(value);
        jobj.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var metadata = serializer.Deserialize<GameObjectMetadata>(reader);

        // Snipped - Code here successfully converts to GameObject
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof (GameObjectMetadata);
    }
}

public class UnityObjectJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(UnityEngine.Object).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return existingValue; // This should not be called. But it is??
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var metadata = new GameObjectMetadata();
        metadata.UnityType = value.GetType().ToString();

        var path = UnityEditor.AssetDatabase.GetAssetPath((UnityEngine.Object)value);
        var bundleName = UnityEditor.AssetImporter.GetAtPath(path).assetBundleName;
        var guid = UnityEditor.AssetDatabase.AssetPathToGUID(path);

        metadata.AssetBundle = bundleName;
        metadata.AssetFullPath = path;
        metadata.GUID = guid;

        //var jobj = JObject.FromObject(metadata);
        //jobj.WriteTo(writer);
        serializer.Serialize(writer, metadata);
    }
}

Кажется, все, что я здесь делаю, вызывает плохую запись JSON. Но я не могу понять, как еще написать другую структуру/класс от ожидаемой.


Ответы:


1

UnityObjectJsonConverter.ReadJson() действительно будет вызываться, поскольку во время десериализации CanConvert(objectType) будет вызываться с ожидаемым типом для десериализации а не фактический тип, сериализованный в файл. И ожидаемый тип будет некоторым подтипом UnityEngine.Object.

Таким образом, вам нужно сделать что-то вроде:

public struct GameObjectMetadata
{
    public GameObjectMetadata(UnityEngine.Object value)
    {
        if (value == null)
            throw new ArgumentNullException();

        this.UnityType = value.GetType();

        var path = UnityEditor.AssetDatabase.GetAssetPath(value);
        var bundleName = UnityEditor.AssetImporter.GetAtPath(path).assetBundleName;
        var guid = UnityEditor.AssetDatabase.AssetPathToGUID(path);

        this.AssetBundle = bundleName;
        this.AssetFullPath = path;
        this.GUID = guid;
    }

    public UnityEngine.Object GetRealObject()
    {
        // I'm not a unity3d developer so I am not sure this is the correct method to call.
        return UnityEditor.AssetDatabase.LoadAssetAtPath(AssetFullPath, UnityType);
    }

    public string AssetFullPath;
    public string AssetBundle;
    public string GUID;
    public Type UnityType;
}

public class UnityObjectJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(UnityEngine.Object).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var metadata = serializer.Deserialize<GameObjectMetadata>(reader);
        return metadata.GetRealObject();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // WriteJson is never called when value == null.  Instead Json.NET calls writer.WriteNull() directly.
        var metadata = new GameObjectMetadata((UnityEngine.Object)value);
        serializer.Serialize(writer, metadata);
    }
}

Нет необходимости в GameObjectMetadataJsonConverter.

Примечание. Я не являюсь разработчиком unity3d, поэтому я не уверен, что AssetDatabase.LoadAssetAtPath(AssetFullPath, Type.GetType(UnityType)) является правильным методом для восстановления игрового объекта из его метаданные, поэтому замените на то, что правильно.

20.03.2017
  • Скала! Большое спасибо, приятель. И очень близко к догадке в Unity API для GetRealObject();) 21.03.2017
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]