Есть ли способ сериализации ссылок MonoBehaviours в JSON? - PullRequest
1 голос
/ 12 июня 2019

Допустим, у меня есть объект с именем ObjectA, который содержит два объекта: ObjectsB и ObjectC.

[System.Serializable]
public ClassA
{
    public ClassB classB;
    public ClassC classC;

    public ClassA()
    {
        classB = new ClassB();
        classC = new ClassC();
    }
}

[System.Serializable]
public ClassB
{
    //Some fields.
}

[System.Serializable]
public ClassC
{
    //Some fields.
}

Если я сериализую ObjectA в JSON, ObjectB и ObjectC не сериализуются. Вот что я получил после сериализации ObjectA в JSON:
{"_instanceB":{"instanceID":10480},"_instanceC":{"instanceID":10230}}
Мне нужно сериализовать все эти объекты в файл и сохранить его локально на жестком диске, чтобы потом можно было восстановить их состояния. Как я могу это сделать?
Должен ли я извлечь ClassB и ClassC из ClassA, затем сериализовать и сохранить их по отдельности? Примерно так:

public void Save()
{
    //Get instances of ClassB and ClassC.
    ClassB classB = classA.GetClassB;
    ClassC classC = classA.GetClassC;

    //Generate different JSON for each class.
    string classA_JSON = JsonUtility.ToJson(classA);
    string classB_JSON = JsonUtility.ToJson(classB);
    string classC_JSON = JsonUtility.ToJson(classC);

    //Paths for saving locally.
    string pathForClassA = Path.Combine("C:\\", "classA.json");
    string pathForClassB = Path.Combine("C:\\", "classB.json");
    string pathForClassC = Path.Combine("C:\\", "classC.json");

    File.WriteAllText(pathForClassA, classA_JSON);
    File.WriteAllText(pathForClassB, classB_JSON);
    File.WriteAllText(pathForClassC, classC_JSON);
}

Выглядит ужасно, и он будет генерировать новый файл JSON для каждого вложенного класса. Можно ли как-то сериализовать ClassA, включая вложенные классы, в один файл JSON?

P.S. Это проект Unity, и ClassA, ClassB и ClassC получены из MonoBehaviour. Поскольку BinaryFormatter не поддерживает MonoBehaviour, я не могу его использовать. Единственное, что мне осталось, это сериализовать его в JSON.

1 Ответ

0 голосов
/ 13 июня 2019

Можно ли как-то сериализовать ClassA, включая вложенные классы, в один файл JSON?

Да, вы могли бы, но это потребовало бы некоторой работы:

Вы можете иметь [Serializable] классы представления для ClassB и ClassC и использовать ISerializationCallbackReceiver интерфейс для заполнения и использовать их в ClassA

Нечто подобное, например,

public class ClassB : MonoBehaviour
{
    [SerializeField] private float example1;
    [SerializeField] private string example2;
    // etc.

    public void SetUp(SerializableClassB values)
    {
        // set all values
        this.example1 = values.example1;
        this.example2 = values.example2;
        // etc.
    }

    public SerializableClassB GetSerializable()
    {
        var output = new SerializableClassB();

        output.example1 = this.example1;
        output.example2 = this.example2;
        // etc.

        return output;
    }
}

[Serializable]
public class SerializableClassB
{
    public float example1;
    public string example2;
    // etc
}

И то же самое для ClassC

public class ClassC : MonoBehaviour
{
    [SerializeField] private float example3;
    [SerializeField] private string example4;
    // etc.

    public void SetUp(SerializableClassC values)
    {
        // set all values
        example3 = values.example3;
        example4 = values.example4;
        // etc.
    }

    public SerializableClassC GetSerializable()
    {
        var output = new SerializableClassC();

        output.example3 = example3;
        output.example4 = example4;
        // etc.

        return output;
    }
}

[Serializable]
public class SerializableClassC
{
    public float example3;
    public string example4;
    // etc
}

Тогда в ClassA вы можете сделать

public class ClassA : MonoBehaviour, ISerializationCallbackReceiver
{
    public ClassB _classB;
    public ClassC _classC;

    [SerializeField] private SerializableClassB _serializableClassB;
    [SerializeField] private SerializableClassC _serializeableClassC;


    public void OnBeforeSerialize()
    {
        // before writing to a Json get the information from the MonoBehaviours into the normal classes
        if(_classB) _serializableClassB = _classB.GetSerializable();
        if(_classC) _serializeableClassC = _classC.GetSerializable();

    }

    public void OnAfterDeserialize()
    {
        // after deserializing write the infromation from the normal classes into the MonoBehaviours
        if(_classB) _classB.SetUp(_serializableClassB);
        if(_classC) _classC.SetUp(_serializeableClassC);
    }
}

Вторым огромным преимуществом (побочным эффектом) является то, что теперь вы дополнительно можете также контролировать значения _classB и _classC непосредственно в экземпляре ClassA. Таким образом, вы можете изменять значения MonoBehaviour в классе централизованного менеджера.

enter image description here

После сериализации в JSON с использованием

private void Start()
{
    File.WriteAllText(Path.Combine(Application.streamingAssetsPath, "Test.txt"), JsonUtility.ToJson(this));
    AssetDatabase.Refresh();
}

теперь вы получите

{
    "_classB":{"instanceID":-6766},"_classC":{"instanceID":-6826},
    "_serializableClassB": {
            "example1":23.0,
            "example2":"54ththfghg"
    },
    "_serializeableClassC": {
            "example3":67.0,
            "example4":"567gffhgfhgf"
    }
}

Чем для примера я изменил его на

{
    "_classB":{"instanceID":-6766},"_classC":{"instanceID":-6826},
    "_serializableClassB": {
            "example1":47,
            "example2":"Hello"
    },
    "_serializeableClassC": {
            "example3":32.123,
            "example4":"World!"
    }
}

и десериализовано при запуске из json с использованием

private void Start()
{
    JsonUtility.FromJsonOverwrite(File.ReadAllText(Path.Combine(Application.streamingAssetsPath, "Test.txt")), this);
}

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...