diff --git a/GDSerializer.csproj b/GDSerializer.csproj index 2128b12..4a5cdce 100644 --- a/GDSerializer.csproj +++ b/GDSerializer.csproj @@ -8,7 +8,7 @@ true true - 2.0.2 + 2.0.3 GDSerializer Carnagion An XML (de)serialization framework for Godot's C# API. diff --git a/README.md b/README.md index 9976fa7..0f96677 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ It supports (de)serialization of almost any C# type including collections and ma **GDSerializer** is available as a [NuGet package](https://www.nuget.org/packages/GDSerializer/), which can be installed either through an IDE or by manually including the following lines in a Godot project's `.csproj` file: ```xml - + ``` Its dependencies may need to be installed as well, in a similar fashion. diff --git a/Serialization/Serializer.cs b/Serialization/Serializer.cs index 77d4666..da3b280 100644 --- a/Serialization/Serializer.cs +++ b/Serialization/Serializer.cs @@ -103,7 +103,6 @@ public static bool CanSerialize(PropertyInfo property) && property.CanWrite && !property.GetIndexParameters().Any() && property.GetCustomAttribute() is null - && property.GetMethod.GetCustomAttribute() is null && !Serializer.forbiddenTypes.Contains(property.PropertyType) && (property.GetCustomAttribute()?.Serializable ?? true); } @@ -132,10 +131,9 @@ public XmlNode Serialize(object instance, Type? type = null) { type ??= instance.GetType(); - XmlNode element; - try { + XmlNode element; // Use a more specialized serializer if possible if (this.TryGetSpecialSerializerForType(type, out ISerializer? serializer)) { @@ -144,23 +142,10 @@ public XmlNode Serialize(object instance, Type? type = null) else { XmlDocument context = new(); - - // Use the "Type" attribute if generic or nested type as ` and + are not allowed as XML node names - if (type.IsGenericType) - { - element = context.CreateElement("Generic"); - ((XmlElement)element).SetAttribute("Type", type.GetDisplayName().XMLEscape()); - } - else if (type.IsNested) - { - element = context.CreateElement("Nested"); - ((XmlElement)element).SetAttribute("Type", type.GetDisplayName().XMLEscape()); - } - else - { - element = context.CreateElement(type.GetDisplayName()); - } - + element = context.CreateElement("Object"); + ((XmlElement)element).SetAttribute("Type", type.GetDisplayName()); + + // Serialize fields and properties this.SerializeMembers(instance, type).ForEach(pair => element.AppendChild(context.ImportNode(pair.Item1, true))); } @@ -273,11 +258,21 @@ public XmlNode Serialize(object instance, Type? type = null) { continue; } - XmlNode node = context.CreateElement(property.Name); - this.Serialize(value, property.PropertyType).ChildNodes + + XmlElement element = context.CreateElement(property.Name); + + XmlNode serialized = this.Serialize(value, value.GetType()); + string? typeAttribute = serialized.Attributes?["Type"]?.InnerText; + if (typeAttribute is not null) + { + element.SetAttribute("Type", typeAttribute); + } + + serialized.ChildNodes .Cast() - .ForEach(child => node.AppendChild(context.ImportNode(child, true))); - yield return (node, property); + .ForEach(child => element.AppendChild(context.ImportNode(child, true))); + + yield return (element, property); } // Recursively serialize fields @@ -288,11 +283,21 @@ public XmlNode Serialize(object instance, Type? type = null) { continue; } - XmlNode node= context.CreateElement(field.Name); - this.Serialize(value, field.FieldType).ChildNodes + + XmlElement element = context.CreateElement(field.Name); + + XmlNode serialized = this.Serialize(value, value.GetType()); + string? typeAttribute = serialized.Attributes?["Type"]?.InnerText; + if (typeAttribute is not null) + { + element.SetAttribute("Type", typeAttribute); + } + + serialized.ChildNodes .Cast() - .ForEach(child => node.AppendChild(context.ImportNode(child, true))); - yield return (node, field); + .ForEach(child => element.AppendChild(context.ImportNode(child, true))); + + yield return (element, field); } } @@ -320,7 +325,7 @@ public XmlNode Serialize(object instance, Type? type = null) { throw new SerializationException(child, $"Attempted to deserialize non-deserializable property {property.Name} in {type.GetDisplayName()}"); } - deserialized.Add((this.Deserialize(child, property.PropertyType), property)); + deserialized.Add((this.Deserialize(child, child.GetTypeToDeserialize() ?? property.PropertyType), property)); continue; } @@ -332,7 +337,7 @@ public XmlNode Serialize(object instance, Type? type = null) { throw new SerializationException(child, $"Attempted to deserialize non-deserializable field {field.Name} in {type.GetDisplayName()}"); } - deserialized.Add((this.Deserialize(child, field.FieldType), field)); + deserialized.Add((this.Deserialize(child, child.GetTypeToDeserialize() ?? field.FieldType), field)); continue; } @@ -345,7 +350,8 @@ public XmlNode Serialize(object instance, Type? type = null) .Where(pair => pair.Item2 is not null && pair.Item2.Serializable) .Select(pair => pair.member) .ToArray(); - if (toDeserialize.Any() && !toDeserialize.All(deserialized.Select(pair => pair.Item2).Contains)) + HashSet deserializedMembers = deserialized.Select(pair => pair.Item2).ToHashSet(); + if (toDeserialize.Any() && !toDeserialize.All(deserializedMembers.Contains)) { throw new SerializationException(node, $"One or more mandatory properties or fields of {type.GetDisplayName()} were not deserialized"); } diff --git a/Serialization/Specialized/ArraySerializer.cs b/Serialization/Specialized/ArraySerializer.cs index 29b6178..74e3821 100644 --- a/Serialization/Specialized/ArraySerializer.cs +++ b/Serialization/Specialized/ArraySerializer.cs @@ -39,7 +39,7 @@ public override XmlNode Serialize(object instance, Type? arrayType = null) XmlDocument context = new(); XmlElement arrayElement = context.CreateElement("Array"); - arrayElement.SetAttribute("Type", $"{itemType.GetDisplayName().XMLEscape()}[]"); + arrayElement.SetAttribute("Type", $"{itemType.GetDisplayName()}[]"); this.SerializeItems(instance, itemType).ForEach(node => arrayElement.AppendChild(context.ImportNode(node, true))); return arrayElement; } diff --git a/Serialization/Specialized/CollectionSerializer.cs b/Serialization/Specialized/CollectionSerializer.cs index 2f600ad..d49fdc7 100644 --- a/Serialization/Specialized/CollectionSerializer.cs +++ b/Serialization/Specialized/CollectionSerializer.cs @@ -50,7 +50,7 @@ public virtual XmlNode Serialize(object instance, Type? collectionType = null) XmlDocument context = new(); XmlElement collectionElement = context.CreateElement("Collection"); - collectionElement.SetAttribute("Type", collectionType.GetDisplayName().XMLEscape()); + collectionElement.SetAttribute("Type", collectionType.GetDisplayName()); this.SerializeItems(instance, itemType).ForEach(node => collectionElement.AppendChild(context.ImportNode(node, true))); return collectionElement; } @@ -97,7 +97,7 @@ protected IEnumerable SerializeItems(object instance, Type itemType) XmlElement itemElement = context.CreateElement("item"); if (item.GetType() != itemType) { - itemElement.SetAttribute("Type", item.GetType().GetDisplayName().XMLEscape()); + itemElement.SetAttribute("Type", item.GetType().GetDisplayName()); } this.ItemSerializer.Serialize(item, item.GetType()).ChildNodes .Cast() diff --git a/Serialization/Specialized/DictionarySerializer.cs b/Serialization/Specialized/DictionarySerializer.cs index fabbced..7dc481a 100644 --- a/Serialization/Specialized/DictionarySerializer.cs +++ b/Serialization/Specialized/DictionarySerializer.cs @@ -50,7 +50,7 @@ public XmlNode Serialize(object instance, Type? dictionaryType = null) XmlDocument context = new(); XmlElement dictionaryElement = context.CreateElement("Dictionary"); - dictionaryElement.SetAttribute("Type", dictionaryType.GetDisplayName().XMLEscape()); + dictionaryElement.SetAttribute("Type", dictionaryType.GetDisplayName()); foreach (object item in (IEnumerable)instance) { @@ -60,7 +60,7 @@ public XmlNode Serialize(object instance, Type? dictionaryType = null) XmlElement keyElement = context.CreateElement("key"); if (key.GetType() != keyType) { - keyElement.SetAttribute("Type", key.GetType().GetDisplayName().XMLEscape()); + keyElement.SetAttribute("Type", key.GetType().GetDisplayName()); } this.itemSerializer.Serialize(key, key.GetType()).ChildNodes .Cast() @@ -69,7 +69,7 @@ public XmlNode Serialize(object instance, Type? dictionaryType = null) XmlElement valueElement = context.CreateElement("value"); if (value.GetType() != valueType) { - valueElement.SetAttribute("Type", value.GetType().GetDisplayName().XMLEscape()); + valueElement.SetAttribute("Type", value.GetType().GetDisplayName()); } this.itemSerializer.Serialize(value, value.GetType()).ChildNodes .Cast() diff --git a/Serialization/Specialized/EnumerableSerializer.cs b/Serialization/Specialized/EnumerableSerializer.cs index 74b428e..08edc21 100644 --- a/Serialization/Specialized/EnumerableSerializer.cs +++ b/Serialization/Specialized/EnumerableSerializer.cs @@ -39,7 +39,7 @@ public override XmlNode Serialize(object instance, Type? enumerableType = null) XmlDocument context = new(); XmlElement enumerableElement = context.CreateElement("Enumerable"); - enumerableElement.SetAttribute("Type", enumerableType.GetDisplayName().XMLEscape()); + enumerableElement.SetAttribute("Type", enumerableType.GetDisplayName()); this.SerializeItems(instance, itemType).ForEach(node => enumerableElement.AppendChild(context.ImportNode(node, true))); return enumerableElement; } diff --git a/Serialization/Specialized/NodeSerializer.cs b/Serialization/Specialized/NodeSerializer.cs index 9fc58df..e638549 100644 --- a/Serialization/Specialized/NodeSerializer.cs +++ b/Serialization/Specialized/NodeSerializer.cs @@ -37,25 +37,10 @@ public override XmlNode Serialize(object instance, Type? nodeType = null) XmlDocument context = new(); - // Use the "Type" attribute if generic or nested type as ` and + are not allowed as XML node names - XmlElement element; - if (nodeType.IsGenericType) - { - element = context.CreateElement("Generic"); - element.SetAttribute("Type", nodeType.GetDisplayName().XMLEscape()); - } - else if (nodeType.IsNested) - { - element = context.CreateElement("Nested"); - element.SetAttribute("Type", nodeType.GetDisplayName().XMLEscape()); - } - else - { - element = context.CreateElement(nodeType.GetDisplayName()); - } - - Serializer defaultSerializer = (Serializer)this.ItemSerializer; + XmlElement element = context.CreateElement("Node"); + element.SetAttribute("Type", nodeType.GetDisplayName()); + Serializer defaultSerializer = (Serializer)this.ItemSerializer; defaultSerializer.SerializeMembers(nodeInstance, nodeType).ForEach(pair => element.AppendChild(context.ImportNode(pair.Item1, true))); if (nodeInstance.GetChildCount() is 0) @@ -90,7 +75,7 @@ public override object Deserialize(XmlNode node, Type? nodeType = null) { node.RemoveChild(childrenElement); } - + Serializer defaultSerializer = (Serializer)this.ItemSerializer; object instance = Activator.CreateInstance(nodeType, true) ?? throw new SerializationException(node, $"Unable to instantiate {nodeType.GetDisplayName()}");