3 using Microsoft.Xna.Framework;
5 using System.Collections.Immutable;
6 using System.Globalization;
8 using System.Reflection;
10 using System.Xml.Schema;
14 public static class StructSerialization
16 private static readonly ImmutableDictionary<Type, MethodInfo> deserializeMethods;
17 private static readonly ImmutableDictionary<Type, MethodInfo> serializeMethods;
21 private static bool ShouldSkip(
this FieldInfo field)
29 public readonly Func<string?, object?>
Read;
30 public readonly Func<object?, string?>
Write;
35 handlerType.GetMethod(nameof(
Read), BindingFlags.Public | BindingFlags.Static)
36 ??
throw new Exception($
"Type {handlerType.Name} does not have a static {nameof(Read)} method");
38 handlerType.GetMethod(nameof(
Write), BindingFlags.Public | BindingFlags.Static)
39 ??
throw new Exception($
"Type {handlerType.Name} does not have a static {nameof(Write)} method");
40 var paramArray =
new object?[1];
44 return readAction.Invoke(
null, paramArray);
49 return writeAction.Invoke(
null, paramArray)?.ToString();
54 static StructSerialization()
57 typeof(StructSerialization)
58 .GetMethods(BindingFlags.Static | BindingFlags.Public)
61 if (!m.Name.StartsWith(
"Deserialize")) { return false; }
62 var parameters = m.GetParameters();
63 if (parameters.Length < 1 || parameters.Length > 2 ||
64 parameters[0].ParameterType != typeof(
string))
70 .
Select(m => (m.ReturnType, m))
71 .ToImmutableDictionary();
74 typeof(StructSerialization)
75 .GetMethods(BindingFlags.Static | BindingFlags.Public)
78 if (!m.Name.StartsWith(
"Serialize")) { return false; }
79 var parameters = m.GetParameters();
80 if (parameters.Length != 1 ||
81 m.ReturnType != typeof(
string))
87 .
Select(m => (m.GetParameters()[0].ParameterType, m))
88 .ToImmutableDictionary();
91 public static void CopyPropertiesFrom<T>(
this ref T
self, in T other) where T :
struct
93 var fields =
self.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => !f.IsInitOnly).ToArray();
94 foreach (var field
in fields)
96 if (field.ShouldSkip()) {
continue; }
97 field.SetValue(
self, field.GetValue(other));
101 public static void DeserializeElement<T>(
this ref T
self, XElement element) where T :
struct
103 var fields =
self.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).ToArray();
107 object boxedSelf =
self;
108 foreach (var field
in fields)
110 if (field.ShouldSkip()) {
continue; }
111 boxedSelf.TryDeserialize(field, element);
117 private static void TryDeserialize(
this object boxedSelf, FieldInfo field, XElement element)
119 string fieldName = field.Name.ToLowerInvariant();
120 string valueStr = element.GetAttributeString(fieldName, field.GetValue(boxedSelf)?.ToString() ??
"");
122 var handler = field.ExtractHandler();
126 field.SetValue(boxedSelf, handler.Read(valueStr));
128 else if (deserializeMethods.TryGetValue(field.FieldType, out MethodInfo? deserializeMethod))
130 object?[] parameters = { valueStr };
131 if (deserializeMethod.GetParameters().Length > 1)
133 Array.Resize(ref parameters, 2);
134 parameters[1] = field.GetValue(boxedSelf);
136 field.SetValue(boxedSelf, deserializeMethod.Invoke(boxedSelf, parameters));
138 else if (field.FieldType.IsEnum)
140 field.SetValue(boxedSelf, DeserializeEnum(field.FieldType, valueStr, (Enum)field.GetValue(boxedSelf)!));
144 public static string DeserializeString(
string str)
149 public static bool DeserializeBool(
string str,
bool defaultValue)
151 if (
bool.TryParse(str, out
bool result)) {
return result; }
155 public static float DeserializeFloat(
string str,
float defaultValue)
157 if (
float.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out
float result)) {
return result; }
161 public static Int32 DeserializeInt32(
string str, Int32 defaultValue)
163 if (Int32.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out Int32 result)) {
return result; }
167 public static Identifier DeserializeIdentifier(
string str)
169 return str.ToIdentifier();
172 public static LanguageIdentifier DeserializeLanguageIdentifier(
string str)
174 return str.ToLanguageIdentifier();
177 public static Color DeserializeColor(
string str)
179 return XMLExtensions.ParseColor(str);
182 public static Enum DeserializeEnum(Type enumType,
string str, Enum defaultValue)
184 if (Enum.TryParse(enumType, str, out
object? result)) {
return (Enum)result!; }
188 public static void SerializeElement<T>(
this ref T
self, XElement element) where T :
struct
190 var fields =
self.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).ToArray();
192 foreach (var field
in fields)
194 if (field.ShouldSkip()) {
continue; }
195 self.TrySerialize(field, element);
199 public static void TrySerialize<T>(
this T
self, FieldInfo field, XElement element) where T :
struct
201 string fieldName = field.Name.ToLowerInvariant();
202 object? fieldValue = field.GetValue(
self);
204 string valueStr = fieldValue?.ToString() ??
"";
206 var handler = field.ExtractHandler();
210 valueStr = handler.Write(valueStr) ??
"";
212 else if (serializeMethods.TryGetValue(field.FieldType, out MethodInfo? method))
214 object?[] parameters = { fieldValue };
215 valueStr = (string)method.Invoke(
self, parameters)!;
218 element.SetAttributeValue(fieldName, valueStr);
221 public static string SerializeBool(
bool val)
222 => val ?
"true" :
"false";
224 public static string SerializeInt32(Int32 val)
225 => val.ToString(CultureInfo.InvariantCulture);
227 public static string SerializeFloat(
float val)
228 => val.ToString(CultureInfo.InvariantCulture);
230 public static string SerializeColor(Color val)
231 => val.ToStringHex();
readonly Func< object?, string?> Write
readonly Func< string?, object?> Read
HandlerAttribute(Type handlerType)