sharplib/ser/XmlSer_Core.cs

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 | 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<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.error( $"Failed to create instance of {type.Name}: {ex.Message}" );
return (null, -1);
}
id = xml._idGen.GetId( newObj, out first );
return (newObj, id);
}
}