x) Add TODO

x) Add various attributes to control specific things
x) Remove some unused logs
x) Pass MethodInfo through almost everything
x) Add DeserializeInto which keeps existing objects around
x) Add Properties to things.  Off by default
x) Add various filtering code
This commit is contained in:
Marc Hernandez 2023-12-05 22:48:35 -08:00
parent d6a9cb42cf
commit a27140700c

View File

@ -10,8 +10,27 @@ using System.Collections.Generic;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
/*
* TODO
* HUGE FLAW IN DESERIALIZATION. If you deser into something it resets everything to default values
* This didnt matter before when I did full things, but it matters now
*/
namespace lib
{
@ -25,6 +44,41 @@ namespace lib
void OnDeserialize( object enclosing );
}
public class InstanceAttribute : Attribute
{
}
public class PropertiesAttribute : Attribute
{
}
public class WhitelistAttribute : Attribute
{
}
public class WhitelistFieldsAttribute : Attribute
{
public string[] Values { get; private set; }
public WhitelistFieldsAttribute( params string[] values )
{
this.Values = values;
}
}
public class WhitelistPropsAttribute : Attribute
{
public string[] Values { get; private set; }
public WhitelistPropsAttribute( params string[] values )
{
this.Values = values;
}
}
public enum Datastructure
{
Invalid,
@ -88,37 +142,29 @@ namespace lib
#region Deserialize
private static FormatterConverter s_conv = new FormatterConverter();
public object Deserialize( Stream stream )
{
//lib.log.info( "Deserialize( Stream stream ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
return DeserializeKnownType( stream, null );
//lib.log.info( "Deserialize END ( Stream stream ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
}
public T Deserialize<T>(Stream stream)
{
//lib.log.info( "Deserialize( Stream stream ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
return (T)DeserializeKnownType(stream, typeof(T));
//lib.log.info( "Deserialize END ( Stream stream ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
}
public object DeserializeKnownType( Stream stream, Type t )
{
//lib.log.info( "DeserializeKnownType( Stream stream, Type t ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
XmlTextReader reader = new XmlTextReader(stream);
//reader.Settings = System.Text.Encoding.ASCII;
object obj = Deserialize(reader, t);
//lib.log.info( "DeserializeKnownType END( Stream stream, Type t ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
return obj;
}
private object Deserialize( XmlReader reader, Type t )
{
//lib.log.info( "Deserialize( XmlReader reader, Type t ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
m_alreadySerialized.Clear();
m_objectID = new ObjectIDGenerator();
@ -128,12 +174,22 @@ namespace lib
doc.Load( reader );
////lib.log.info( "What to deserialize {0}", doc.OuterXml.ToString() );
if( t == null )
return Deserialize( doc.DocumentElement );
return Deserialize( doc.DocumentElement, t );
return Deserialize( doc.DocumentElement, null, t );
}
public void DeserializeInto<T>(Stream stream, T obj)
{
XmlTextReader reader = new XmlTextReader( stream );
reader.Read();
XmlDocument doc = new XmlDocument();
doc.Load( reader );
HydrateObject<T>( doc.DocumentElement, null, obj );
}
private object Deserialize( XmlElement elem )
@ -142,10 +198,10 @@ namespace lib
string typename = elem.HasAttribute("t") ? elem.GetAttribute("t") : elem.Name;
return Deserialize( elem, typename );
return Deserialize( elem, null, typename );
}
private object Deserialize( XmlElement elem, string typename )
private object Deserialize( XmlElement elem, MemberInfo mi, string typename )
{
AppDomain currentDomain = AppDomain.CurrentDomain;
Assembly[] assems = currentDomain.GetAssemblies();
@ -167,22 +223,22 @@ namespace lib
return null;
}
return Deserialize( elem, type );
return Deserialize( elem, null, type );
}
private object Deserialize( XmlElement elem, Type type, object enclosing = null )
private object Deserialize( XmlElement elem, MemberInfo mi, Type type, object enclosing = null )
{
TypeCode typeCode = Type.GetTypeCode(type);
if( typeCode != TypeCode.Object )
{
return DeserializeConcrete( elem, type );
return DeserializeConcrete( elem, mi, type );
}
else
{
if( !type.IsArray )
{
object obj = DeserializeObject(elem, type);
object obj = DeserializeObject(elem, mi, type);
if( obj is I_Serialize )
{
@ -195,7 +251,7 @@ namespace lib
}
else
{
return DeserializeArray( elem, type );
return DeserializeArray( elem, mi, type );
}
}
}
@ -215,7 +271,7 @@ namespace lib
return default( T );
}
private object DeserializeConcrete( XmlElement elem, Type type )
private object DeserializeConcrete( XmlElement elem, MemberInfo mi, Type type )
{
string val = "";
@ -278,73 +334,72 @@ namespace lib
private Type[] mm_consType = new Type[2];
private object[] mm_args = new object[2];
private object DeserializeObject( XmlElement elem, Type type )
private object DeserializeObject( XmlElement elem, MemberInfo mi, Type type )
{
Type finalType;
object obj;
GetObjectForDeser( elem, type, out finalType, out obj );
return HydrateObject( elem, mi, finalType, obj );
}
public object HydrateObject<T>( XmlElement elem, MemberInfo mi, T obj )
{
return HydrateObject( elem, mi, typeof( T ), obj );
}
private object HydrateObject( XmlElement elem, MemberInfo mi, Type finalType, object obj )
{
string refString = elem.GetAttribute("ref");
int refInt = refString.Length > 0 ? Convert.ToInt32(refString) : -1;
var finalType = type;
if( elem.HasAttribute( "t" ) )
{
var typename = elem.GetAttribute("t");
finalType = FindType( typename );
if( finalType == null )
finalType = type;
}
object obj = createObject(finalType, refInt);
if( obj is IList )
{
var list = obj as IList;
return DeserializeList( elem, type, list );
return DeserializeList( elem, mi, finalType, list );
}
Type typeISerializable = typeof(ISerializable);
Type typeISerializable = typeof( ISerializable );
if( obj is ISerializable ) // type.IsSubclassOf( typeISerializable ) )
if( obj is ISerializable ) // type.IsSubclassOf( typeISerializable ) )
{
XmlNodeList allChildren = elem.ChildNodes;
//ISerializable ser = obj as ISerializable;
var serInfo = new SerializationInfo(finalType, new FormatterConverter());
//var serInfoForTypes = new SerializationInfo( type, new FormatterConverter() );
//ser.GetObjectData( serInfoForTypes, Context );
XmlNodeList allChildren = elem.ChildNodes;
//ISerializable ser = obj as ISerializable;
var serInfo = new SerializationInfo( finalType, new FormatterConverter() );
//var serInfoForTypes = new SerializationInfo( type, new FormatterConverter() );
//ser.GetObjectData( serInfoForTypes, Context );
foreach( var objNode in allChildren )
{
var node = objNode as XmlElement;
String name = node.Name;
String childType = node.GetAttribute("t");
String childType = node.GetAttribute( "t" );
name = refl.TypeToIdentifier( name );
XmlElement childElem = getNamedChild(allChildren, name);
XmlElement childElem = getNamedChild( allChildren, name );
var des = Deserialize(childElem, childType);
var des = Deserialize( childElem, mi, childType );
serInfo.AddValue( name, des, des.GetType() );
}
//ConstructorInfo[] allCons = obj.GetType().GetConstructors( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
//var serMem = FormatterServices.GetSerializableMembers( finalType );
//object objUn = FormatterServices.GetSafeUninitializedObject( finalType );
}
//ConstructorInfo[] allCons = obj.GetType().GetConstructors( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
//var serMem = FormatterServices.GetSerializableMembers( finalType );
//object objUn = FormatterServices.GetSafeUninitializedObject( finalType );
IDeserializationCallback objUnOnDeser = obj as IDeserializationCallback;
mm_consType[0] = typeof( SerializationInfo );
mm_consType[1] = typeof( StreamingContext );
ConstructorInfo serCons = finalType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, mm_consType, null);
ConstructorInfo serCons = finalType.GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, mm_consType, null );
mm_args[0] = serInfo;
mm_args[1] = Context;
@ -353,8 +408,8 @@ namespace lib
if( objUnOnDeser != null )
{
objUnOnDeser.OnDeserialization( objUnOnDeser );
}
}
/*
ser.GetObjectData( serInfo, Context );
@ -370,47 +425,117 @@ namespace lib
var des = Deserialize( childElem, name );
}
*/
*/
}
else
{
XmlNodeList allChildren = elem.ChildNodes;
XmlNodeList allChildren = elem.ChildNodes;
var doFields = true;
var doProperties = false;
var filterFields = mi?.GetCustomAttribute<WhitelistFieldsAttribute>() != null;
var filterProps = mi?.GetCustomAttribute<WhitelistPropsAttribute>() != null;
HashSet<string> whitelistFields = new( mi?.GetCustomAttribute<WhitelistFieldsAttribute>()?.Values ?? new string[0] );
HashSet<string> whitelistProps = new( mi?.GetCustomAttribute<WhitelistPropsAttribute>()?.Values ?? new string[0] );
if( finalType.GetCustomAttribute<PropertiesAttribute>() != null )
{
doProperties = true;
}
if( mi?.GetCustomAttribute<WhitelistPropsAttribute>() != null )
{
doProperties = true;
doFields = mi?.GetCustomAttribute<WhitelistFieldsAttribute>() != null;
}
var fields = refl.GetAllFields(type);
//MemberInfo[] miArr = FormatterServices.GetSerializableMembers( type, Context );
foreach( var childFi in fields )
if( doFields )
{
String name = childFi.Name;
name = refl.TypeToIdentifier( name );
XmlElement childElem = getNamedChild(allChildren, name);
if( childElem != null )
var fields = refl.GetAllFields( finalType );
foreach( var childFi in fields )
{
object childObj = Deserialize(childElem, childFi.FieldType, obj);
String name = childFi.Name;
if( filterFields && !whitelistFields.Contains( name ) ) continue;
childFi.SetValue( obj, childObj );
}
else if( fields.Count == 1 )
{
object childObj = Deserialize(elem, childFi.FieldType, obj);
name = refl.TypeToIdentifier( name );
childFi.SetValue( obj, childObj );
}
XmlElement childElem = getNamedChild( allChildren, name );
if( childElem != null )
{
object childObj = Deserialize( childElem, childFi, childFi.FieldType, obj );
childFi.SetValue( obj, childObj );
}
else if( fields.Count == 1 )
{
object childObj = Deserialize( elem, childFi, childFi.FieldType, obj );
childFi.SetValue( obj, childObj );
}
}
}
if( doProperties )
{
var props = refl.GetAllProperties( finalType );
foreach( var childPi in props )
{
String name = childPi.Name;
if( filterProps && !whitelistProps.Contains( name ) ) continue;
name = refl.TypeToIdentifier( name );
XmlElement childElem = getNamedChild( allChildren, name );
if( childElem != null )
{
object childObj = Deserialize( childElem, childPi, childPi.PropertyType, obj );
childPi.SetValue( obj, childObj );
}
else if( props.Count == 1 )
{
object childObj = Deserialize( elem, childPi, childPi.PropertyType, obj );
childPi.SetValue( obj, childObj );
}
}
}
}
return obj;
}
return obj;
}
private void GetObjectForDeser( XmlElement elem, Type type, out Type finalType, out object obj )
{
string refString = elem.GetAttribute( "ref" );
private object DeserializeList( XmlElement elem, Type type, IList list )
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
finalType = type;
if( elem.HasAttribute( "t" ) )
{
var typename = elem.GetAttribute( "t" );
finalType = FindType( typename );
if( finalType == null )
finalType = type;
}
obj = createObject( finalType, refInt );
}
private object DeserializeList( XmlElement elem, MemberInfo mi, Type type, IList list )
{
XmlNodeList arrNodeList = elem.ChildNodes;
@ -426,14 +551,14 @@ namespace lib
{
XmlElement arrElem = (XmlElement)arrNodeList.Item(i);
list.Add( Deserialize( arrElem, genT[0] ) );
list.Add( Deserialize( arrElem, mi, genT[0] ) );
}
}
return list;
}
private object DeserializeArray( XmlElement elem, Type type )
private object DeserializeArray( XmlElement elem, MemberInfo mi, Type type )
{
Type typeElem = type.GetElementType();
@ -462,7 +587,7 @@ namespace lib
finalType = typeElem;
}
arr.SetValue( Deserialize( arrElem, finalType), i );
arr.SetValue( Deserialize( arrElem, mi, finalType), i );
}
}
@ -567,9 +692,16 @@ namespace lib
//string assName = ass.GetName().Name;
return type.FullName; // + ", " + assName;
}
}
public void Serialize( Stream stream, object root )
{
Serialize( stream, null, root );
}
public void Serialize( Stream stream, 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 );
@ -580,7 +712,7 @@ namespace lib
writer.Formatting = Formatting.Indented;
Serialize( writer, root );
Serialize( writer, mi, root );
//Rely on the parent closing the stream.
//writer.Close();
@ -589,14 +721,14 @@ namespace lib
//lib.log.info( "Serialize END ( Stream stream, object root ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
}
private void Serialize( XmlWriter writer, object root )
private void Serialize( XmlWriter writer, MemberInfo mi, object root )
{
//writer.WriteStartDocument();
Serialize( writer, root, "root", true );
Serialize( writer, mi, root, "root", true );
//writer.WriteEndDocument();
}
private void Serialize( XmlWriter writer, object root, string name, bool forceType )
private void Serialize( XmlWriter writer, MemberInfo mi, object root, string name, bool forceType )
{
writer.WriteStartElement( name );
@ -608,17 +740,17 @@ namespace lib
if( typeCode != TypeCode.Object )
{
SerializeConcrete( writer, root, forceType );
SerializeConcrete( writer, mi, root, forceType );
}
else
{
if( !type.IsArray )
{
SerializeObject( writer, root );
SerializeObject( writer, mi, root );
}
else
{
SerializeArray( writer, root );
SerializeArray( writer, mi, root );
}
}
}
@ -630,7 +762,7 @@ namespace lib
writer.WriteEndElement();
}
private void SerializeConcrete( XmlWriter writer, object root, bool forceType )
private void SerializeConcrete( XmlWriter writer, MemberInfo mi, object root, bool forceType )
{
//TODO: Only write this out if debugging.
if( forceType )
@ -640,7 +772,7 @@ namespace lib
writer.WriteAttributeString( "v", root.ToString() );
}
private void SerializeObject( XmlWriter writer, object root )
private void SerializeObject( XmlWriter writer, MemberInfo mi, object root )
{
writer.WriteAttributeString( "t", getTypeName( root.GetType() ) );
@ -674,7 +806,12 @@ 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 ) )
@ -693,7 +830,7 @@ namespace lib
name = refl.TypeToIdentifier( name );
Serialize( writer, serMember.Value, name, true );
Serialize( writer, mi, serMember.Value, name, true );
}
//var sc = new SerializationContext(
@ -703,31 +840,83 @@ namespace lib
else
//*/
{
var fields = refl.GetAllFields(type);
var doFields = true;
var doProperties = false;
//MemberInfo[] miArr = FormatterServices.GetSerializableMembers( type, Context );
var filterFields = mi?.GetCustomAttribute<WhitelistFieldsAttribute>() != null;
var filterProps = mi?.GetCustomAttribute<WhitelistPropsAttribute>() != null;
foreach( var childFi in fields )
HashSet<string> whitelistFields = new( mi?.GetCustomAttribute<WhitelistFieldsAttribute>()?.Values ?? new string[0] );
HashSet<string> whitelistProps = new( mi?.GetCustomAttribute<WhitelistPropsAttribute>()?.Values ?? new string[0] );
if( type.GetCustomAttribute<PropertiesAttribute>() != null )
{
object[] objs = childFi.GetCustomAttributes(typeof(NonSerializedAttribute), true);
if( objs.Length > 0 )
{
continue;
}
String name = childFi.Name;
name = refl.TypeToIdentifier( name );
Serialize( writer, childFi.GetValue( root ), name, false );
doProperties = true;
}
if( mi?.GetCustomAttribute<WhitelistPropsAttribute>() != null )
{
doProperties = true;
doFields = mi?.GetCustomAttribute<WhitelistFieldsAttribute>() != null;
}
//MemberInfo[] miArr = FormatterServices.GetSerializableMembers( type, Context );
if( doFields )
{
var fields = refl.GetAllFields( type );
foreach( var childFi in fields )
{
String name = childFi.Name;
if( filterFields && !whitelistFields.Contains( name ) ) continue;
object[] objs = childFi.GetCustomAttributes( typeof( NonSerializedAttribute ), true );
if( objs.Length > 0 )
{
continue;
}
//if( childFi.GetCustomAttribute<WhitelistAttribute>() )
name = refl.TypeToIdentifier( name );
Serialize( writer, childFi, childFi.GetValue( root ), name, false );
}
}
if( doProperties )
{
var props = refl.GetAllProperties( type );
foreach( var childPi in props )
{
String name = childPi.Name;
if( filterProps && !whitelistProps.Contains( name ) ) continue;
object[] objs = childPi.GetCustomAttributes( typeof( NonSerializedAttribute ), true );
if( objs.Length > 0 )
{
continue;
}
name = refl.TypeToIdentifier( name );
Serialize( writer, childPi, childPi.GetValue( root ), name, false );
}
}
}
}
}
private void SerializeArray( XmlWriter writer, object root )
private void SerializeArray( XmlWriter writer, MemberInfo mi, object root )
{
Array arr = (Array)root;
@ -756,10 +945,10 @@ namespace lib
for( int i = 0; i < arr.Length; ++i )
{
Serialize( writer, arr.GetValue( i ), "i" + i.ToString(), false );
Serialize( writer, mi, arr.GetValue( i ), "i" + i.ToString(), false );
}
}
}
}
#endregion
}