x) Add Type Proxy for saving and loading objects we dont have control

This commit is contained in:
Marc Hernandez 2024-03-03 14:59:17 -08:00
parent 94ce81da96
commit 5e025284f0

View File

@ -90,6 +90,8 @@ namespace lib
}
public record struct TypeProxy( Func<object, string> ser, Func<string, string, object> des );
public class XmlFormatter2Cfg: Config
{
public Datastructure datastructure = Datastructure.Graph;
@ -98,6 +100,8 @@ namespace lib
public Dictionary<string, List<string>> WLProps = new();
public Dictionary<string, List<string>> WLFields= new();
public Dictionary<Type, TypeProxy> TypeProxy = new();
}
public class XmlFormatter2: IFormatter
@ -129,9 +133,6 @@ namespace lib
Context = new StreamingContext( StreamingContextStates.All );
}
public XmlFormatter2( XmlFormatter2Cfg cfg )
{
Context = new StreamingContext( StreamingContextStates.All );
@ -141,8 +142,6 @@ namespace lib
log.warn( $"XML serialization is NOT fast" );
}
#region Deserialize
private static FormatterConverter s_conv = new FormatterConverter();
@ -491,7 +490,7 @@ namespace lib
name = refl.TypeToIdentifier( name );
if( FilterField( filterFields, doImpls, whitelistFields, childPi as MemberInfo, name ) ) continue;
if( FilterField( filterProps, doImpls, whitelistProps, childPi as PropertyInfo, name ) ) continue;
XmlElement childElem = getNamedChild( allChildren, name );
@ -501,7 +500,20 @@ namespace lib
object childObj = Deserialize( childElem, childPi, childPi.PropertyType, existingObj );
childPi.SetValue( obj, childObj );
var setMethod = childPi.GetSetMethod();
if( setMethod != null )
{
//Object o = Activator.CreateInstance( setMethod.ReflectedType );
setMethod.Invoke( obj, new object[]{ childObj } );
//setMethod.CreateDelegate()
}
else
{
childPi.SetValue( obj, childObj );
}
}
}
}
@ -540,7 +552,7 @@ namespace lib
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
obj = createObject( finalType, refInt, obj );
obj = createObject( elem, finalType, refInt, obj );
return obj;
}
@ -604,14 +616,14 @@ namespace lib
return arr;
}
private object createObject( string typename, int refInt, object obj )
private object createObject( XmlElement elem, string typename, int refInt, object obj )
{
Type type = Type.GetType(typename);
return createObject( type, refInt, obj );
return createObject( elem, type, refInt, obj );
}
private object createObject( Type type, int refInt, object existingObj )
private object createObject( XmlElement elem, Type type, int refInt, object existingObj )
{
TypeCode tc = Type.GetTypeCode(type);
@ -621,13 +633,60 @@ namespace lib
return m_alreadySerialized[refInt];
}
// FIRST If there is a proxy deserializer, we skip using the existing object.
var isProxy = elem.HasAttribute( "proxy" );
if( isProxy )
{
object obj = null;
var tryType = type;
TypeProxy? proxy = null;
while( tryType != typeof(object) && obj == null )
{
//m_cfg.TypeProxy.TryGetValue( )
if( m_cfg.TypeProxy.TryGetValue( tryType, out var newProxy ) )
{
proxy = newProxy;
break;
}
tryType = tryType.BaseType;
}
var proxyVal = elem.GetAttribute( "proxy" );
if( proxy.HasValue )
{
obj = proxy.Value.des( type.Name, proxyVal );
return obj;
}
else
{
log.warn( $"Trying to proxy in {elem.Name}, but there is no proxy available. Falling back to regular hydrate/create" );
}
}
// SECOND if we have an existing object there of the same type, replace
if( existingObj != null )
{
var existingObjType = existingObj.GetType();
if( type == existingObjType ) return existingObj;
// @@@ GROSS Fix the types so theyre known good.
var isSubclass = type.IsSubclassOf( existingObjType ) || existingObjType.IsSubclassOf( type );
if( isSubclass ) return existingObj;
// old
//if( type == existingObjType ) return existingObj;
}
// THIRD create a new object
{
object obj = null;
@ -647,7 +706,6 @@ namespace lib
{
log.error( $"Got exception {exInner.Message} trying to make an uninitialized object" );
}
}
if( obj == null )
@ -711,13 +769,11 @@ namespace lib
return type.FullName; // + ", " + assName;
}
public void Serialize( Stream stream, object root )
{
Serialize( stream, null, root );
}
public void Serialize( Stream stream, MemberInfo mi, object root )
{
//lib.log.info( "Serialize( Stream stream, object root ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
@ -798,25 +854,14 @@ namespace lib
writer.WriteAttributeString( "_.version.", $"{m_cfg.Version}" );
}
/*
if( root is IList )
{
var list = root as IList;
Type t = root.GetType();
Type[] genT = t.GetGenericArguments();
}
*/
bool first;
long refInt = m_objectID.GetId(root, out first);
// @@@@ FIX for proxies.
if( m_cfg.datastructure == Datastructure.Graph )
{
writer.WriteAttributeString( "ref", refInt.ToString() );
}
if( first )
@ -828,24 +873,15 @@ namespace lib
Type type = root.GetType();
//var whitelistProp = type.GetCustomAttribute<WhitelistPropsAttribute>();
//var useWhitelist = whitelistProp != null;
//*
Type typeISerializable = typeof(ISerializable);
if( root is ISerializable ) // type.IsSubclassOf( typeISerializable ) )
if( root is ISerializable ser )
{
ISerializable ser = root as ISerializable;
var serInfo = new SerializationInfo(type, new FormatterConverter());
ser.GetObjectData( serInfo, Context );
//var serEnum = ;
foreach( var serMember in serInfo )
{
String name = serMember.Name;
@ -855,11 +891,33 @@ namespace lib
Serialize( writer, mi, serMember.Value, name, depth, true );
}
//var sc = new SerializationContext(
//ser.GetObjectData(
return;
}
else
{
var tryType = type;
TypeProxy? proxy = null;
while( tryType != typeof(object) )
{
if( m_cfg.TypeProxy.TryGetValue( tryType, out var newProxy ) )
{
proxy = newProxy;
break;
}
tryType = tryType.BaseType;
}
if( proxy.HasValue )
{
var proxyStr = proxy.Value.ser( root );
writer.WriteAttributeString( "proxy", proxyStr );
return;
}
}
//*/
{
bool filterFields, filterProps, doImpls, doFields, doProps;