using System; using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Xml; namespace ser; // --- Primitive Handler --- public partial class PrimitiveHandler : ser.ITypeHandler { public bool CanHandle( TypeInfo ti, XmlElement? elem ) { var typeCode = Type.GetTypeCode( ti.Type ); var typeNotObject = Type.GetTypeCode( ti.Type ) != TypeCode.Object; var isString = ti.Type == typeof( string ); return typeNotObject | isString; } } // --- Proxy Handler --- public partial class ProxyHandler : ITypeHandler { public bool CanHandle( TypeInfo ti, XmlElement? elem ) => ti.IsProxy || ( elem?.HasAttribute( "proxy" ) ?? false ); } // --- ISerializable Handler --- public partial class ISerializableHandler : ITypeHandler { public bool CanHandle( TypeInfo ti, XmlElement? elem ) => ti.IsISerializable; } // --- Collection Handler --- public partial class CollectionHandler : ITypeHandler { public bool CanHandle( TypeInfo ti, XmlElement? elem ) => typeof( IEnumerable ).IsAssignableFrom( ti.Type ); private Type GetElementType( Type collectionType ) { if( collectionType.IsArray ) return collectionType.GetElementType()!; if( collectionType.IsGenericType ) { var args = collectionType.GetGenericArguments(); if( args.Length == 1 ) return args[0]; if( args.Length == 2 ) return typeof( KeyValuePair<,> ).MakeGenericType( args ); } return typeof( object ); // Fallback } private object ConvertToFinalCollection( IList list, Type expectedType, Type elemType ) { if( expectedType.IsArray ) { var arr = Array.CreateInstance( elemType, list.Count ); list.CopyTo( arr, 0 ); return arr; } if( expectedType.IsGenericType ) { var genDef = expectedType.GetGenericTypeDefinition(); if( genDef == typeof( ImmutableArray<> ) ) { var method = typeof( ImmutableArray ).GetMethods() .First( m => m.Name == "ToImmutableArray" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) ) .MakeGenericMethod( elemType ); return method.Invoke( null, new object[] { list } )!; } if( genDef == typeof( ImmutableList<> ) ) { var method = typeof( ImmutableList ).GetMethods() .First( m => m.Name == "ToImmutableList" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) ) .MakeGenericMethod( elemType ); return method.Invoke( null, new object[] { list } )!; } // Add more immutable/dictionary handlers here (using MakeImmutableDictionary etc.) } return list; // Default to List if no specific match } } // --- Object Handler (Default/Complex) --- public partial class ObjectHandler : ITypeHandler { public bool CanHandle( TypeInfo ti, XmlElement? elem ) => true; // Fallback private (XmlNode? source, bool isAttribute) FindValueSource( XmlElement parent, string name ) { if( parent.HasAttribute( name ) ) { return (parent.Attributes[name], true); } foreach( XmlNode node in parent.ChildNodes ) { if( node.NodeType == XmlNodeType.Element && node.Name == name ) { return (node, false); } } return (null, false); } private bool ShouldSetValue( MemberMeta member, bool isHydrating ) { // [Dont] members are filtered out during metadata generation. // If a member is present in the metadata and a value is found in the XML, // we should always set it. This handles both new creation and hydration/merge. return true; } private (object? obj, long id) GetOrCreateInstance( XmlSer xml, XmlElement elem, Type type, object? existing ) { long id = -1; bool first = true; // Check existing if( existing != null && type.IsAssignableFrom( existing.GetType() ) ) { id = xml._idGen.GetId( existing, out first ); return (existing, id); } // Create new object? newObj = null; try { if( type.GetConstructor( Type.EmptyTypes ) != null ) { newObj = Activator.CreateInstance( type ); } else { newObj = FormatterServices.GetUninitializedObject( type ); } } catch( Exception ex ) { log.error( $"Failed to create instance of {type.Name}: {ex.Message}" ); return (null, -1); } id = xml._idGen.GetId( newObj, out first ); return (newObj, id); } }