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 class XmlFormatter2Cfg: Config
{ {
public Datastructure datastructure = Datastructure.Graph; public Datastructure datastructure = Datastructure.Graph;
@ -98,6 +100,8 @@ namespace lib
public Dictionary<string, List<string>> WLProps = new(); public Dictionary<string, List<string>> WLProps = new();
public Dictionary<string, List<string>> WLFields= new(); public Dictionary<string, List<string>> WLFields= new();
public Dictionary<Type, TypeProxy> TypeProxy = new();
} }
public class XmlFormatter2: IFormatter public class XmlFormatter2: IFormatter
@ -129,9 +133,6 @@ namespace lib
Context = new StreamingContext( StreamingContextStates.All ); Context = new StreamingContext( StreamingContextStates.All );
} }
public XmlFormatter2( XmlFormatter2Cfg cfg ) public XmlFormatter2( XmlFormatter2Cfg cfg )
{ {
Context = new StreamingContext( StreamingContextStates.All ); Context = new StreamingContext( StreamingContextStates.All );
@ -141,8 +142,6 @@ namespace lib
log.warn( $"XML serialization is NOT fast" ); log.warn( $"XML serialization is NOT fast" );
} }
#region Deserialize #region Deserialize
private static FormatterConverter s_conv = new FormatterConverter(); private static FormatterConverter s_conv = new FormatterConverter();
@ -491,7 +490,7 @@ namespace lib
name = refl.TypeToIdentifier( name ); 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 ); XmlElement childElem = getNamedChild( allChildren, name );
@ -501,7 +500,20 @@ namespace lib
object childObj = Deserialize( childElem, childPi, childPi.PropertyType, existingObj ); 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; int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
obj = createObject( finalType, refInt, obj ); obj = createObject( elem, finalType, refInt, obj );
return obj; return obj;
} }
@ -604,14 +616,14 @@ namespace lib
return arr; 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); 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); TypeCode tc = Type.GetTypeCode(type);
@ -621,13 +633,60 @@ namespace lib
return m_alreadySerialized[refInt]; 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 ) if( existingObj != null )
{ {
var existingObjType = existingObj.GetType(); 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; object obj = null;
@ -647,7 +706,6 @@ namespace lib
{ {
log.error( $"Got exception {exInner.Message} trying to make an uninitialized object" ); log.error( $"Got exception {exInner.Message} trying to make an uninitialized object" );
} }
} }
if( obj == null ) if( obj == null )
@ -711,13 +769,11 @@ namespace lib
return type.FullName; // + ", " + assName; return type.FullName; // + ", " + assName;
} }
public void Serialize( Stream stream, object root ) public void Serialize( Stream stream, object root )
{ {
Serialize( stream, null, root ); Serialize( stream, null, root );
} }
public void Serialize( Stream stream, MemberInfo mi, object 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 ); //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}" ); 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; bool first;
long refInt = m_objectID.GetId(root, out first); long refInt = m_objectID.GetId(root, out first);
// @@@@ FIX for proxies.
if( m_cfg.datastructure == Datastructure.Graph ) if( m_cfg.datastructure == Datastructure.Graph )
{ {
writer.WriteAttributeString( "ref", refInt.ToString() ); writer.WriteAttributeString( "ref", refInt.ToString() );
} }
if( first ) if( first )
@ -828,24 +873,15 @@ namespace lib
Type type = root.GetType(); Type type = root.GetType();
//var whitelistProp = type.GetCustomAttribute<WhitelistPropsAttribute>();
//var useWhitelist = whitelistProp != null;
//* //*
Type typeISerializable = typeof(ISerializable); 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()); var serInfo = new SerializationInfo(type, new FormatterConverter());
ser.GetObjectData( serInfo, Context ); ser.GetObjectData( serInfo, Context );
//var serEnum = ;
foreach( var serMember in serInfo ) foreach( var serMember in serInfo )
{ {
String name = serMember.Name; String name = serMember.Name;
@ -855,11 +891,33 @@ namespace lib
Serialize( writer, mi, serMember.Value, name, depth, true ); Serialize( writer, mi, serMember.Value, name, depth, true );
} }
//var sc = new SerializationContext( return;
//ser.GetObjectData(
} }
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; bool filterFields, filterProps, doImpls, doFields, doProps;