< Summary

Class:Towel.Serialization
Assembly:Towel
File(s):File 1: /home/runner/work/Towel/Towel/Sources/Towel/Serialization.cs
Covered lines:153
Uncovered lines:79
Coverable lines:232
Total lines:496
Line coverage:65.9% (153 of 232)
Covered branches:54
Total branches:80
Branch coverage:67.5% (54 of 80)

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
File 1: .cctor()100%1100%
File 1: DefaultToXml(...)100%10%
File 1: DefaultToXml(...)100%10%
File 1: DefaultToXml(...)100%10%
File 1: DefaultToXml(...)100%10%
File 1: DefaultFromXml(...)100%10%
File 1: DefaultFromXml(...)100%10%
File 1: StaticDelegateToXml(...)100%1100%
File 1: StaticDelegateToXml(...)100%1100%
File 1: StaticDelegateToXml(...)100%10%
File 1: StaticDelegateToXml(...)87.5%894.28%
File 1: StaticDelegateFromXml(...)100%1100%
File 1: StaticDelegateFromXml(...)70.58%3466.66%
File 1: get_DeclaringType()100%1100%
File 1: get_MethodName()100%1100%
File 1: get_ParameterTypes()100%1100%
File 1: get_ReturnType()100%1100%
File 1: StaticDelegateToJson(...)75%1282.85%
File 1: StaticDelegateFromJson(...)53.84%2650.94%

File(s)

/home/runner/work/Towel/Towel/Sources/Towel/Serialization.cs

#LineLine coverage
 1using System.IO;
 2using System.Reflection;
 3using System.Text.Json;
 4using System.Xml;
 5using System.Xml.Serialization;
 6
 7namespace Towel;
 8
 9/// <summary>Static class containing serialization code.</summary>
 10public static partial class Serialization
 11{
 12  #region Shared
 13
 114  internal static readonly XmlWriterSettings DefaultXmlWriterSettings =
 115    new() { OmitXmlDeclaration = true, };
 16
 17  #endregion
 18
 19  #region System.Xml.Serialization.XmlSerializer .NET Wrapper (Default)
 20
 21  #region To XML
 22
 23  /// <summary>Wrapper for the default XML serialization in .NET using XmlSerializer.</summary>
 24  /// <typeparam name="T">The type of object to serialize.</typeparam>
 25  /// <param name="value">The value to serialize.</param>
 26  /// <returns>The XML serialzation of the value.</returns>
 27  public static string DefaultToXml<T>(T value) =>
 028    DefaultToXml(value, DefaultXmlWriterSettings);
 29
 30  /// <summary>Wrapper for the default XML serialization in .NET using XmlSerializer.</summary>
 31  /// <typeparam name="T">The type of object to serialize.</typeparam>
 32  /// <param name="value">The value to serialize.</param>
 33  /// <param name="xmlWriterSettings">The settings of the XML writer during serialization.</param>
 34  /// <returns>The XML serialzation of the value.</returns>
 35  public static string DefaultToXml<T>(T value, XmlWriterSettings xmlWriterSettings)
 036  {
 037    using StringWriter stringWriter = new();
 038    DefaultToXml(value, stringWriter, xmlWriterSettings);
 039    return stringWriter.ToString();
 040  }
 41
 42  /// <summary>Wrapper for the default XML serialization in .NET using XmlSerializer.</summary>
 43  /// <typeparam name="T">The type of object to serialize.</typeparam>
 44  /// <param name="value">The value to serialize.</param>
 45  /// <param name="textWriter">The text writer to output the XML serialization to.</param>
 46  public static void DefaultToXml<T>(T value, TextWriter textWriter) =>
 047    DefaultToXml(value, textWriter, DefaultXmlWriterSettings);
 48
 49  /// <summary>Wrapper for the default XML serialization in .NET using XmlSerializer.</summary>
 50  /// <typeparam name="T">The type of object to serialize.</typeparam>
 51  /// <param name="value">The value to serialize.</param>
 52  /// <param name="textWriter">The text writer to output the XML serialization to.</param>
 53  /// <param name="xmlWriterSettings">The settings of the XML writer during serialization.</param>
 54  public static void DefaultToXml<T>(T value, TextWriter textWriter, XmlWriterSettings xmlWriterSettings)
 055  {
 056    using XmlWriter xmlWriter = XmlWriter.Create(textWriter, xmlWriterSettings);
 057    XmlSerializer serializer = new(typeof(T));
 058    serializer.Serialize(xmlWriter, value);
 059  }
 60
 61  #endregion
 62
 63  #region From XML
 64
 65  /// <summary>Wrapper for the default XML deserialization in .NET using XmlSerializer.</summary>
 66  /// <typeparam name="T">The type of object to deserialize.</typeparam>
 67  /// <param name="string">The string containing the XML content to deserialize.</param>
 68  /// <returns>The deserialized value.</returns>
 69  public static T? DefaultFromXml<T>(string @string)
 070  {
 071    using StringReader stringReader = new(@string);
 072    return DefaultFromXml<T>(stringReader);
 073  }
 74
 75  /// <summary>Wrapper for the default XML deserialization in .NET using XmlSerializer.</summary>
 76  /// <typeparam name="T">The type of object to deserialize.</typeparam>
 77  /// <param name="textReader">The text reader providing the XML to deserialize.</param>
 78  /// <returns>The deserialized value.</returns>
 79  public static T? DefaultFromXml<T>(TextReader textReader)
 080  {
 081    XmlSerializer serializer = new(typeof(T));
 082    return (T?)serializer.Deserialize(textReader);
 083  }
 84
 85  #endregion
 86
 87  #endregion
 88
 89  #region Static Delegates
 90
 91  #region XML
 92
 93  internal static class StaticDelegateConstants
 94  {
 95    internal const string Name = "Delegate";
 96    internal const string DeclaringType = "Method.DeclaringType.AssemblyQualifiedName";
 97    internal const string MethodName = "Method.Name";
 98    internal const string Parameters = "Method.GetParameters";
 99    internal const string ParameterType = "ParameterType.AssemblyQualifiedName";
 100    internal const string ReturnType = "Method.ReturnType";
 101  }
 102
 103  #region To XML
 104
 105  /// <summary>Serializes a static delegate to XML.</summary>
 106  /// <typeparam name="T">The type of delegate to serialize.</typeparam>
 107  /// <param name="delegate">The delegate to serialize.</param>
 108  /// <returns>The XML serialization of the delegate.</returns>
 109  /// <exception cref="NotSupportedException">
 110  /// Thrown when the delegate is pointing to a non-static method.
 111  /// </exception>
 112  /// <exception cref="NotSupportedException">
 113  /// Thrown when the delegate is pointing to a local function.
 114  /// </exception>
 115  public static string StaticDelegateToXml<T>(T @delegate) where T : Delegate =>
 7116    StaticDelegateToXml(@delegate, DefaultXmlWriterSettings);
 117
 118  /// <summary>Serializes a static delegate to XML.</summary>
 119  /// <typeparam name="T">The type of delegate to serialize.</typeparam>
 120  /// <param name="delegate">The delegate to serialize.</param>
 121  /// <param name="xmlWriterSettings">The settings of the XML writer during serialization.</param>
 122  /// <returns>The XML serialization of the delegate.</returns>
 123  /// <exception cref="NotSupportedException">
 124  /// Thrown when the delegate is pointing to a non-static method.
 125  /// </exception>
 126  /// <exception cref="NotSupportedException">
 127  /// Thrown when the delegate is pointing to a local function.
 128  /// </exception>
 129  public static string StaticDelegateToXml<T>(T @delegate, XmlWriterSettings xmlWriterSettings) where T : Delegate
 7130  {
 7131    using StringWriter stringWriter = new();
 7132    StaticDelegateToXml(@delegate, stringWriter, xmlWriterSettings);
 4133    return stringWriter.ToString();
 4134  }
 135
 136  /// <summary>Serializes a static delegate to XML.</summary>
 137  /// <typeparam name="T">The type of delegate to serialize.</typeparam>
 138  /// <param name="delegate">The delegate to serialize.</param>
 139  /// <param name="textWriter">The text writer to output the XML serialization to.</param>
 140  /// <exception cref="NotSupportedException">
 141  /// Thrown when the delegate is pointing to a non-static method.
 142  /// </exception>
 143  /// <exception cref="NotSupportedException">
 144  /// Thrown when the delegate is pointing to a local function.
 145  /// </exception>
 146  public static void StaticDelegateToXml<T>(T @delegate, TextWriter textWriter) where T : Delegate =>
 0147    StaticDelegateToXml(@delegate, textWriter, DefaultXmlWriterSettings);
 148
 149  /// <summary>Serializes a static delegate to XML.</summary>
 150  /// <typeparam name="T">The type of delegate to serialize.</typeparam>
 151  /// <param name="delegate">The delegate to serialize.</param>
 152  /// <param name="textWriter">The text writer to output the XML serialization to.</param>
 153  /// <param name="xmlWriterSettings">The settings of the XML writer during serialization.</param>
 154  /// <exception cref="NotSupportedException">
 155  /// Thrown when the delegate is pointing to a non-static method.
 156  /// </exception>
 157  /// <exception cref="NotSupportedException">
 158  /// Thrown when the delegate is pointing to a local function.
 159  /// </exception>
 160  public static void StaticDelegateToXml<T>(T @delegate, TextWriter textWriter, XmlWriterSettings xmlWriterSettings) whe
 7161  {
 7162    using XmlWriter xmlWriter = XmlWriter.Create(textWriter, xmlWriterSettings);
 7163    MethodInfo methodInfo = @delegate.Method;
 7164    if (methodInfo.IsLocalFunction())
 1165    {
 1166      throw new NotSupportedException("delegates assigned to local functions are not supported");
 167    }
 6168    if (!methodInfo.IsStatic)
 2169    {
 2170      throw new NotSupportedException("delegates assigned to non-static methods are not supported");
 171    }
 4172    if (methodInfo.DeclaringType is null)
 0173    {
 0174      throw new ArgumentException($"{nameof(@delegate)}.{nameof(Delegate.Method)}.{nameof(MethodInfo.DeclaringType)} is 
 175    }
 4176    ParameterInfo[] parameterInfos = methodInfo.GetParameters();
 4177    xmlWriter.WriteStartElement(StaticDelegateConstants.Name);
 4178    {
 4179      xmlWriter.WriteStartElement(StaticDelegateConstants.DeclaringType);
 4180      xmlWriter.WriteString(methodInfo.DeclaringType.AssemblyQualifiedName);
 4181      xmlWriter.WriteEndElement();
 182
 4183      xmlWriter.WriteStartElement(StaticDelegateConstants.MethodName);
 4184      xmlWriter.WriteString(methodInfo.Name);
 4185      xmlWriter.WriteEndElement();
 186
 4187      xmlWriter.WriteStartElement(StaticDelegateConstants.Parameters);
 32188      for (int i = 0; i < parameterInfos.Length; i++)
 12189      {
 12190        xmlWriter.WriteStartElement(StaticDelegateConstants.ParameterType);
 12191        xmlWriter.WriteString(parameterInfos[i].ParameterType.AssemblyQualifiedName);
 12192        xmlWriter.WriteEndElement();
 12193      }
 4194      xmlWriter.WriteEndElement();
 195
 4196      xmlWriter.WriteStartElement(StaticDelegateConstants.ReturnType);
 4197      xmlWriter.WriteString(methodInfo.ReturnType.AssemblyQualifiedName);
 4198      xmlWriter.WriteEndElement();
 4199    }
 4200    xmlWriter.WriteEndElement();
 8201  }
 202
 203  #endregion
 204
 205  #region From XML
 206
 207  /// <summary>Deserializes a static delegate from XML.</summary>
 208  /// <typeparam name="T">The type of the delegate to deserialize.</typeparam>
 209  /// <param name="string">The string of XML content to deserialize.</param>
 210  /// <returns>The deserialized delegate.</returns>
 211  /// <exception cref="NotSupportedException">
 212  /// Thrown when the delegate is pointing to a non-static method.
 213  /// </exception>
 214  /// <exception cref="NotSupportedException">
 215  /// Thrown when the delegate is pointing to a local function.
 216  /// </exception>
 217  /// <exception cref="InvalidOperationException">
 218  /// Thrown when deserialization fails due to a return type mis-match.
 219  /// </exception>
 220  /// <exception cref="Exception">
 221  /// Thrown when deserialization fails. See the inner exception for more information.
 222  /// </exception>
 223  public static T StaticDelegateFromXml<T>(string @string) where T : Delegate
 2224  {
 2225    using StringReader stringReader = new(@string);
 2226    return StaticDelegateFromXml<T>(stringReader);
 2227  }
 228
 229  /// <summary>Deserializes a static delegate from XML.</summary>
 230  /// <typeparam name="TDelegate">The type of the delegate to deserialize.</typeparam>
 231  /// <param name="textReader">The text reader providing the XML to deserialize.</param>
 232  /// <returns>The deserialized delegate.</returns>
 233  /// <exception cref="NotSupportedException">
 234  /// Thrown when the delegate is pointing to a non-static method.
 235  /// </exception>
 236  /// <exception cref="NotSupportedException">
 237  /// Thrown when the delegate is pointing to a local function.
 238  /// </exception>
 239  /// <exception cref="InvalidOperationException">
 240  /// Thrown when deserialization fails due to a return type mis-match.
 241  /// </exception>
 242  /// <exception cref="Exception">
 243  /// Thrown when deserialization fails. See the inner exception for more information.
 244  /// </exception>
 245  public static TDelegate StaticDelegateFromXml<TDelegate>(TextReader textReader) where TDelegate : Delegate
 2246  {
 2247    string? declaringTypeString = null;
 2248    string? methodNameString = null;
 2249    ListArray<string> parameterTypeStrings = new();
 2250    string? returnTypeString = null;
 2251    using (XmlReader xmlReader = XmlReader.Create(textReader))
 2252    {
 10253      while (xmlReader.Read())
 8254      {
 20255      Loop:
 20256        if (xmlReader.NodeType is XmlNodeType.Element)
 16257        {
 16258          switch (xmlReader.Name)
 259          {
 260            case StaticDelegateConstants.DeclaringType:
 2261              declaringTypeString = xmlReader.ReadInnerXml();
 2262              goto Loop;
 263            case StaticDelegateConstants.MethodName:
 2264              methodNameString = xmlReader.ReadInnerXml();
 2265              goto Loop;
 266            case StaticDelegateConstants.ParameterType:
 6267              parameterTypeStrings.Add(xmlReader.ReadInnerXml());
 6268              goto Loop;
 269            case StaticDelegateConstants.ReturnType:
 2270              returnTypeString = xmlReader.ReadInnerXml();
 2271              goto Loop;
 272          }
 4273        }
 8274      }
 2275    }
 2276    if (methodNameString is null)
 0277    {
 0278      throw new ArgumentException("Deserialization failed due to missing name.");
 279    }
 2280    if (declaringTypeString is null)
 0281    {
 0282      throw new ArgumentException("Deserialization failed due to missing type.");
 283    }
 2284    if (returnTypeString is null)
 0285    {
 0286      throw new ArgumentException("Deserialization failed due to missing return type.");
 287    }
 2288    Type? declaringType = Type.GetType(declaringTypeString);
 2289    if (declaringType is null)
 0290    {
 0291      throw new ArgumentException("Deserialization failed due to an invalid type.");
 292    }
 2293    Type? returnType = Type.GetType(returnTypeString);
 294    MethodInfo? methodInfo;
 2295    if (parameterTypeStrings.Count > 0)
 2296    {
 2297      Type[] parameterTypes = new Type[parameterTypeStrings.Count];
 16298      for (int i = 0; i < parameterTypes.Length; i++)
 6299      {
 6300        Type? parameterType = Type.GetType(parameterTypeStrings[i]);
 6301        if (parameterType is null)
 0302        {
 0303          throw new ArgumentException("Deserialization failed due to an invalid parameter type.");
 304        }
 6305        parameterTypes[i] = parameterType;
 6306      }
 2307      methodInfo = declaringType.GetMethod(methodNameString, parameterTypes);
 2308    }
 309    else
 0310    {
 0311      methodInfo = declaringType.GetMethod(methodNameString);
 0312    }
 2313    if (methodInfo is null)
 0314    {
 0315      throw new ArgumentException("The method of the deserialization was not found.");
 316    }
 2317    if (methodInfo.IsLocalFunction())
 0318    {
 0319      throw new NotSupportedException("Delegates assigned to local functions are not supported.");
 320    }
 2321    if (!methodInfo.IsStatic)
 0322    {
 0323      throw new NotSupportedException("Delegates assigned to non-static methods are not supported.");
 324    }
 2325    if (methodInfo.ReturnType != returnType)
 0326    {
 0327      throw new ArgumentException("Deserialization failed due to a return type mis-match.");
 328    }
 329    try
 2330    {
 2331      return methodInfo.CreateDelegate<TDelegate>();
 332    }
 0333    catch (Exception exception)
 0334    {
 0335      throw new Exception("Deserialization failed.", exception);
 336    }
 2337  }
 338
 339  #endregion
 340
 341  #endregion
 342
 343  #region JSON
 344
 345  /// <summary>An object for the purposes of serializing static delegates.</summary>
 346  public static class Json
 347  {
 348    /// <summary>An object for the purposes of serializing static delegates.</summary>
 349    public class Delegate
 350    {
 351      /// <summary>The assemlby qualified declaring type of the method.</summary>
 14352      public string? DeclaringType { get; set; }
 353      /// <summary>The name of the method.</summary>
 14354      public string? MethodName { get; set; }
 355      /// <summary>The assembly qualified parameter types of the method.</summary>
 22356      public string[]? ParameterTypes { get; set; }
 357      /// <summary>The assembly qualified return type of the method.</summary>
 14358      public string? ReturnType { get; set; }
 359    }
 360  }
 361
 362  #region To JSON
 363
 364  /// <summary>Serializes a static delegate to JSON.</summary>
 365  /// <typeparam name="T">The type of delegate to serialize.</typeparam>
 366  /// <param name="delegate">The delegate to serialize.</param>
 367  /// <returns>The JSON serialization of the delegate.</returns>
 368  public static string StaticDelegateToJson<T>(T @delegate) where T : Delegate
 7369  {
 7370    if (@delegate is null)
 0371    {
 0372      throw new ArgumentNullException(nameof(@delegate));
 373    }
 7374    MethodInfo methodInfo = @delegate.Method;
 7375    if (methodInfo.DeclaringType is null)
 0376    {
 0377      throw new ArgumentException(message: $"{nameof(@delegate)} has a null DeclaringType", paramName: nameof(@delegate)
 378    }
 7379    if (methodInfo.IsLocalFunction())
 1380    {
 1381      throw new NotSupportedException("delegates assigned to local functions are not supported");
 382    }
 6383    if (!methodInfo.IsStatic)
 2384    {
 2385      throw new NotSupportedException("delegates assigned to non-static methods are not supported");
 386    }
 4387    ParameterInfo[] parameterInfos = methodInfo.GetParameters();
 4388    string[] parameterTypes = new string[parameterInfos.Length];
 32389    for (int i = 0; i < parameterTypes.Length; i++)
 12390    {
 12391      string? assemblyQualifiedName = parameterInfos[i].ParameterType.AssemblyQualifiedName;
 12392      if (assemblyQualifiedName is null)
 0393      {
 0394        throw new NotSupportedException("delegates with generic type definitions are not supported");
 395      }
 396      else
 12397      {
 12398        parameterTypes[i] = assemblyQualifiedName;
 12399      }
 12400    }
 401
 4402    Json.Delegate delegateObject = new()
 4403    {
 4404      DeclaringType = methodInfo.DeclaringType.AssemblyQualifiedName,
 4405      MethodName = methodInfo.Name,
 4406      ParameterTypes = parameterTypes,
 4407      ReturnType = methodInfo.ReturnType.AssemblyQualifiedName,
 4408    };
 409
 4410    return JsonSerializer.Serialize(delegateObject);
 4411  }
 412
 413  #endregion
 414
 415  #region From JSON
 416
 417  /// <summary>Deserializes a static delegate from JSON.</summary>
 418  /// <typeparam name="TDelegate">The type of the delegate to deserialize.</typeparam>
 419  /// <param name="string">The string of JSON content to deserialize.</param>
 420  /// <returns>The deserialized delegate.</returns>
 421  public static TDelegate StaticDelegateFromJson<TDelegate>(string @string) where TDelegate : Delegate
 2422  {
 2423    Json.Delegate? delegateObject = JsonSerializer.Deserialize<Json.Delegate>(@string);
 2424    if (delegateObject is null)
 0425    {
 0426      throw new ArgumentException(message: $"JSON deserialization resulted in null", paramName: nameof(@string));
 427    }
 2428    if (delegateObject.DeclaringType is null)
 0429    {
 0430      throw new ArgumentException("Deserialization failed due to missing type.");
 431    }
 2432    if (delegateObject.ReturnType is null)
 0433    {
 0434      throw new ArgumentException("Deserialization failed due to missing return type.");
 435    }
 2436    if (delegateObject.MethodName is null)
 0437    {
 0438      throw new ArgumentException("Deserialization failed due to missing name.");
 439    }
 2440    Type? declaringType = Type.GetType(delegateObject.DeclaringType);
 2441    if (declaringType is null)
 0442    {
 0443      throw new ArgumentException("Deserialization failed due to an invalid type.");
 444    }
 2445    Type? returnType = Type.GetType(delegateObject.ReturnType);
 446    MethodInfo? methodInfo;
 2447    if (delegateObject.ParameterTypes is not null && delegateObject.ParameterTypes.Length > 0)
 2448    {
 2449      Type[] parameterTypes = new Type[delegateObject.ParameterTypes.Length];
 16450      for (int i = 0; i < parameterTypes.Length; i++)
 6451      {
 6452        Type? parameterType = Type.GetType(delegateObject.ParameterTypes[i]);
 6453        if (parameterType is null)
 0454        {
 0455          throw new ArgumentException("Deserialization failed due to an invalid parameter type.");
 456        }
 6457        parameterTypes[i] = parameterType;
 6458      }
 2459      methodInfo = declaringType.GetMethod(delegateObject.MethodName, parameterTypes);
 2460    }
 461    else
 0462    {
 0463      methodInfo = declaringType.GetMethod(delegateObject.MethodName);
 0464    }
 2465    if (methodInfo is null)
 0466    {
 0467      throw new ArgumentException("The method of the deserialization was not found.");
 468    }
 2469    if (methodInfo.IsLocalFunction())
 0470    {
 0471      throw new NotSupportedException("Delegates assigned to local functions are not supported.");
 472    }
 2473    if (!methodInfo.IsStatic)
 0474    {
 0475      throw new NotSupportedException("Delegates assigned to non-static methods are not supported.");
 476    }
 2477    if (methodInfo.ReturnType != returnType)
 0478    {
 0479      throw new ArgumentException("Deserialization failed due to a return type mis-match.");
 480    }
 481    try
 2482    {
 2483      return methodInfo.CreateDelegate<TDelegate>();
 484    }
 0485    catch (Exception exception)
 0486    {
 0487      throw new Exception("Deserialization failed.", exception);
 488    }
 2489  }
 490
 491  #endregion
 492
 493  #endregion
 494
 495  #endregion
 496}