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:
parent
d6a9cb42cf
commit
a27140700c
@ -12,6 +12,25 @@ using System.Diagnostics;
|
||||
|
||||
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,29 +334,28 @@ 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 )
|
||||
{
|
||||
string refString = elem.GetAttribute("ref");
|
||||
Type finalType;
|
||||
object obj;
|
||||
GetObjectForDeser( elem, type, out finalType, out obj );
|
||||
|
||||
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;
|
||||
return HydrateObject( elem, mi, finalType, obj );
|
||||
}
|
||||
|
||||
object obj = createObject(finalType, refInt);
|
||||
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 )
|
||||
{
|
||||
if( obj is IList )
|
||||
{
|
||||
var list = obj as IList;
|
||||
|
||||
return DeserializeList( elem, type, list );
|
||||
return DeserializeList( elem, mi, finalType, list );
|
||||
}
|
||||
|
||||
Type typeISerializable = typeof( ISerializable );
|
||||
@ -329,7 +384,7 @@ namespace lib
|
||||
|
||||
XmlElement childElem = getNamedChild( allChildren, name );
|
||||
|
||||
var des = Deserialize(childElem, childType);
|
||||
var des = Deserialize( childElem, mi, childType );
|
||||
|
||||
serInfo.AddValue( name, des, des.GetType() );
|
||||
}
|
||||
@ -376,41 +431,111 @@ namespace lib
|
||||
{
|
||||
XmlNodeList allChildren = elem.ChildNodes;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if( doFields )
|
||||
{
|
||||
var fields = refl.GetAllFields( finalType );
|
||||
|
||||
foreach( var childFi in fields )
|
||||
{
|
||||
|
||||
String name = childFi.Name;
|
||||
|
||||
if( filterFields && !whitelistFields.Contains( name ) ) continue;
|
||||
|
||||
name = refl.TypeToIdentifier( name );
|
||||
|
||||
XmlElement childElem = getNamedChild( allChildren, name );
|
||||
|
||||
|
||||
if( childElem != null )
|
||||
{
|
||||
object childObj = Deserialize(childElem, childFi.FieldType, obj);
|
||||
object childObj = Deserialize( childElem, childFi, childFi.FieldType, obj );
|
||||
|
||||
childFi.SetValue( obj, childObj );
|
||||
}
|
||||
else if( fields.Count == 1 )
|
||||
{
|
||||
object childObj = Deserialize(elem, childFi.FieldType, obj);
|
||||
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;
|
||||
}
|
||||
|
||||
private object DeserializeList( XmlElement elem, Type type, IList list )
|
||||
private void GetObjectForDeser( XmlElement elem, Type type, out Type finalType, out object obj )
|
||||
{
|
||||
string refString = elem.GetAttribute( "ref" );
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -569,7 +694,14 @@ 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 );
|
||||
|
||||
@ -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,6 +806,11 @@ namespace lib
|
||||
|
||||
Type type = root.GetType();
|
||||
|
||||
//var whitelistProp = type.GetCustomAttribute<WhitelistPropsAttribute>();
|
||||
|
||||
//var useWhitelist = whitelistProp != null;
|
||||
|
||||
|
||||
//*
|
||||
Type typeISerializable = typeof(ISerializable);
|
||||
|
||||
@ -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,12 +840,39 @@ namespace lib
|
||||
else
|
||||
//*/
|
||||
{
|
||||
var fields = refl.GetAllFields(type);
|
||||
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( type.GetCustomAttribute<PropertiesAttribute>() != null )
|
||||
{
|
||||
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 );
|
||||
|
||||
@ -717,17 +881,42 @@ namespace lib
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = childFi.Name;
|
||||
//if( childFi.GetCustomAttribute<WhitelistAttribute>() )
|
||||
|
||||
name = refl.TypeToIdentifier( name );
|
||||
|
||||
Serialize( writer, childFi.GetValue( root ), name, false );
|
||||
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,7 +945,7 @@ 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user