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
956 lines
21 KiB
C#
956 lines
21 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Xml;
|
|
using System.Runtime.Serialization;
|
|
//using System.Web.Configuration;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Reflection;
|
|
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
|
|
{
|
|
|
|
public class AttDocumentSubclasses : Attribute
|
|
{
|
|
}
|
|
|
|
public interface I_Serialize
|
|
{
|
|
void OnSerialize();
|
|
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,
|
|
Tree,
|
|
Full,
|
|
|
|
}
|
|
|
|
public class XmlFormatter2Cfg: Config
|
|
{
|
|
public readonly Datastructure datastructure = Datastructure.Full;
|
|
}
|
|
|
|
public class XmlFormatter2: IFormatter
|
|
{
|
|
public StreamingContext Context { get; set; }
|
|
|
|
static Random s_rnd = new Random();
|
|
int m_rndVal = s_rnd.Next();
|
|
|
|
XmlFormatter2Cfg m_cfg = new XmlFormatter2Cfg();
|
|
|
|
|
|
|
|
#region Unimplimented
|
|
public ISurrogateSelector SurrogateSelector
|
|
{
|
|
get { throw new NotImplementedException(); }
|
|
set { throw new NotImplementedException(); }
|
|
}
|
|
|
|
public SerializationBinder Binder
|
|
{
|
|
get { throw new NotImplementedException(); }
|
|
set { throw new NotImplementedException(); }
|
|
}
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public XmlFormatter2()
|
|
{
|
|
Context = new StreamingContext( StreamingContextStates.All );
|
|
}
|
|
|
|
|
|
|
|
|
|
public XmlFormatter2( XmlFormatter2Cfg cfg )
|
|
{
|
|
Context = new StreamingContext( StreamingContextStates.All );
|
|
|
|
m_cfg = cfg;
|
|
}
|
|
|
|
|
|
|
|
#region Deserialize
|
|
private static FormatterConverter s_conv = new FormatterConverter();
|
|
|
|
|
|
public object Deserialize( Stream stream )
|
|
{
|
|
return DeserializeKnownType( stream, null );
|
|
}
|
|
|
|
|
|
public T Deserialize<T>(Stream stream)
|
|
{
|
|
return (T)DeserializeKnownType(stream, typeof(T));
|
|
}
|
|
|
|
public object DeserializeKnownType( Stream stream, Type t )
|
|
{
|
|
XmlTextReader reader = new XmlTextReader(stream);
|
|
|
|
object obj = Deserialize(reader, t);
|
|
|
|
return obj;
|
|
}
|
|
|
|
private object Deserialize( XmlReader reader, Type t )
|
|
{
|
|
m_alreadySerialized.Clear();
|
|
m_objectID = new ObjectIDGenerator();
|
|
|
|
reader.Read();
|
|
|
|
XmlDocument doc = new XmlDocument();
|
|
|
|
doc.Load( reader );
|
|
|
|
if( t == null )
|
|
return Deserialize( doc.DocumentElement );
|
|
|
|
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 )
|
|
{
|
|
//lib.log.info( "object Deserialize( XmlElement elem ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
|
|
|
|
string typename = elem.HasAttribute("t") ? elem.GetAttribute("t") : elem.Name;
|
|
|
|
return Deserialize( elem, null, typename );
|
|
}
|
|
|
|
private object Deserialize( XmlElement elem, MemberInfo mi, string typename )
|
|
{
|
|
AppDomain currentDomain = AppDomain.CurrentDomain;
|
|
Assembly[] assems = currentDomain.GetAssemblies();
|
|
|
|
Type type = null;
|
|
|
|
// @@@@: This should go backwards, we tend to lookup our own stuff, then builtins.
|
|
// Also, cache a typename into its assembly.
|
|
foreach( Assembly a in assems )
|
|
{
|
|
type = a.GetType( typename );
|
|
|
|
if( type != null )
|
|
break;
|
|
}
|
|
|
|
if( type == null )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return Deserialize( elem, null, type );
|
|
}
|
|
|
|
private object Deserialize( XmlElement elem, MemberInfo mi, Type type, object enclosing = null )
|
|
{
|
|
TypeCode typeCode = Type.GetTypeCode(type);
|
|
|
|
if( typeCode != TypeCode.Object )
|
|
{
|
|
return DeserializeConcrete( elem, mi, type );
|
|
}
|
|
else
|
|
{
|
|
if( !type.IsArray )
|
|
{
|
|
object obj = DeserializeObject(elem, mi, type);
|
|
|
|
if( obj is I_Serialize )
|
|
{
|
|
var iser = obj as I_Serialize;
|
|
|
|
iser.OnDeserialize( enclosing );
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
else
|
|
{
|
|
return DeserializeArray( elem, mi, type );
|
|
}
|
|
}
|
|
}
|
|
|
|
Type[] mm_types = new Type[1];
|
|
private object GetDefault( Type t )
|
|
{
|
|
mm_types[0] = t;
|
|
|
|
var fn = GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(mm_types);
|
|
|
|
return fn.Invoke( this, null );
|
|
}
|
|
|
|
public T GetDefaultGeneric<T>()
|
|
{
|
|
return default( T );
|
|
}
|
|
|
|
private object DeserializeConcrete( XmlElement elem, MemberInfo mi, Type type )
|
|
{
|
|
string val = "";
|
|
|
|
if( elem.HasAttribute("v") )
|
|
{
|
|
val = elem.GetAttribute("v");
|
|
}
|
|
else
|
|
{
|
|
val = elem.InnerText;
|
|
}
|
|
|
|
if ( !type.IsEnum )
|
|
{
|
|
try
|
|
{
|
|
return s_conv.Convert( val, type );
|
|
}
|
|
catch( Exception )
|
|
{
|
|
return GetDefault( type );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Enum.Parse( type, val );
|
|
}
|
|
|
|
}
|
|
|
|
private XmlElement getNamedChild( XmlNodeList list, string name )
|
|
{
|
|
foreach( XmlNode node in list )
|
|
{
|
|
if( node.Name == name )
|
|
{
|
|
return (XmlElement)node;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Type FindType( string shortname )
|
|
{
|
|
Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies();
|
|
|
|
foreach( Assembly a in ass )
|
|
{
|
|
Type t = a.GetType(shortname);
|
|
|
|
if( t != null )
|
|
{
|
|
return t;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Type[] mm_consType = new Type[2];
|
|
private object[] mm_args = new object[2];
|
|
|
|
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 )
|
|
{
|
|
if( obj is IList )
|
|
{
|
|
var list = obj as IList;
|
|
|
|
return DeserializeList( elem, mi, finalType, list );
|
|
}
|
|
|
|
Type typeISerializable = typeof( ISerializable );
|
|
|
|
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 );
|
|
|
|
foreach( var objNode in allChildren )
|
|
{
|
|
var node = objNode as XmlElement;
|
|
|
|
String name = node.Name;
|
|
|
|
String childType = node.GetAttribute( "t" );
|
|
|
|
name = refl.TypeToIdentifier( name );
|
|
|
|
XmlElement childElem = getNamedChild( allChildren, name );
|
|
|
|
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 );
|
|
|
|
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 );
|
|
|
|
mm_args[0] = serInfo;
|
|
mm_args[1] = Context;
|
|
serCons.Invoke( obj, mm_args );
|
|
|
|
if( objUnOnDeser != null )
|
|
{
|
|
objUnOnDeser.OnDeserialization( objUnOnDeser );
|
|
}
|
|
|
|
/*
|
|
ser.GetObjectData( serInfo, Context );
|
|
|
|
//var serEnum = ;
|
|
|
|
foreach( var serMember in serInfo )
|
|
{
|
|
String name = serMember.Name;
|
|
|
|
name = refl.TypeToIdentifier( name );
|
|
|
|
XmlElement childElem = getNamedChild( allChildren, name );
|
|
|
|
var des = Deserialize( childElem, name );
|
|
}
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
|
|
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, 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;
|
|
}
|
|
|
|
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;
|
|
|
|
Type t = list.GetType();
|
|
|
|
Type[] genT = t.GetGenericArguments();
|
|
|
|
Debug.Assert( genT.Length == 1 );
|
|
|
|
for( int i = 0; i < arrNodeList.Count; ++i )
|
|
{
|
|
if( arrNodeList.Item( i ) is XmlElement )
|
|
{
|
|
XmlElement arrElem = (XmlElement)arrNodeList.Item(i);
|
|
|
|
list.Add( Deserialize( arrElem, mi, genT[0] ) );
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private object DeserializeArray( XmlElement elem, MemberInfo mi, Type type )
|
|
{
|
|
Type typeElem = type.GetElementType();
|
|
|
|
string refString = elem.GetAttribute("ref");
|
|
int refInt = refString.Length > 0 ? Convert.ToInt32(refString) : -1;
|
|
|
|
XmlNodeList arrNodeList = elem.ChildNodes;
|
|
|
|
int length = arrNodeList.Count;
|
|
|
|
Array arr = createArray(typeElem, refInt, length);
|
|
|
|
for( int i = 0; i < arr.Length; ++i )
|
|
{
|
|
if( arrNodeList.Item( i ) is XmlElement )
|
|
{
|
|
XmlElement arrElem = (XmlElement)arrNodeList.Item(i);
|
|
|
|
var finalType = typeElem;
|
|
if (arrElem.HasAttribute("t"))
|
|
{
|
|
var typename = arrElem.GetAttribute("t");
|
|
finalType = FindType(typename);
|
|
|
|
if (finalType == null)
|
|
finalType = typeElem;
|
|
}
|
|
|
|
arr.SetValue( Deserialize( arrElem, mi, finalType), i );
|
|
}
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
|
|
private object createObject( string typename, int refInt )
|
|
{
|
|
Type type = Type.GetType(typename);
|
|
|
|
return createObject( type, refInt );
|
|
}
|
|
|
|
private object createObject( Type type, int refInt )
|
|
{
|
|
TypeCode tc = Type.GetTypeCode(type);
|
|
|
|
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
|
|
{
|
|
//lib.log.info( "Reusing object for {0}", refInt );
|
|
return m_alreadySerialized[refInt];
|
|
}
|
|
else
|
|
{
|
|
object obj = null;
|
|
|
|
try
|
|
{
|
|
//Trying the nice way to creat objects first.
|
|
obj = Activator.CreateInstance( type );
|
|
|
|
}
|
|
catch( Exception )
|
|
{
|
|
try
|
|
{
|
|
obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject( type );
|
|
}
|
|
catch( Exception exInner )
|
|
{
|
|
log.error( $"Got exception {exInner.Message} trying to make an uninitialized object" );
|
|
}
|
|
|
|
}
|
|
|
|
if( obj == null )
|
|
{
|
|
log.warn( $"Could not create object of type {type.Name}" );
|
|
|
|
return obj;
|
|
}
|
|
|
|
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 )
|
|
{
|
|
m_alreadySerialized[refInt] = obj;
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
private Array createArray( string elemTypename, int refInt, int length )
|
|
{
|
|
Type elemType = Type.GetType(elemTypename);
|
|
|
|
return createArray( elemType, refInt, length );
|
|
}
|
|
|
|
private Array createArray( Type elemType, int refInt, int length )
|
|
{
|
|
TypeCode elemTC = Type.GetTypeCode(elemType);
|
|
|
|
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
|
|
{
|
|
return (Array)m_alreadySerialized[refInt];
|
|
}
|
|
else
|
|
{
|
|
Array arr = Array.CreateInstance(elemType, length);
|
|
|
|
if( m_cfg.datastructure == Datastructure.Full )
|
|
{
|
|
m_alreadySerialized[refInt] = arr;
|
|
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
}
|
|
|
|
private ObjectIDGenerator m_objectID = new ObjectIDGenerator();
|
|
private Dictionary<long, object> m_alreadySerialized = new Dictionary<long, object>();
|
|
|
|
#endregion
|
|
|
|
#region Serialize
|
|
|
|
private string getTypeName( Type type )
|
|
{
|
|
//Assembly ass = type.Assembly;
|
|
|
|
//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, MemberInfo mi, object root )
|
|
{
|
|
//lib.log.info( "Serialize( Stream stream, object root ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
|
|
|
|
m_alreadySerialized.Clear();
|
|
m_objectID = new ObjectIDGenerator();
|
|
|
|
XmlTextWriter writer = new XmlTextWriter(stream, System.Text.Encoding.ASCII);
|
|
|
|
writer.Formatting = Formatting.Indented;
|
|
|
|
Serialize( writer, mi, root );
|
|
|
|
//Rely on the parent closing the stream.
|
|
//writer.Close();
|
|
writer.Flush();
|
|
|
|
//lib.log.info( "Serialize END ( Stream stream, object root ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
|
|
}
|
|
|
|
private void Serialize( XmlWriter writer, MemberInfo mi, object root )
|
|
{
|
|
//writer.WriteStartDocument();
|
|
Serialize( writer, mi, root, "root", true );
|
|
//writer.WriteEndDocument();
|
|
}
|
|
|
|
private void Serialize( XmlWriter writer, MemberInfo mi, object root, string name, bool forceType )
|
|
{
|
|
writer.WriteStartElement( name );
|
|
|
|
if( root != null )
|
|
{
|
|
Type type = root.GetType();
|
|
|
|
TypeCode typeCode = Type.GetTypeCode(type);
|
|
|
|
if( typeCode != TypeCode.Object )
|
|
{
|
|
SerializeConcrete( writer, mi, root, forceType );
|
|
}
|
|
else
|
|
{
|
|
if( !type.IsArray )
|
|
{
|
|
SerializeObject( writer, mi, root );
|
|
}
|
|
else
|
|
{
|
|
SerializeArray( writer, mi, root );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.WriteAttributeString( "v", "null" );
|
|
}
|
|
|
|
writer.WriteEndElement();
|
|
}
|
|
|
|
private void SerializeConcrete( XmlWriter writer, MemberInfo mi, object root, bool forceType )
|
|
{
|
|
//TODO: Only write this out if debugging.
|
|
if( forceType )
|
|
{
|
|
writer.WriteAttributeString( "t", getTypeName( root.GetType() ) );
|
|
}
|
|
writer.WriteAttributeString( "v", root.ToString() );
|
|
}
|
|
|
|
private void SerializeObject( XmlWriter writer, MemberInfo mi, object root )
|
|
{
|
|
writer.WriteAttributeString( "t", getTypeName( root.GetType() ) );
|
|
|
|
/*
|
|
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);
|
|
|
|
if( m_cfg.datastructure == Datastructure.Full )
|
|
{
|
|
writer.WriteAttributeString( "ref", refInt.ToString() );
|
|
|
|
}
|
|
|
|
if( first )
|
|
{
|
|
if( m_cfg.datastructure == Datastructure.Full )
|
|
{
|
|
m_alreadySerialized[refInt] = root;
|
|
}
|
|
|
|
Type type = root.GetType();
|
|
|
|
//var whitelistProp = type.GetCustomAttribute<WhitelistPropsAttribute>();
|
|
|
|
//var useWhitelist = whitelistProp != null;
|
|
|
|
|
|
//*
|
|
Type typeISerializable = typeof(ISerializable);
|
|
|
|
if( root is ISerializable ) // type.IsSubclassOf( typeISerializable ) )
|
|
{
|
|
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;
|
|
|
|
name = refl.TypeToIdentifier( name );
|
|
|
|
Serialize( writer, mi, serMember.Value, name, true );
|
|
}
|
|
|
|
//var sc = new SerializationContext(
|
|
|
|
//ser.GetObjectData(
|
|
}
|
|
else
|
|
//*/
|
|
{
|
|
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 );
|
|
|
|
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, MemberInfo mi, object root )
|
|
{
|
|
Array arr = (Array)root;
|
|
|
|
Type typeElem = arr.GetType().GetElementType();
|
|
|
|
Type type = root.GetType();
|
|
|
|
writer.WriteAttributeString( "t", getTypeName( type ) );
|
|
|
|
bool first;
|
|
|
|
long refInt = m_objectID.GetId(root, out first);
|
|
|
|
if( m_cfg.datastructure == Datastructure.Full )
|
|
{
|
|
writer.WriteAttributeString( "ref", refInt.ToString() );
|
|
}
|
|
|
|
if( first )
|
|
{
|
|
if( m_cfg.datastructure == Datastructure.Full )
|
|
{
|
|
m_alreadySerialized[refInt] = root;
|
|
}
|
|
|
|
|
|
for( int i = 0; i < arr.Length; ++i )
|
|
{
|
|
Serialize( writer, mi, arr.GetValue( i ), "i" + i.ToString(), false );
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
}
|