x) ser/deser of collections

This commit is contained in:
Marc Hernandez 2024-04-28 10:59:47 -07:00
parent 79b42b723e
commit 14615e95e0

View File

@ -13,6 +13,7 @@ using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static System.Net.WebRequestMethods; using static System.Net.WebRequestMethods;
using System.Linq; using System.Linq;
using System.Collections.Immutable;
/* /*
@ -102,6 +103,9 @@ namespace lib
} }
public record struct TypeProxy( Func<object, string> ser, Func<string, string, object> des ); public record struct TypeProxy( Func<object, string> ser, Func<string, string, object> des );
//public record struct CollectionCreator( Func<IEnumerable, object> FnCreate );
public class XmlFormatter2Cfg: Config public class XmlFormatter2Cfg: Config
{ {
@ -114,6 +118,9 @@ namespace lib
public Dictionary<Type, TypeProxy> TypeProxy = new(); public Dictionary<Type, TypeProxy> TypeProxy = new();
//public Dictionary<Type, CollectionCreator> CollectionCreator = new();
public Types TypesDefault = Types.Fields; public Types TypesDefault = Types.Fields;
} }
@ -242,34 +249,55 @@ namespace lib
return Deserialize( elem, null, type, null ); return Deserialize( elem, null, type, null );
} }
private bool IsEnumerable( Type type )
{
return type.IsAssignableTo( typeof(IEnumerable) );
}
private object Deserialize( XmlElement elem, MemberInfo mi, Type type, object existing /*, object enclosing = null*/ ) private object Deserialize( XmlElement elem, MemberInfo mi, Type type, object existing /*, object enclosing = null*/ )
{ {
TypeCode typeCode = Type.GetTypeCode(type); try
{
TypeCode typeCode = Type.GetTypeCode(type);
if( typeCode != TypeCode.Object ) if( typeCode != TypeCode.Object )
{
return DeserializeConcrete( elem, mi, type );
}
else
{
if( !type.IsArray )
{ {
object obj = DeserializeObject(elem, mi, type, existing); return DeserializeConcrete( elem, mi, type );
if( obj is I_Serialize )
{
var iser = obj as I_Serialize;
iser.OnDeserialize( null );
}
return obj;
} }
else else
{ {
return DeserializeArray( elem, mi, type ); if( !type.IsArray )
{
if( IsEnumerable(type) )
{
return DeserializeCollection( elem, mi, type );
}
else
{
object obj = DeserializeObject(elem, mi, type, existing);
if( obj is I_Serialize )
{
var iser = obj as I_Serialize;
iser.OnDeserialize( null );
}
return obj;
}
}
else
{
return DeserializeArray( elem, mi, type );
}
} }
} }
catch( Exception ex )
{
log.warn( $"Caught exception fn {mi.Name} type {type.Name} of {ex.Message}" );
}
return existing;
} }
Type[] mm_types = new Type[1]; Type[] mm_types = new Type[1];
@ -472,11 +500,13 @@ namespace lib
var dontAtt = childFi.GetCustomAttributes<lib.Dont>(); var dontAtt = childFi.GetCustomAttributes<lib.Dont>();
string propName = "";
if( name.StartsWith( "<" ) && name.EndsWith( "BackingField" ) ) if( name.StartsWith( "<" ) && name.EndsWith( "BackingField" ) )
{ {
var gtIndex = name.IndexOf( '>' ); var gtIndex = name.IndexOf( '>' );
var propName = name.Substring( 1, gtIndex - 1 ); propName = name.Substring( 1, gtIndex - 1 );
var propInfo = narrowType.GetProperty( propName ); var propInfo = narrowType.GetProperty( propName );
@ -496,6 +526,7 @@ namespace lib
if (FilterField(filterFields, doImpls, whitelistFields, childFi as MemberInfo, name)) continue; if (FilterField(filterFields, doImpls, whitelistFields, childFi as MemberInfo, name)) continue;
XmlElement childElem = getNamedChild(allChildren, name); XmlElement childElem = getNamedChild(allChildren, name);
if( childElem == null && !string.IsNullOrEmpty( propName ) ) childElem = getNamedChild( allChildren, propName );
if (childElem != null) if (childElem != null)
{ {
@ -609,6 +640,114 @@ namespace lib
return list; return list;
} }
private object DeserializeCollection( XmlElement elem, MemberInfo mi, Type type )
{
Type typeElem = typeof(object);
if( type.GenericTypeArguments.Length == 1 )
{
typeElem = type.GenericTypeArguments[0];
}
else if( type.GenericTypeArguments.Length == 2 )
{
typeElem = typeof(KeyValuePair<,>).MakeGenericType(type.GenericTypeArguments );
}
string refString = elem.GetAttribute("ref");
int refInt = refString.Length > 0 ? Convert.ToInt32(refString) : -1;
XmlNodeList arrNodeList = elem.ChildNodes;
int length = arrNodeList.Count;
Array arr = createArray(typeElem, refInt, length);
for( int i = 0; i < arr.Length; ++i )
{
if( arrNodeList.Item( i ) is XmlElement )
{
XmlElement arrElem = (XmlElement)arrNodeList.Item(i);
var finalType = typeElem;
if (arrElem.HasAttribute("_.t"))
{
var typename = arrElem.GetAttribute("_.t");
finalType = FindType(typename);
if (finalType == null)
finalType = typeElem;
}
arr.SetValue( Deserialize( arrElem, mi, finalType, null), i );
}
}
var listType = (typeof(List<>)).MakeGenericType( typeElem );
IList list = Activator.CreateInstance( listType ) as IList;
foreach( var a in arr )
{
list.Add( a );
}
MethodInfo ?toMeth = null;
var typeGen = Type.MakeGenericSignatureType( type );
if( type == typeof(ImmutableArray<>).MakeGenericType( typeElem ) )
{
var genMeth = GetType().GetMethod("MakeImmutableArray", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
toMeth = genMeth.MakeGenericMethod( typeElem );
}
else if( type == typeof(ImmutableDictionary<,>).MakeGenericType( typeElem.GenericTypeArguments ) )
{
var genMeth = GetType().GetMethod("MakeImmutableDictionary", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
toMeth = genMeth.MakeGenericMethod( typeElem.GenericTypeArguments );
}
else if( type == typeof(List<>).MakeGenericType( typeElem ) )
{
var genMeth = GetType().GetMethod("MakeList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
toMeth = genMeth.MakeGenericMethod( typeElem );
}
else if( type == typeof(Dictionary<,>).MakeGenericType( typeElem.GenericTypeArguments ) )
{
var genMeth = GetType().GetMethod("MakeDictionary", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
toMeth = genMeth.MakeGenericMethod( typeElem.GenericTypeArguments );
}
var obj = toMeth?.Invoke( this, new object[1] { list } );
return obj;
}
private List<T> MakeList<T>( IList list )
{
return list as List<T>;
}
private object MakeImmutableArray<T>( List<T> list )
{
var arr = list.ToImmutableArray();
return arr;
}
private object MakeImmutableDictionary<K,V>( List<KeyValuePair<K,V>> list )
{
var dict = list.ToImmutableDictionary();
return dict;
}
private object MakeDictionary<K,V>( List<KeyValuePair<K,V>> list )
{
var dict = list.ToDictionary();
return dict;
}
private object DeserializeArray( XmlElement elem, MemberInfo mi, Type type ) private object DeserializeArray( XmlElement elem, MemberInfo mi, Type type )
{ {
Type typeElem = type.GetElementType(); Type typeElem = type.GetElementType();
@ -848,7 +987,14 @@ namespace lib
{ {
if( !type.IsArray ) if( !type.IsArray )
{ {
SerializeObject( writer, mi, root, depth ); if( IsEnumerable( type ))
{
SerializeCollection( writer, mi, root, depth );
}
else
{
SerializeObject( writer, mi, root, depth );
}
} }
else else
{ {
@ -874,6 +1020,44 @@ namespace lib
writer.WriteAttributeString( "v", root.ToString() ); writer.WriteAttributeString( "v", root.ToString() );
} }
private void SerializeCollection( XmlWriter writer, MemberInfo mi, object root, int depth )
{
IEnumerable it = root as IEnumerable;
//Array arr = (Array)root;
Type typeElem = it.GetType().GenericTypeArguments[0];
Type type = root.GetType();
writer.WriteAttributeString( "_.t", getTypeName( type ) );
bool first;
long refInt = m_objectID.GetId(root, out first);
if( m_cfg.datastructure == Datastructure.Graph )
{
writer.WriteAttributeString( "ref", refInt.ToString() );
}
if( first )
{
if( m_cfg.datastructure == Datastructure.Graph )
{
m_alreadySerialized[refInt] = root;
}
int i = 0;
foreach( var v in it )
{
Serialize( writer, mi, v, "i" + i.ToString(), depth + 1, false );
++i;
}
}
}
private void SerializeObject( XmlWriter writer, MemberInfo mi, object root, int depth ) private void SerializeObject( XmlWriter writer, MemberInfo mi, object root, int depth )
{ {
writer.WriteAttributeString( "_.t", getTypeName( root.GetType() ) ); writer.WriteAttributeString( "_.t", getTypeName( root.GetType() ) );