From 5e025284f06e8f87ae959964c073d440901097fc Mon Sep 17 00:00:00 2001 From: Marc Hernandez Date: Sun, 3 Mar 2024 14:59:17 -0800 Subject: [PATCH] x) Add Type Proxy for saving and loading objects we dont have control --- ser/XmlFormatter2.cs | 140 ++++++++++++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 41 deletions(-) diff --git a/ser/XmlFormatter2.cs b/ser/XmlFormatter2.cs index 79cbdcf..702c590 100644 --- a/ser/XmlFormatter2.cs +++ b/ser/XmlFormatter2.cs @@ -90,6 +90,8 @@ namespace lib } + public record struct TypeProxy( Func ser, Func des ); + public class XmlFormatter2Cfg: Config { public Datastructure datastructure = Datastructure.Graph; @@ -98,6 +100,8 @@ namespace lib public Dictionary> WLProps = new(); public Dictionary> WLFields= new(); + + public Dictionary 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(); - - //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;