Massive XML changes

This commit is contained in:
Marc Hernandez 2023-12-22 15:28:06 -08:00
parent a27140700c
commit 8bc0aafebc
2 changed files with 830 additions and 810 deletions

View File

@ -180,8 +180,6 @@ static public class refl
BindingFlags.Instance BindingFlags.Instance
); );
var en = PredEnumerator.Create<PropertyInfo>( propArr.AsEnumerable<PropertyInfo>(), var en = PredEnumerator.Create<PropertyInfo>( propArr.AsEnumerable<PropertyInfo>(),
fa => fa.GetCustomAttribute( typeof( NonSerializedAttribute ) ) == null && !list.Exists( f => f.Name == fa.Name ) ); fa => fa.GetCustomAttribute( typeof( NonSerializedAttribute ) ) == null && !list.Exists( f => f.Name == fa.Name ) );

View File

@ -11,85 +11,86 @@ using System.Reflection;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static System.Net.WebRequestMethods;
/* /*
element and attribute names
- Element names are case-sensitive
- Element names must start with a letter or underscore
- Element names cannot start with the letters xml(or XML, or Xml, etc)
- Element names can contain letters, digits, hyphens, underscores, and periods
- Element names cannot contain spaces
* TODO * TODO
* HUGE FLAW IN DESERIALIZATION. If you deser into something it resets everything to default values * x) Add the ability for correctly named attributes to be able to fill in classes
* This didnt matter before when I did full things, but it matters now * x)
*/ */
namespace lib namespace lib
{ {
public class AttDocumentSubclasses : Attribute
{
}
public interface I_Serialize public interface I_Serialize
{ {
void OnSerialize(); void OnSerialize();
void OnDeserialize( object enclosing ); void OnDeserialize( object enclosing );
} }
public class InstanceAttribute : Attribute [Flags]
public enum Types
{ {
Fields = 0b_0001,
Props = 0b_0010,
Implied= 0b_0100,
None = 0b_000,
Default = Fields,
All = Fields | Props,
} }
public class PropertiesAttribute : Attribute public class Ser : Attribute
{ {
public Types Types { get; set; } = Types.Default;
} }
public class WLChildAttributes : Attribute
public class WhitelistAttribute : Attribute
{
}
public class WhitelistFieldsAttribute : Attribute
{ {
public string[] Values { get; private set; } public string[] Values { get; private set; }
public WhitelistFieldsAttribute( params string[] values ) public WLChildAttributes( params string[] values )
{ {
this.Values = values; this.Values = values;
} }
} }
public class WhitelistPropsAttribute : Attribute public class WLChildFieldsAttribute : WLChildAttributes
{ {
public string[] Values { get; private set; } public WLChildFieldsAttribute( params string[] values ) : base( values ) { }
public WhitelistPropsAttribute( params string[] values )
{
this.Values = values;
} }
public class WLChildPropsAttribute : WLChildAttributes
{
public WLChildPropsAttribute( params string[] values ) : base( values ) { }
} }
public enum Datastructure public enum Datastructure
{ {
Invalid, Invalid,
Tree, Tree,
Full, Graph,
} }
public class XmlFormatter2Cfg: Config public class XmlFormatter2Cfg: Config
{ {
public readonly Datastructure datastructure = Datastructure.Full; public readonly Datastructure datastructure = Datastructure.Graph;
public readonly int Version = 2;
} }
public class XmlFormatter2: IFormatter public class XmlFormatter2: IFormatter
@ -135,6 +136,8 @@ namespace lib
Context = new StreamingContext( StreamingContextStates.All ); Context = new StreamingContext( StreamingContextStates.All );
m_cfg = cfg; m_cfg = cfg;
log.warn( $"XML serialization is NOT fast" );
} }
@ -177,7 +180,7 @@ namespace lib
if( t == null ) if( t == null )
return Deserialize( doc.DocumentElement ); return Deserialize( doc.DocumentElement );
return Deserialize( doc.DocumentElement, null, t ); return Deserialize( doc.DocumentElement, null, t, null );
} }
public void DeserializeInto<T>(Stream stream, T obj) public void DeserializeInto<T>(Stream stream, T obj)
@ -196,7 +199,7 @@ namespace lib
{ {
//lib.log.info( "object Deserialize( XmlElement elem ) {0} {1}", m_rndVal, m_alreadySerialized.Count ); //lib.log.info( "object Deserialize( XmlElement elem ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
string typename = elem.HasAttribute("t") ? elem.GetAttribute("t") : elem.Name; string typename = elem.HasAttribute("_.t") ? elem.GetAttribute("_.t") : elem.Name;
return Deserialize( elem, null, typename ); return Deserialize( elem, null, typename );
} }
@ -223,10 +226,10 @@ namespace lib
return null; return null;
} }
return Deserialize( elem, null, type ); return Deserialize( elem, null, type, null );
} }
private object Deserialize( XmlElement elem, MemberInfo mi, Type type, object enclosing = null ) private object Deserialize( XmlElement elem, MemberInfo mi, Type type, object existing /*, object enclosing = null*/ )
{ {
TypeCode typeCode = Type.GetTypeCode(type); TypeCode typeCode = Type.GetTypeCode(type);
@ -238,13 +241,13 @@ namespace lib
{ {
if( !type.IsArray ) if( !type.IsArray )
{ {
object obj = DeserializeObject(elem, mi, type); object obj = DeserializeObject(elem, mi, type, existing);
if( obj is I_Serialize ) if( obj is I_Serialize )
{ {
var iser = obj as I_Serialize; var iser = obj as I_Serialize;
iser.OnDeserialize( enclosing ); iser.OnDeserialize( null );
} }
return obj; return obj;
@ -335,11 +338,11 @@ namespace lib
private Type[] mm_consType = new Type[2]; private Type[] mm_consType = new Type[2];
private object[] mm_args = new object[2]; private object[] mm_args = new object[2];
private object DeserializeObject( XmlElement elem, MemberInfo mi, Type type ) private object DeserializeObject( XmlElement elem, MemberInfo mi, Type type, object existing )
{ {
Type finalType; Type finalType;
object obj;
GetObjectForDeser( elem, type, out finalType, out obj ); var obj = GetObjectForDeser( elem, type, out finalType, existing );
return HydrateObject( elem, mi, finalType, obj ); return HydrateObject( elem, mi, finalType, obj );
} }
@ -378,7 +381,7 @@ namespace lib
String name = node.Name; String name = node.Name;
String childType = node.GetAttribute( "t" ); String childType = node.GetAttribute( "_.t" );
name = refl.TypeToIdentifier( name ); name = refl.TypeToIdentifier( name );
@ -431,57 +434,53 @@ namespace lib
{ {
XmlNodeList allChildren = elem.ChildNodes; XmlNodeList allChildren = elem.ChildNodes;
var doFields = true; bool filterFields, filterProps, doImpls, doFields, doProps;
var doProperties = false; HashSet<string> whitelistFields, whitelistProps;
GetFilters( mi, finalType, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps );
var filterFields = mi?.GetCustomAttribute<WhitelistFieldsAttribute>() != null; /*
var filterProps = mi?.GetCustomAttribute<WhitelistPropsAttribute>() != null; List<MemberInfo> members = new();
HashSet<string> whitelistFields = new( mi?.GetCustomAttribute<WhitelistFieldsAttribute>()?.Values ?? new string[0] ); if( doFields || doImpls )
HashSet<string> whitelistProps = new( mi?.GetCustomAttribute<WhitelistPropsAttribute>()?.Values ?? new string[0] );
if( finalType.GetCustomAttribute<PropertiesAttribute>() != null )
{ {
doProperties = true; members.AddRange( refl.GetAllFields( finalType ) );
} }
if( mi?.GetCustomAttribute<WhitelistPropsAttribute>() != null ) if( doProps || doImpls )
{ {
doProperties = true; members.AddRange( refl.GetAllProperties( finalType ) );
doFields = mi?.GetCustomAttribute<WhitelistFieldsAttribute>() != null;
} }
*/
if( doFields ) if( doFields || doImpls )
{ {
var fields = refl.GetAllFields( finalType ); var fields = refl.GetAllFields( finalType );
foreach( var childFi in fields ) foreach( FieldInfo childFi in fields )
{ {
String name = childFi.Name; String name = childFi.Name;
if( filterFields && !whitelistFields.Contains( name ) ) continue; //This is to convert c# names that would be bad as XML tags
name = refl.TypeToIdentifier( name ); name = refl.TypeToIdentifier( name );
if( FilterField( filterFields, doImpls, whitelistFields, childFi as MemberInfo, name ) ) continue;
XmlElement childElem = getNamedChild( allChildren, name ); XmlElement childElem = getNamedChild( allChildren, name );
if( childElem != null ) if( childElem != null )
{ {
object childObj = Deserialize( childElem, childFi, childFi.FieldType, obj ); object existingObj = childFi.GetValue( obj );
childFi.SetValue( obj, childObj ); object childObj = Deserialize( childElem, childFi, childFi.FieldType, existingObj );
}
else if( fields.Count == 1 )
{
object childObj = Deserialize( elem, childFi, childFi.FieldType, obj );
childFi.SetValue( obj, childObj ); childFi.SetValue( obj, childObj );
} }
} }
} }
if( doProperties )
if( doProps || doImpls )
{ {
var props = refl.GetAllProperties( finalType ); var props = refl.GetAllProperties( finalType );
@ -489,23 +488,23 @@ namespace lib
{ {
String name = childPi.Name; String name = childPi.Name;
if( filterProps && !whitelistProps.Contains( name ) ) continue;
name = refl.TypeToIdentifier( name ); name = refl.TypeToIdentifier( name );
if( FilterField( filterFields, doImpls, whitelistFields, childPi as MemberInfo, name ) ) continue;
XmlElement childElem = getNamedChild( allChildren, name ); XmlElement childElem = getNamedChild( allChildren, name );
if( childElem != null ) if( childElem != null )
{ {
object childObj = Deserialize( childElem, childPi, childPi.PropertyType, obj ); object existingObj = childPi.GetValue( obj );
object childObj = Deserialize( childElem, childPi, childPi.PropertyType, existingObj );
childPi.SetValue( obj, childObj ); childPi.SetValue( obj, childObj );
} }
else if( props.Count == 1 ) else
{ {
object childObj = Deserialize( elem, childPi, childPi.PropertyType, obj ); log.warn( $"" );
childPi.SetValue( obj, childObj );
} }
} }
} }
@ -516,23 +515,37 @@ namespace lib
return obj; return obj;
} }
private void GetObjectForDeser( XmlElement elem, Type type, out Type finalType, out object obj ) private static bool FilterField( bool filterFields, bool doImpls, HashSet<string> whitelistFields, MemberInfo mi, string name )
{ {
string refString = elem.GetAttribute( "ref" ); if( doImpls )
{
if( mi.GetCustomAttribute<WLChildAttributes>() == null ) return true;
}
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1; if( filterFields && !whitelistFields.Contains( name ) ) return true;
return false;
}
private object GetObjectForDeser( XmlElement elem, Type type, out Type finalType, object obj )
{
finalType = type; finalType = type;
if( elem.HasAttribute( "t" ) ) if( elem.HasAttribute( "_.t" ) )
{ {
var typename = elem.GetAttribute( "t" ); var typename = elem.GetAttribute( "_.t" );
finalType = FindType( typename ); finalType = FindType( typename );
if( finalType == null ) if( finalType == null )
finalType = type; finalType = type;
} }
obj = createObject( finalType, refInt ); string refString = elem.GetAttribute( "ref" );
int refInt = refString.Length > 0 ? Convert.ToInt32( refString ) : -1;
obj = createObject( finalType, refInt, obj );
return obj;
} }
private object DeserializeList( XmlElement elem, MemberInfo mi, Type type, IList list ) private object DeserializeList( XmlElement elem, MemberInfo mi, Type type, IList list )
@ -551,7 +564,7 @@ namespace lib
{ {
XmlElement arrElem = (XmlElement)arrNodeList.Item(i); XmlElement arrElem = (XmlElement)arrNodeList.Item(i);
list.Add( Deserialize( arrElem, mi, genT[0] ) ); list.Add( Deserialize( arrElem, mi, genT[0], null ) );
} }
} }
@ -578,39 +591,46 @@ namespace lib
XmlElement arrElem = (XmlElement)arrNodeList.Item(i); XmlElement arrElem = (XmlElement)arrNodeList.Item(i);
var finalType = typeElem; var finalType = typeElem;
if (arrElem.HasAttribute("t")) if (arrElem.HasAttribute("_.t"))
{ {
var typename = arrElem.GetAttribute("t"); var typename = arrElem.GetAttribute("_.t");
finalType = FindType(typename); finalType = FindType(typename);
if (finalType == null) if (finalType == null)
finalType = typeElem; finalType = typeElem;
} }
arr.SetValue( Deserialize( arrElem, mi, finalType), i ); arr.SetValue( Deserialize( arrElem, mi, finalType, null), i );
} }
} }
return arr; return arr;
} }
private object createObject( string typename, int refInt ) private object createObject( string typename, int refInt, object obj )
{ {
Type type = Type.GetType(typename); Type type = Type.GetType(typename);
return createObject( type, refInt ); return createObject( type, refInt, obj );
} }
private object createObject( Type type, int refInt ) private object createObject( Type type, int refInt, object existingObj )
{ {
TypeCode tc = Type.GetTypeCode(type); TypeCode tc = Type.GetTypeCode(type);
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) ) if( m_cfg.datastructure == Datastructure.Graph && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
{ {
//lib.log.info( "Reusing object for {0}", refInt ); //lib.log.info( "Reusing object for {0}", refInt );
return m_alreadySerialized[refInt]; return m_alreadySerialized[refInt];
} }
else
if( existingObj != null )
{
var existingObjType = existingObj.GetType();
if( type == existingObjType ) return existingObj;
}
{ {
object obj = null; object obj = null;
@ -640,7 +660,7 @@ namespace lib
return obj; return obj;
} }
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 ) if( m_cfg.datastructure == Datastructure.Graph && refInt > 0 )
{ {
m_alreadySerialized[refInt] = obj; m_alreadySerialized[refInt] = obj;
} }
@ -660,7 +680,7 @@ namespace lib
{ {
TypeCode elemTC = Type.GetTypeCode(elemType); TypeCode elemTC = Type.GetTypeCode(elemType);
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) ) if( m_cfg.datastructure == Datastructure.Graph && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
{ {
return (Array)m_alreadySerialized[refInt]; return (Array)m_alreadySerialized[refInt];
} }
@ -668,7 +688,7 @@ namespace lib
{ {
Array arr = Array.CreateInstance(elemType, length); Array arr = Array.CreateInstance(elemType, length);
if( m_cfg.datastructure == Datastructure.Full ) if( m_cfg.datastructure == Datastructure.Graph )
{ {
m_alreadySerialized[refInt] = arr; m_alreadySerialized[refInt] = arr;
@ -724,11 +744,11 @@ namespace lib
private void Serialize( XmlWriter writer, MemberInfo mi, object root ) private void Serialize( XmlWriter writer, MemberInfo mi, object root )
{ {
//writer.WriteStartDocument(); //writer.WriteStartDocument();
Serialize( writer, mi, root, "root", true ); Serialize( writer, mi, root, "root", 1, true );
//writer.WriteEndDocument(); //writer.WriteEndDocument();
} }
private void Serialize( XmlWriter writer, MemberInfo mi, object root, string name, bool forceType ) private void Serialize( XmlWriter writer, MemberInfo mi, object root, string name, int depth, bool forceType )
{ {
writer.WriteStartElement( name ); writer.WriteStartElement( name );
@ -746,11 +766,11 @@ namespace lib
{ {
if( !type.IsArray ) if( !type.IsArray )
{ {
SerializeObject( writer, mi, root ); SerializeObject( writer, mi, root, depth );
} }
else else
{ {
SerializeArray( writer, mi, root ); SerializeArray( writer, mi, root, depth );
} }
} }
} }
@ -767,14 +787,19 @@ namespace lib
//TODO: Only write this out if debugging. //TODO: Only write this out if debugging.
if( forceType ) if( forceType )
{ {
writer.WriteAttributeString( "t", getTypeName( root.GetType() ) ); writer.WriteAttributeString( "_.t", getTypeName( root.GetType() ) );
} }
writer.WriteAttributeString( "v", root.ToString() ); writer.WriteAttributeString( "v", root.ToString() );
} }
private void SerializeObject( XmlWriter writer, MemberInfo mi, object root ) private void SerializeObject( XmlWriter writer, MemberInfo mi, object root, int depth )
{ {
writer.WriteAttributeString( "t", getTypeName( root.GetType() ) ); writer.WriteAttributeString( "_.t", getTypeName( root.GetType() ) );
if(depth == 1)
{
writer.WriteAttributeString( "_.version.", $"{m_cfg.Version}" );
}
/* /*
if( root is IList ) if( root is IList )
@ -791,7 +816,7 @@ namespace lib
long refInt = m_objectID.GetId(root, out first); long refInt = m_objectID.GetId(root, out first);
if( m_cfg.datastructure == Datastructure.Full ) if( m_cfg.datastructure == Datastructure.Graph )
{ {
writer.WriteAttributeString( "ref", refInt.ToString() ); writer.WriteAttributeString( "ref", refInt.ToString() );
@ -799,7 +824,7 @@ namespace lib
if( first ) if( first )
{ {
if( m_cfg.datastructure == Datastructure.Full ) if( m_cfg.datastructure == Datastructure.Graph )
{ {
m_alreadySerialized[refInt] = root; m_alreadySerialized[refInt] = root;
} }
@ -830,7 +855,7 @@ namespace lib
name = refl.TypeToIdentifier( name ); name = refl.TypeToIdentifier( name );
Serialize( writer, mi, serMember.Value, name, true ); Serialize( writer, mi, serMember.Value, name, depth, true );
} }
//var sc = new SerializationContext( //var sc = new SerializationContext(
@ -840,30 +865,11 @@ namespace lib
else else
//*/ //*/
{ {
var doFields = true; bool filterFields, filterProps, doImpls, doFields, doProps;
var doProperties = false; HashSet<string> whitelistFields, whitelistProps;
GetFilters( mi, type, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps );
var filterFields = mi?.GetCustomAttribute<WhitelistFieldsAttribute>() != null; if( doFields || doImpls )
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 ); var fields = refl.GetAllFields( type );
@ -871,8 +877,7 @@ namespace lib
{ {
String name = childFi.Name; String name = childFi.Name;
if( filterFields && !whitelistFields.Contains( name ) ) continue; if( FilterField( filterFields, doImpls, whitelistFields, childFi as MemberInfo, name ) ) continue;
object[] objs = childFi.GetCustomAttributes( typeof( NonSerializedAttribute ), true ); object[] objs = childFi.GetCustomAttributes( typeof( NonSerializedAttribute ), true );
@ -885,11 +890,11 @@ namespace lib
name = refl.TypeToIdentifier( name ); name = refl.TypeToIdentifier( name );
Serialize( writer, childFi, childFi.GetValue( root ), name, false ); Serialize( writer, childFi, childFi.GetValue( root ), name, depth + 1, false );
} }
} }
if( doProperties ) if( doProps || doImpls )
{ {
var props = refl.GetAllProperties( type ); var props = refl.GetAllProperties( type );
@ -897,7 +902,7 @@ namespace lib
{ {
String name = childPi.Name; String name = childPi.Name;
if( filterProps && !whitelistProps.Contains( name ) ) continue; if( FilterField( filterProps, doImpls, whitelistProps, childPi as MemberInfo, name ) ) continue;
object[] objs = childPi.GetCustomAttributes( typeof( NonSerializedAttribute ), true ); object[] objs = childPi.GetCustomAttributes( typeof( NonSerializedAttribute ), true );
@ -908,7 +913,7 @@ namespace lib
name = refl.TypeToIdentifier( name ); name = refl.TypeToIdentifier( name );
Serialize( writer, childPi, childPi.GetValue( root ), name, false ); Serialize( writer, childPi, childPi.GetValue( root ), name, depth + 1, false );
} }
} }
@ -916,7 +921,24 @@ namespace lib
} }
} }
private void SerializeArray( XmlWriter writer, MemberInfo mi, object root ) private static void GetFilters( MemberInfo mi, Type type, out bool filterFields, out bool filterProps, out bool doImpls, out bool doFields, out bool doProps, out HashSet<string> whitelistFields, out HashSet<string> whitelistProps )
{
var custWLFields = mi?.GetCustomAttribute<WLChildFieldsAttribute>();
var custWLProps = mi?.GetCustomAttribute<WLChildPropsAttribute>();
filterFields = custWLFields != null;
filterProps = custWLProps != null;
var typesTodo = type.GetCustomAttribute<Ser>()?.Types ?? Types.None;
doImpls = typesTodo.HasFlag( Types.Implied );
doFields = filterFields || typesTodo.HasFlag( Types.Fields );
doProps = filterProps || typesTodo.HasFlag( Types.Props );
whitelistFields = new( custWLFields?.Values ?? new string[0] );
whitelistProps = new( custWLProps?.Values ?? new string[0] );
}
private void SerializeArray( XmlWriter writer, MemberInfo mi, object root, int depth )
{ {
Array arr = (Array)root; Array arr = (Array)root;
@ -924,20 +946,20 @@ namespace lib
Type type = root.GetType(); Type type = root.GetType();
writer.WriteAttributeString( "t", getTypeName( type ) ); writer.WriteAttributeString( "_.t", getTypeName( type ) );
bool first; bool first;
long refInt = m_objectID.GetId(root, out first); long refInt = m_objectID.GetId(root, out first);
if( m_cfg.datastructure == Datastructure.Full ) if( m_cfg.datastructure == Datastructure.Graph )
{ {
writer.WriteAttributeString( "ref", refInt.ToString() ); writer.WriteAttributeString( "ref", refInt.ToString() );
} }
if( first ) if( first )
{ {
if( m_cfg.datastructure == Datastructure.Full ) if( m_cfg.datastructure == Datastructure.Graph )
{ {
m_alreadySerialized[refInt] = root; m_alreadySerialized[refInt] = root;
} }
@ -945,7 +967,7 @@ namespace lib
for( int i = 0; i < arr.Length; ++i ) for( int i = 0; i < arr.Length; ++i )
{ {
Serialize( writer, mi, arr.GetValue( i ), "i" + i.ToString(), false ); Serialize( writer, mi, arr.GetValue( i ), "i" + i.ToString(), depth + 1, false );
} }
} }
} }