- Home /
How to serialize and deserialize components at runtime?
Hello.
I'm working on a dynamic loading system, and need to serialize and deserialize components in the editor and in the runtime.
In the editor, it is easy as there are some classes available, but they aren't available in the runtime.
My aproach was to find all public fields as well as the nonpublic fields where a "SerializeField" attribute is applied to. Then I would serialize those fields with the XMLSerializer.
The problem is, I don't get the fields from any components other than my own.
The code I use to serialize a component:
public class ComponentContent
{
public List<FieldContainer> fields;
public Type componentType;
// Serializes the component with all it's fields
public static ComponentContent SerializeComponent(Component component)
{
var cc = new ComponentContent();
var type = component.GetType();
cc.componentType = type;
// By using this code, fields is an empty array by most components
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic);
foreach (var field in fields)
{
// Only use public fields or fields with a SerializeField attribute
if (field.IsPublic || field.IsDefined(typeof(SerializeField), true))
{
cc.fields.Add(FieldContainer.SerializeField(field.GetValue(component), field.Name));
}
}
return cc;
}
public Component DeserializeComponent()
{
// Not yet implemented
return null;
}
}
And the code to serialize a field and store it's values:
public class FieldContainer
{
public string fieldName;
public Type fieldType;
public string fieldValue;
// Serializes the value of the field and returns a setup FieldContainer
public static FieldContainer SerializeField(object obj, string fieldName)
{
var fc = new FieldContainer();
Type type = obj.GetType();
// Serializes the field using XmlSerializer
var xmlSerializer = new XmlSerializer(type);
using (var textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, obj);
fc.fieldValue = textWriter.ToString();
}
fc.fieldType = type;
fc.fieldName = fieldName;
return fc;
}
// Returns the deserialized value of the field
public object DeserializeField()
{
// Deserializes the stored data to the field value using XmlSerializer
var xmlSerializer = new XmlSerializer(this.fieldType);
using (var textReader = new StringReader(this.fieldValue))
{
return xmlSerializer.Deserialize(textReader);
}
}
}
Can somebody tell me, why I don't get the fields in most components (In the first script)?
Greetings
Chillersanim
I'm not so sure this could would successfully [de]serialize UnityEngine.Objects
, since, first of all how do you know that it's safe to just serialize public fields? and, UEObjects fields are not mocked with [SerializeField]
- remember, if you want to serialize A
where A : $$anonymous$$onoBehaviour
you'd have to serialize the stuff that's in $$anonymous$$onoBehaviour
too, and since $$anonymous$$onoBehaviour
is a Behaviour
, which is a Component
, which is a Object
, you'd have to serialize everything there too - and I'm pretty sure not everything's there is mocked with [SerializeField]
.
And even though you managed to somehow successfully serialize a UEObject, how are you gonna 'safely' deserialize it? Cause you know, there's a reason why Unity doesn't allow you to create Components with the 'new' operator (using constructors) - that's cause it does some home cooking/ internal prepping for you from the inside - I suspect the deserializer could do these things for you.
Please go on though, I'm very interested in this - Let me know if you have any success :)
I would be especially interessted to know, how the unity serializer works. I wan't to implement a simillar construct, but one that works also in runtime.
The problem with "SerializeField" is, that I check it after the type.GetFields() call, so that should not be the problem...
Answer by vexe · Apr 27, 2014 at 12:55 PM
You need to add BindingFlags.Instance
.
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
EDIT:
The bindings above, do get you the fields you want (if any) - but it seems that UE.Component
and many others (Transform, MonoBehaviour, etc) don't have any fields, they're just auto-properties which the compiler turn into set_xxx and get_xxx
If I were to make a serialization system, I'd probably let the [de]serialization of UEObjects to Unity and handle myself what Unity can't, pretty much like Full Inspector.
Even with that BindingFlags.Instance, I do not get any fields from components like Transform or $$anonymous$$eshFilter.
They do have fields, do they?
Well if you go to their definitions, you'll mostly find auto-properties - which the compiler turns into get_XXX and set_XXX with a backing field - But yea you should get fields, let me try.
I thought, that the Unity serializer isn't able to serialize properties?
Or is it just not able to serialize customized properties?
When I use the line:
var properties = type.GetProperties( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty);
... then I find about 40 properties in the Transform component.
But I want to serialize like the unity internal serialize, in order to get the same result.
That is important, because I need to load (and unload) objects like Unity, in order to create the same instantiating behaviour as Unity.
If I don't do that, then there can be problems with the calls of OnEnable etc, because some properties then already have the wrong value...
Also I would call the get/set method of the properties, before the initializing through the OnEnable/Start/Awake and that could lead to errors
Why don't you let Unity do it for you? can you talk more about your dynamic loading system, maybe there's another approach you could take? if not, then consider taking a peek at Unity serializer's code.
Your answer
Follow this Question
Related Questions
Cannot convert object[] to Vector3[] 2 Answers
Multiple Cars not working 1 Answer
Serialization at runtime 0 Answers
Distribute terrain in zones 3 Answers
pass child class to ScriptableObject.CreateInstance<> ? 1 Answer