Various XML fixes

This commit is contained in:
Marc Hernandez 2024-04-27 17:51:39 -07:00
parent d237c438c7
commit 72a28b5f81
2 changed files with 231 additions and 155 deletions

View File

@ -1,15 +1,25 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using lib;
namespace imm; namespace imm;
/*
T O D O :
T O D O :
T O D O :
x) Add unit tests for all this. This will definitely benefit from them
*/
static public class Util static public class Util
{ {
//This can handle both Timed and Recorded //This can handle both Timed and Recorded
@ -38,7 +48,7 @@ static public class Util
} }
} }
//[lib.Ser( Types = lib.Types.None )]
public record class Versioned<T> public record class Versioned<T>
where T : Versioned<T> where T : Versioned<T>
{ {
@ -61,6 +71,7 @@ public record class Versioned<T>
public MetaData Meta => MetaStorage; public MetaData Meta => MetaStorage;
[lib.Dont]
public ChangeDelegate OnChange = (x, y) => {}; public ChangeDelegate OnChange = (x, y) => {};
public T Process( Func<T, T> fn, string reason = "" ) public T Process( Func<T, T> fn, string reason = "" )
@ -78,11 +89,14 @@ public record class Versioned<T>
} }
} }
//[lib.Ser( Types = lib.Types.None )]
public record class Recorded<T> : Versioned<T> public record class Recorded<T> : Versioned<T>
where T : Recorded<T> where T : Recorded<T>
{ {
new public record class MetaData : Versioned<T>.MetaData new public record class MetaData : Versioned<T>.MetaData
{ {
[lib.Dont]
public T? ZZOld { get; internal set; } public T? ZZOld { get; internal set; }
public T? Old => ZZOld; public T? Old => ZZOld;
public string Expression { get; internal set; } = ""; public string Expression { get; internal set; } = "";

View File

@ -12,6 +12,7 @@ using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static System.Net.WebRequestMethods; using static System.Net.WebRequestMethods;
using System.Linq;
/* /*
@ -44,11 +45,13 @@ namespace lib
[Flags] [Flags]
public enum Types public enum Types
{ {
Fields = 0b_0001, Fields = 0b_0001,
Props = 0b_0010, Props = 0b_0010,
Implied= 0b_0100, Implied = 0b_0100,
Explicit= 0b_1000,
None = 0b_000, None = 0b_0000,
Default = Fields, Default = Fields,
All = Fields | Props, All = Fields | Props,
} }
@ -58,6 +61,14 @@ namespace lib
public Types Types { get; set; } = Types.Default; public Types Types { get; set; } = Types.Default;
} }
public class Do : Attribute
{
}
public class Dont : Attribute
{
}
public class ChildAttributes : Attribute public class ChildAttributes : Attribute
{ {
public string[] Values { get; private set; } public string[] Values { get; private set; }
@ -82,10 +93,10 @@ namespace lib
{ {
Invalid, Invalid,
// // Breaks on circular datastructures since it will go on forever
Tree, Tree,
// // Works for everything.
Graph, Graph,
} }
@ -94,7 +105,7 @@ namespace lib
public class XmlFormatter2Cfg: Config public class XmlFormatter2Cfg: Config
{ {
public Datastructure datastructure = Datastructure.Graph; public Datastructure datastructure = Datastructure.Tree;
public int Version = 2; public int Version = 2;
@ -132,12 +143,12 @@ namespace lib
public XmlFormatter2() public XmlFormatter2()
{ {
Context = new StreamingContext( StreamingContextStates.All ); //Context = new StreamingContext( StreamingContextStates.All );
} }
public XmlFormatter2( XmlFormatter2Cfg cfg ) public XmlFormatter2( XmlFormatter2Cfg cfg )
{ {
Context = new StreamingContext( StreamingContextStates.All ); //Context = new StreamingContext( StreamingContextStates.All );
m_cfg = cfg; m_cfg = cfg;
@ -406,8 +417,10 @@ namespace lib
mm_consType[1] = typeof( StreamingContext ); 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 );
var context = new StreamingContext( StreamingContextStates.File, obj );
mm_args[0] = serInfo; mm_args[0] = serInfo;
mm_args[1] = Context; mm_args[1] = context;
serCons.Invoke( obj, mm_args ); serCons.Invoke( obj, mm_args );
if( objUnOnDeser != null ) if( objUnOnDeser != null )
@ -434,103 +447,117 @@ namespace lib
} }
else else
{ {
XmlNodeList allChildren = elem.ChildNodes; HydrateObjectOfNarrowType(elem, mi, finalType, obj);
bool filterFields, filterProps, doImpls, doFields, doProps;
HashSet<string> whitelistFields, whitelistProps;
GetFilters( m_cfg.TypesDefault, mi, finalType, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps );
/*
List<MemberInfo> members = new();
if( doFields || doImpls )
{
members.AddRange( refl.GetAllFields( finalType ) );
}
if( doProps || doImpls )
{
members.AddRange( refl.GetAllProperties( finalType ) );
}
*/
if( doFields || doImpls )
{
var fields = refl.GetAllFields( finalType );
foreach( FieldInfo childFi in fields )
{
String name = childFi.Name;
//This is to convert c# names that would be bad as XML tags
name = refl.TypeToIdentifier( name );
if( FilterField( filterFields, doImpls, whitelistFields, childFi as MemberInfo, name ) ) continue;
XmlElement childElem = getNamedChild( allChildren, name );
if( childElem != null )
{
object existingObj = childFi.GetValue( obj );
object childObj = Deserialize( childElem, childFi, childFi.FieldType, existingObj );
childFi.SetValue( obj, childObj );
}
}
}
if( doProps || doImpls )
{
var props = refl.GetAllProperties( finalType );
foreach( var childPi in props )
{
String name = childPi.Name;
name = refl.TypeToIdentifier( name );
if( FilterField( filterProps, doImpls, whitelistProps, childPi as PropertyInfo, name ) ) continue;
XmlElement childElem = getNamedChild( allChildren, name );
if( childElem != null )
{
object existingObj = childPi.GetValue( obj );
object childObj = Deserialize( childElem, childPi, childPi.PropertyType, existingObj );
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 );
}
}
}
}
} }
return obj; return obj;
} }
private void HydrateObjectOfNarrowType(XmlElement elem, MemberInfo mi, Type narrowType, object obj)
{
XmlNodeList allChildren = elem.ChildNodes;
bool filterFields, filterProps, doImpls, doFields, doProps;
HashSet<string> whitelistFields, whitelistProps;
GetFilters(m_cfg.TypesDefault, mi, narrowType, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps);
if (doFields || doImpls)
{
var fields = refl.GetAllFields(narrowType);
foreach (FieldInfo childFi in fields)
{
String name = childFi.Name;
var dontAtt = childFi.GetCustomAttributes<lib.Dont>();
if( name.StartsWith( "<" ) && name.EndsWith( "BackingField" ) )
{
var gtIndex = name.IndexOf( '>' );
var propName = name.Substring( 1, gtIndex - 1 );
var propInfo = narrowType.GetProperty( propName );
dontAtt = propInfo.GetCustomAttributes<lib.Dont>();
}
if( dontAtt.Any() )
{
continue;
}
//if( name.EndsWith( ) )
//This is to convert c# names that would be bad as XML tags
name = refl.TypeToIdentifier(name);
if (FilterField(filterFields, doImpls, whitelistFields, childFi as MemberInfo, name)) continue;
XmlElement childElem = getNamedChild(allChildren, name);
if (childElem != null)
{
object existingObj = childFi.GetValue(obj);
object childObj = Deserialize(childElem, childFi, childFi.FieldType, existingObj);
childFi.SetValue(obj, childObj);
}
}
}
if (doProps || doImpls)
{
var props = refl.GetAllProperties(narrowType);
foreach (var childPi in props)
{
String name = childPi.Name;
var dontAtt = childPi.GetCustomAttributes<lib.Dont>();
if( dontAtt.Any() )
{
continue;
}
name = refl.TypeToIdentifier(name);
if (FilterField(filterProps, doImpls, whitelistProps, childPi as PropertyInfo, name)) continue;
XmlElement childElem = getNamedChild(allChildren, name);
if (childElem != null)
{
object existingObj = childPi.GetValue(obj);
object childObj = Deserialize(childElem, childPi, childPi.PropertyType, existingObj);
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);
}
}
}
}
}
private static bool FilterField( bool filterFields, bool doImpls, HashSet<string> whitelistFields, MemberInfo mi, string name ) private static bool FilterField( bool filterFields, bool doImpls, HashSet<string> whitelistFields, MemberInfo mi, string name )
{ {
if( doImpls ) if( doImpls )
{ {
if( mi.GetCustomAttribute<ChildAttributes>() == null ) return true; if( mi.GetCustomAttribute<ChildAttributes>( true ) == null ) return true;
} }
if( filterFields && !whitelistFields.Contains( name ) ) return true; if( filterFields && !whitelistFields.Contains( name ) ) return true;
@ -868,7 +895,7 @@ namespace lib
if( first ) if( first )
{ {
if( m_cfg.datastructure == Datastructure.Graph ) if (m_cfg.datastructure == Datastructure.Graph)
{ {
m_alreadySerialized[refInt] = root; m_alreadySerialized[refInt] = root;
} }
@ -878,19 +905,26 @@ namespace lib
//* //*
Type typeISerializable = typeof(ISerializable); Type typeISerializable = typeof(ISerializable);
if( root is ISerializable ser ) if (root is ISerializable ser)
{ {
if ((root is Delegate))
{
return;
}
var serInfo = new SerializationInfo(type, new FormatterConverter()); var serInfo = new SerializationInfo(type, new FormatterConverter());
ser.GetObjectData( serInfo, Context ); var context = new StreamingContext(StreamingContextStates.File, root);
foreach( var serMember in serInfo ) ser.GetObjectData(serInfo, context);
foreach (var serMember in serInfo)
{ {
String name = serMember.Name; String name = serMember.Name;
name = refl.TypeToIdentifier( name ); name = refl.TypeToIdentifier(name);
Serialize( writer, mi, serMember.Value, name, depth, true ); Serialize(writer, mi, serMember.Value, name, depth, true);
} }
return; return;
@ -900,9 +934,9 @@ namespace lib
var tryType = type; var tryType = type;
TypeProxy? proxy = null; TypeProxy? proxy = null;
while( tryType != typeof(object) ) while (tryType != typeof(object))
{ {
if( m_cfg.TypeProxy.TryGetValue( tryType, out var newProxy ) ) if (m_cfg.TypeProxy.TryGetValue(tryType, out var newProxy))
{ {
proxy = newProxy; proxy = newProxy;
break; break;
@ -911,82 +945,110 @@ namespace lib
tryType = tryType.BaseType; tryType = tryType.BaseType;
} }
if( proxy.HasValue ) if (proxy.HasValue)
{ {
var proxyStr = proxy.Value.ser( root ); var proxyStr = proxy.Value.ser(root);
writer.WriteAttributeString( "proxy", proxyStr ); writer.WriteAttributeString("proxy", proxyStr);
return; return;
} }
} }
//*/ //*/
SerializeObjectOfNarrowType(writer, mi, root, depth, type);
}
}
private void SerializeObjectOfNarrowType(XmlWriter writer, MemberInfo mi, object root, int depth, Type narrowType)
{
bool filterFields, filterProps, doImpls, doFields, doProps;
HashSet<string> whitelistFields, whitelistProps;
GetFilters(m_cfg.TypesDefault, mi, narrowType, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps);
if (doFields || doImpls)
{
var fields = refl.GetAllFields(narrowType);
foreach (var childFi in fields)
{ {
bool filterFields, filterProps, doImpls, doFields, doProps; String name = childFi.Name;
HashSet<string> whitelistFields, whitelistProps; var dontAtt = childFi.GetCustomAttributes<lib.Dont>();
GetFilters( m_cfg.TypesDefault, mi, type, out filterFields, out filterProps, out doImpls, out doFields, out doProps, out whitelistFields, out whitelistProps );
if( doFields || doImpls )
if( name.StartsWith( "<" ) && name.EndsWith( "BackingField" ) )
{ {
var fields = refl.GetAllFields( type ); var gtIndex = name.IndexOf( '>' );
foreach( var childFi in fields ) var propName = name.Substring( 1, gtIndex - 1 );
{
String name = childFi.Name;
if( FilterField( filterFields, doImpls, whitelistFields, childFi as MemberInfo, name ) ) continue; var propInfo = narrowType.GetProperty( propName );
object[] objs = childFi.GetCustomAttributes( typeof( NonSerializedAttribute ), true ); dontAtt = propInfo.GetCustomAttributes<lib.Dont>();
if( objs.Length > 0 )
{
continue;
}
//if( childFi.GetCustomAttribute<WhitelistAttribute>() )
name = refl.TypeToIdentifier( name );
Serialize( writer, childFi, childFi.GetValue( root ), name, depth + 1, false );
}
} }
if( doProps || doImpls )
if( dontAtt.Any() )
{ {
var props = refl.GetAllProperties( type ); continue;
foreach( var childPi in props )
{
String name = childPi.Name;
if( FilterField( filterProps, doImpls, whitelistProps, childPi as MemberInfo, 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, depth + 1, false );
}
} }
if (FilterField(filterFields, doImpls, whitelistFields, childFi as MemberInfo, 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, depth + 1, false);
} }
} }
if (doProps || doImpls)
{
var props = refl.GetAllProperties(narrowType);
foreach (var childPi in props)
{
String name = childPi.Name;
var dontAtt = childPi.GetCustomAttributes<lib.Dont>();
if( dontAtt.Any() )
{
continue;
}
if (FilterField(filterProps, doImpls, whitelistProps, childPi as MemberInfo, 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, depth + 1, false);
}
}
} }
private static void GetFilters( Types TypesDefault, 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 ) private static void GetFilters( Types TypesDefault, 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<ChildFieldsAttribute>(); var custWLFields = mi?.GetCustomAttribute<ChildFieldsAttribute>( true );
var custWLProps = mi?.GetCustomAttribute<ChildPropsAttribute>(); var custWLProps = mi?.GetCustomAttribute<ChildPropsAttribute>( true );
filterFields = custWLFields != null; filterFields = custWLFields != null;
filterProps = custWLProps != null; filterProps = custWLProps != null;
var typesTodo = type.GetCustomAttribute<Ser>()?.Types ?? TypesDefault; var typesTodo = type.GetCustomAttribute<Ser>( true )?.Types ?? TypesDefault;
doImpls = typesTodo.HasFlag( Types.Implied ); doImpls = typesTodo.HasFlag( Types.Implied );
doFields = filterFields || typesTodo.HasFlag( Types.Fields ); doFields = filterFields || typesTodo.HasFlag( Types.Fields );