Допустим, у меня есть объект с именем 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.
@PankajKapare JsonUtility
документация доступна здесь
@Ruzihm Да, я знаю об этих ограничениях. ClassB
и ClassC
имеют простые поля типа float, которые помечены атрибутом SerializeField.
Поскольку ClassB
и ClassC
не являются MonoBehaviour
s, вы решили сделать их structs
? Я предполагаю, что в противном случае вы могли бы получить ссылки на класс как сериализованное значение вместо фактических значений... хотя не уверен
@derHugo Отображает ли Unity поля структуры в инспекторе? Мне нужно иметь возможность редактировать их.
он должен ... единственная разница между class
и struct
заключается в том, что class
является ссылочным типом, а struct
типом значения
Черт, я забыл, что ClassB
и ClassC
также происходят от MonoBehaviour, они не были MonoBehaviours
с самого начала.. но позже мне пришлось сделать их, чтобы можно было ссылаться на них из другого игрового объекта.
so could reference to them from another game object
что вы имеете в виду? Это компоненты, прикрепленные к GameObjects?
С самого начала это были простые классы С#. но позже мне пришлось сделать их MonoBehaviours
и добавить как компоненты, чтобы можно было ссылаться на них из другого игрового объекта.
Должны ли они быть компонентами, на которые можно ссылаться из другого игрового объекта? Можете ли вы иметь компонент в GameObject, к которому вы бы их прикрепили, вместо того, чтобы иметь геттер, который их возвращает? Тогда это могли быть обычные занятия. И для ваших целей редактирования инспектора используйте таможенный инспектор для редактирования ClassB
/ClassC
.
Это именно то, о чем я сейчас думал: D Я попробую и посмотрю, сработает ли это.
@blablaalb см. этот ответ для получения дополнительной информации
Спасибо @Ruzihm и @derHugo за помощь. Я попытаюсь реорганизовать свой код и сделать ClassB
и ClassC
чистые классы C# и получить для них геттерные свойства из ClassA
.
Can I somehow serialize ClassA including its nested classes into one JSON file?
Да, вы могли бы, но это потребует некоторой работы:
Вы можете иметь [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 в централизованном классе менеджера.
После сериализации в 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);
}
Можете ли вы показать нам код для функции JsonUtility.ToJson?