Serialize properties
This commit is contained in:
1 parent
1698c1b027
commit
af88bb2161
3 files changed
+163
-87
No files matched your search
@@ -40,9 +40,15 @@ namespace VocaluxeLib.Xml
|
|||||||
|
|
||||||
public struct SFieldInfo
|
public struct SFieldInfo
|
||||||
{
|
{
|
||||||
|
private readonly FieldInfo _Field;
|
||||||
|
private readonly PropertyInfo _Property;
|
||||||
|
|
||||||
|
public Type Type
|
||||||
|
{
|
||||||
|
get { return _Field != null ? _Field.FieldType : _Property.PropertyType; }
|
||||||
|
}
|
||||||
public string Name;
|
public string Name;
|
||||||
public string AltName;
|
public string AltName;
|
||||||
public FieldInfo Info;
|
|
||||||
public object DefaultValue;
|
public object DefaultValue;
|
||||||
public bool HasDefaultValue;
|
public bool HasDefaultValue;
|
||||||
public bool IsAttribute;
|
public bool IsAttribute;
|
||||||
@@ -54,6 +60,31 @@ namespace VocaluxeLib.Xml
|
|||||||
public XmlRangedAttribute Ranged;
|
public XmlRangedAttribute Ranged;
|
||||||
public Type SubType;
|
public Type SubType;
|
||||||
public string ArrayItemName;
|
public string ArrayItemName;
|
||||||
|
|
||||||
|
public SFieldInfo(FieldInfo field)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
_Field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SFieldInfo(PropertyInfo property)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
_Property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(object o, object value)
|
||||||
|
{
|
||||||
|
if (_Field != null)
|
||||||
|
_Field.SetValue(o, value);
|
||||||
|
else
|
||||||
|
_Property.SetValue(o, value, new object[] {});
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetValue(object o)
|
||||||
|
{
|
||||||
|
return (_Field != null) ?_Field.GetValue(o) :_Property.GetValue(o, new object[] {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CTypeExtensions
|
public static class CTypeExtensions
|
||||||
@@ -61,6 +92,86 @@ namespace VocaluxeLib.Xml
|
|||||||
private static readonly Dictionary<Type, List<SFieldInfo>> _CacheFields = new Dictionary<Type, List<SFieldInfo>>();
|
private static readonly Dictionary<Type, List<SFieldInfo>> _CacheFields = new Dictionary<Type, List<SFieldInfo>>();
|
||||||
private static readonly Dictionary<Type, String> _CacheTypeName = new Dictionary<Type, string>();
|
private static readonly Dictionary<Type, String> _CacheTypeName = new Dictionary<Type, string>();
|
||||||
|
|
||||||
|
private static void _FillInfo(ref SFieldInfo info, MemberInfo field)
|
||||||
|
{
|
||||||
|
XmlAttributeAttribute attribute = field.GetAttribute<XmlAttributeAttribute>();
|
||||||
|
if (attribute != null)
|
||||||
|
{
|
||||||
|
info.IsAttribute = true;
|
||||||
|
info.Name = attribute.AttributeName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XmlElementAttribute element = field.GetAttribute<XmlElementAttribute>();
|
||||||
|
if (element != null)
|
||||||
|
info.Name = element.ElementName;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XmlArrayAttribute array = field.GetAttribute<XmlArrayAttribute>();
|
||||||
|
if (array != null)
|
||||||
|
{
|
||||||
|
Debug.Assert(info.Type.IsList() || info.Type.IsArray, "Only lists and arrays can have the array attribute");
|
||||||
|
Debug.Assert(!info.IsAttribute, "Lists cannot be attributes");
|
||||||
|
info.Name = array.ElementName;
|
||||||
|
info.IsList = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(info.Name))
|
||||||
|
info.Name = field.Name;
|
||||||
|
XmlAltNameAttribute altName = field.GetAttribute<XmlAltNameAttribute>();
|
||||||
|
if (altName != null)
|
||||||
|
info.AltName = altName.AltName;
|
||||||
|
|
||||||
|
if (info.Type.IsGenericType)
|
||||||
|
info.SubType = info.Type.GetGenericArguments().Last();
|
||||||
|
if (info.Type.IsArray)
|
||||||
|
info.SubType = info.Type.GetElementType();
|
||||||
|
if (info.Type.IsList() || info.Type.IsArray)
|
||||||
|
{
|
||||||
|
if (!info.IsList)
|
||||||
|
{
|
||||||
|
Debug.Assert(!field.HasAttribute<XmlArrayAttribute>(), "A field cannot have an XmlElement- and XmlArray-Attribute");
|
||||||
|
info.IsEmbeddedList = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XmlArrayItemAttribute arrayItem = field.GetAttribute<XmlArrayItemAttribute>();
|
||||||
|
if (arrayItem != null && !string.IsNullOrEmpty(arrayItem.ElementName))
|
||||||
|
info.ArrayItemName = arrayItem.ElementName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (info.Type.IsNullable())
|
||||||
|
info.IsNullable = true;
|
||||||
|
else if (info.Type.IsDictionary())
|
||||||
|
{
|
||||||
|
Debug.Assert(info.Type.GetGenericArguments()[0] == typeof(string), "Keys of dictionaries must be strings");
|
||||||
|
info.IsDictionary = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.HasAttribute<XmlNormalizedAttribute>())
|
||||||
|
{
|
||||||
|
Debug.Assert(info.Type == typeof(float) || (info.IsNullable && info.SubType == typeof(float)), "Only floats can be normalized");
|
||||||
|
info.IsNormalized = true;
|
||||||
|
}
|
||||||
|
info.Ranged = field.GetAttribute<XmlRangedAttribute>();
|
||||||
|
Debug.Assert(info.Ranged == null || info.Type == typeof(int) || info.Type == typeof(float) || info.Type == typeof(double),
|
||||||
|
"Only ints,floats and double can be ranged");
|
||||||
|
|
||||||
|
DefaultValueAttribute defAttr = field.GetAttribute<DefaultValueAttribute>();
|
||||||
|
if (defAttr != null)
|
||||||
|
{
|
||||||
|
Debug.Assert(!info.Type.IsList(), "Lists cannot have a default value");
|
||||||
|
info.HasDefaultValue = true;
|
||||||
|
info.DefaultValue = defAttr.Value;
|
||||||
|
}
|
||||||
|
else if (info.IsNullable)
|
||||||
|
{
|
||||||
|
info.HasDefaultValue = true;
|
||||||
|
info.DefaultValue = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<SFieldInfo> GetFieldInfos(this Type type)
|
public static IEnumerable<SFieldInfo> GetFieldInfos(this Type type)
|
||||||
{
|
{
|
||||||
List<SFieldInfo> result;
|
List<SFieldInfo> result;
|
||||||
@@ -72,83 +183,17 @@ namespace VocaluxeLib.Xml
|
|||||||
{
|
{
|
||||||
if (field.HasAttribute<XmlIgnoreAttribute>())
|
if (field.HasAttribute<XmlIgnoreAttribute>())
|
||||||
continue;
|
continue;
|
||||||
XmlAttributeAttribute attribute = field.GetAttribute<XmlAttributeAttribute>();
|
SFieldInfo info = new SFieldInfo(field);
|
||||||
SFieldInfo info = new SFieldInfo {Info = field};
|
_FillInfo(ref info, field);
|
||||||
if (attribute != null)
|
result.Add(info);
|
||||||
{
|
}
|
||||||
info.IsAttribute = true;
|
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||||
info.Name = attribute.AttributeName;
|
foreach (PropertyInfo property in properties)
|
||||||
}
|
{
|
||||||
else
|
if (property.HasAttribute<XmlIgnoreAttribute>())
|
||||||
{
|
continue;
|
||||||
XmlElementAttribute element = field.GetAttribute<XmlElementAttribute>();
|
SFieldInfo info = new SFieldInfo(property);
|
||||||
if (element != null)
|
_FillInfo(ref info, property);
|
||||||
info.Name = element.ElementName;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
XmlArrayAttribute array = field.GetAttribute<XmlArrayAttribute>();
|
|
||||||
if (array != null)
|
|
||||||
{
|
|
||||||
Debug.Assert(field.FieldType.IsList() || field.FieldType.IsArray, "Only lists and arrays can have the array attribute");
|
|
||||||
Debug.Assert(!info.IsAttribute, "Lists cannot be attributes");
|
|
||||||
info.Name = array.ElementName;
|
|
||||||
info.IsList = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(info.Name))
|
|
||||||
info.Name = field.Name;
|
|
||||||
XmlAltNameAttribute altName = field.GetAttribute<XmlAltNameAttribute>();
|
|
||||||
if (altName != null)
|
|
||||||
info.AltName = altName.AltName;
|
|
||||||
|
|
||||||
if (field.FieldType.IsGenericType)
|
|
||||||
info.SubType = field.FieldType.GetGenericArguments().Last();
|
|
||||||
if (field.FieldType.IsArray)
|
|
||||||
info.SubType = field.FieldType.GetElementType();
|
|
||||||
if (field.FieldType.IsList() || field.FieldType.IsArray)
|
|
||||||
{
|
|
||||||
if (!info.IsList)
|
|
||||||
{
|
|
||||||
Debug.Assert(!field.HasAttribute<XmlArrayAttribute>(), "A field cannot have an XmlElement- and XmlArray-Attribute");
|
|
||||||
info.IsEmbeddedList = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
XmlArrayItemAttribute arrayItem = field.GetAttribute<XmlArrayItemAttribute>();
|
|
||||||
if (arrayItem != null && !string.IsNullOrEmpty(arrayItem.ElementName))
|
|
||||||
info.ArrayItemName = arrayItem.ElementName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (field.FieldType.IsNullable())
|
|
||||||
info.IsNullable = true;
|
|
||||||
else if (field.FieldType.IsDictionary())
|
|
||||||
{
|
|
||||||
Debug.Assert(field.FieldType.GetGenericArguments()[0] == typeof(string), "Keys of dictionaries must be strings");
|
|
||||||
info.IsDictionary = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.HasAttribute<XmlNormalizedAttribute>())
|
|
||||||
{
|
|
||||||
Debug.Assert(field.FieldType == typeof(float) || (info.IsNullable && info.SubType == typeof(float)), "Only floats can be normalized");
|
|
||||||
info.IsNormalized = true;
|
|
||||||
}
|
|
||||||
info.Ranged = field.GetAttribute<XmlRangedAttribute>();
|
|
||||||
Debug.Assert(info.Ranged == null || field.FieldType == typeof(int) || field.FieldType == typeof(float) || field.FieldType == typeof(double),
|
|
||||||
"Only ints,floats and double can be ranged");
|
|
||||||
|
|
||||||
DefaultValueAttribute defAttr = field.GetAttribute<DefaultValueAttribute>();
|
|
||||||
if (defAttr != null)
|
|
||||||
{
|
|
||||||
Debug.Assert(!field.FieldType.IsList(), "Lists cannot have a default value");
|
|
||||||
info.HasDefaultValue = true;
|
|
||||||
info.DefaultValue = defAttr.Value;
|
|
||||||
}
|
|
||||||
else if (info.IsNullable)
|
|
||||||
{
|
|
||||||
info.HasDefaultValue = true;
|
|
||||||
info.DefaultValue = null;
|
|
||||||
}
|
|
||||||
result.Add(info);
|
result.Add(info);
|
||||||
}
|
}
|
||||||
_CacheFields.Add(type, result);
|
_CacheFields.Add(type, result);
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ namespace VocaluxeLib.Xml
|
|||||||
}
|
}
|
||||||
if (!field.HasDefaultValue)
|
if (!field.HasDefaultValue)
|
||||||
return false;
|
return false;
|
||||||
field.Info.SetValue(result, field.DefaultValue);
|
field.SetValue(result, field.DefaultValue);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ namespace VocaluxeLib.Xml
|
|||||||
|
|
||||||
private static void _AddList(object result, SFieldInfo listField, ICollection list)
|
private static void _AddList(object result, SFieldInfo listField, ICollection list)
|
||||||
{
|
{
|
||||||
listField.Info.SetValue(result, _CreateList(listField.Info.FieldType, list));
|
listField.SetValue(result, _CreateList(listField.Type, list));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _ReadChildNodes(XmlNode parent, ref object result, bool attributes)
|
private void _ReadChildNodes(XmlNode parent, ref object result, bool attributes)
|
||||||
@@ -279,7 +279,7 @@ namespace VocaluxeLib.Xml
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
object value = _GetValue(node, field.Info.FieldType, field.ArrayItemName);
|
object value = _GetValue(node, field.Type, field.ArrayItemName);
|
||||||
if (field.IsNormalized)
|
if (field.IsNormalized)
|
||||||
{
|
{
|
||||||
if (field.IsNullable && !((float?)value).Value.IsInRange(0, 1) ||
|
if (field.IsNullable && !((float?)value).Value.IsInRange(0, 1) ||
|
||||||
@@ -289,11 +289,11 @@ namespace VocaluxeLib.Xml
|
|||||||
if (field.Ranged != null && value != null)
|
if (field.Ranged != null && value != null)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
if (field.Info.FieldType == typeof(int))
|
if (field.Type == typeof(int))
|
||||||
ok = ((int)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
ok = ((int)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
||||||
else if (field.Info.FieldType == typeof(float))
|
else if (field.Type == typeof(float))
|
||||||
ok = ((float)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
ok = ((float)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
||||||
else if (field.Info.FieldType == typeof(double))
|
else if (field.Type == typeof(double))
|
||||||
ok = ((double)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
ok = ((double)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
||||||
else
|
else
|
||||||
Debug.Assert(false, "Ranged attribute has invalid type");
|
Debug.Assert(false, "Ranged attribute has invalid type");
|
||||||
@@ -303,7 +303,7 @@ namespace VocaluxeLib.Xml
|
|||||||
")");
|
")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field.Info.SetValue(result, value);
|
field.SetValue(result, value);
|
||||||
curField++;
|
curField++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +386,7 @@ namespace VocaluxeLib.Xml
|
|||||||
IEnumerable<SFieldInfo> fields = o.GetType().GetFieldInfos();
|
IEnumerable<SFieldInfo> fields = o.GetType().GetFieldInfos();
|
||||||
foreach (SFieldInfo field in fields)
|
foreach (SFieldInfo field in fields)
|
||||||
{
|
{
|
||||||
object value = field.Info.GetValue(o);
|
object value = field.GetValue(o);
|
||||||
if (!_WriteDefaults && field.HasDefaultValue && Equals(value, field.DefaultValue))
|
if (!_WriteDefaults && field.HasDefaultValue && Equals(value, field.DefaultValue))
|
||||||
continue;
|
continue;
|
||||||
if (field.IsEmbeddedList)
|
if (field.IsEmbeddedList)
|
||||||
@@ -396,7 +396,7 @@ namespace VocaluxeLib.Xml
|
|||||||
_WriteNode(writer, field.Name, field.SubType, subValue, field.IsAttribute);
|
_WriteNode(writer, field.Name, field.SubType, subValue, field.IsAttribute);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_WriteNode(writer, field.Name, field.Info.FieldType, value, field.IsAttribute, field.ArrayItemName);
|
_WriteNode(writer, field.Name, field.Type, value, field.IsAttribute, field.ArrayItemName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -56,6 +56,21 @@ namespace VocaluxeTests
|
|||||||
[XmlElement("Entry")] public SEntry[] Ints;
|
[XmlElement("Entry")] public SEntry[] Ints;
|
||||||
public int J;
|
public int J;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct SProperty
|
||||||
|
{
|
||||||
|
[XmlIgnore] public int Private;
|
||||||
|
// ReSharper disable UnusedMember.Local
|
||||||
|
public int Public
|
||||||
|
{
|
||||||
|
get { return Private - 1; }
|
||||||
|
set { Private = value + 1; }
|
||||||
|
}
|
||||||
|
// ReSharper restore UnusedMember.Local
|
||||||
|
// ReSharper disable UnusedAutoPropertyAccessor.Local
|
||||||
|
public int Auto { get; set; }
|
||||||
|
// ReSharper restore UnusedAutoPropertyAccessor.Local
|
||||||
|
}
|
||||||
#pragma warning restore 169
|
#pragma warning restore 169
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
@@ -285,6 +300,22 @@ namespace VocaluxeTests
|
|||||||
_AssertFail<XmlException>(() => xml.DeserializeString<SArrayEmb>(_Empty));
|
_AssertFail<XmlException>(() => xml.DeserializeString<SArrayEmb>(_Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string _XmlProperty = _Head + @"<root>
|
||||||
|
<Public>2</Public>
|
||||||
|
<Auto>3</Auto>
|
||||||
|
</root>";
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestProperty()
|
||||||
|
{
|
||||||
|
var xml = new CXmlSerializer();
|
||||||
|
SProperty foo = xml.DeserializeString<SProperty>(_XmlProperty);
|
||||||
|
Assert.AreEqual(3, foo.Private);
|
||||||
|
Assert.AreEqual(3, foo.Auto);
|
||||||
|
string newXml = xml.Serialize(foo);
|
||||||
|
Assert.AreEqual(_XmlProperty, newXml);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestRealFiles()
|
public void TestRealFiles()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user