160 lines
4.4 KiB
C#
160 lines
4.4 KiB
C#
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// --- 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<T> 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.exception( ex, $"Failed to create instance of {type.Name}: {ex.Message}" );
|
|
return (null, -1);
|
|
}
|
|
|
|
id = xml._idGen.GetId( newObj, out first );
|
|
return (newObj, id);
|
|
}
|
|
}
|
|
|