Ser updates
This commit is contained in:
parent
3f850cc9b0
commit
4563db15a6
@ -1,4 +1,10 @@
|
|||||||
using System;
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// S H A R P L I B
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -95,7 +101,7 @@ namespace db
|
|||||||
int m_processed = 0;
|
int m_processed = 0;
|
||||||
//volatile string ProcessingDebug = "";
|
//volatile string ProcessingDebug = "";
|
||||||
|
|
||||||
Act m_debugCurrentAct = null;
|
Act? m_debugCurrentAct = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
173
ser/CodeGen.cs
Normal file
173
ser/CodeGen.cs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// S H A R P L I B
|
||||||
|
//
|
||||||
|
/// // (c) 2003..2025
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace ser;
|
||||||
|
|
||||||
|
public record CodeGenConfig : imm.Recorded<CodeGenConfig>
|
||||||
|
{
|
||||||
|
// Whitelists (if needed, otherwise rely on attributes/defaults)
|
||||||
|
public ImmutableDictionary<string, ImmutableList<string>> WLProps { get; init; } = ImmutableDictionary<string, ImmutableList<string>>.Empty;
|
||||||
|
public ImmutableDictionary<string, ImmutableList<string>> WLFields { get; init; } = ImmutableDictionary<string, ImmutableList<string>>.Empty;
|
||||||
|
|
||||||
|
// Default member types to process
|
||||||
|
public lib.Types TypesDefault { get; init; } = lib.Types.Fields | lib.Types.Props;
|
||||||
|
|
||||||
|
// How to handle backing fields (might be less relevant for code gen)
|
||||||
|
public BackingFieldNaming Naming { get; init; } = BackingFieldNaming.Regular;
|
||||||
|
|
||||||
|
public static CodeGenConfig Default { get; } = new CodeGenConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record GenMemberMeta(
|
||||||
|
MemberInfo Info,
|
||||||
|
Type Type,
|
||||||
|
string Name, // Name for code generation (usually original)
|
||||||
|
bool IsPrimitive,
|
||||||
|
bool IsCollection,
|
||||||
|
Type? CollectionElementType,
|
||||||
|
bool HasDo,
|
||||||
|
bool HasDont
|
||||||
|
);
|
||||||
|
|
||||||
|
public record TypeStructureInfo(
|
||||||
|
Type Type,
|
||||||
|
List<GenMemberMeta> Members,
|
||||||
|
bool IsValueType,
|
||||||
|
bool IsCollection
|
||||||
|
);
|
||||||
|
|
||||||
|
public class TypeStructureAnalyzer
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<Type, TypeStructureInfo> _cache = new();
|
||||||
|
private readonly CodeGenConfig _cfg;
|
||||||
|
|
||||||
|
public TypeStructureAnalyzer(CodeGenConfig cfg) => _cfg = cfg;
|
||||||
|
|
||||||
|
public TypeStructureInfo Get(Type type) => _cache.GetOrAdd(type, BuildTypeInfo);
|
||||||
|
|
||||||
|
private TypeStructureInfo BuildTypeInfo(Type type)
|
||||||
|
{
|
||||||
|
var members = new List<GenMemberMeta>();
|
||||||
|
var typesTodo = type.GetCustomAttribute<lib.Ser>(true)?.Types ?? _cfg.TypesDefault;
|
||||||
|
bool doFields = typesTodo.HasFlag(lib.Types.Fields);
|
||||||
|
bool doProps = typesTodo.HasFlag(lib.Types.Props);
|
||||||
|
|
||||||
|
// Track processed names to avoid duplicates (e.g., field + prop)
|
||||||
|
var processedNames = new HashSet<string>();
|
||||||
|
|
||||||
|
// Process Properties First (often preferred interface)
|
||||||
|
if (doProps)
|
||||||
|
{
|
||||||
|
foreach (var pi in refl.GetAllProperties(type))
|
||||||
|
{
|
||||||
|
if (ProcessMember(pi, false, false, new HashSet<string>(), false, members))
|
||||||
|
{
|
||||||
|
processedNames.Add(pi.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process Fields, avoiding those already covered by properties
|
||||||
|
if (doFields)
|
||||||
|
{
|
||||||
|
foreach (var fi in refl.GetAllFields(type))
|
||||||
|
{
|
||||||
|
var (isBacking, propName) = IsBackingField(fi);
|
||||||
|
string nameToTest = isBacking ? propName : fi.Name;
|
||||||
|
|
||||||
|
if (!processedNames.Contains(nameToTest))
|
||||||
|
{
|
||||||
|
if(ProcessMember(fi, false, false, new HashSet<string>(), false, members))
|
||||||
|
{
|
||||||
|
processedNames.Add(nameToTest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TypeStructureInfo(
|
||||||
|
type,
|
||||||
|
members,
|
||||||
|
type.IsValueType,
|
||||||
|
typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ProcessMember(MemberInfo mi, bool filter, bool doImpls, HashSet<string> whitelist, bool isImm, List<GenMemberMeta> members)
|
||||||
|
{
|
||||||
|
var (hasDo, hasDont, propName) = GetMemberAttributes(mi, out var actualMiForAtts);
|
||||||
|
|
||||||
|
if (hasDont) return false;
|
||||||
|
if (mi.GetCustomAttribute<NonSerializedAttribute>(true) != null) return false;
|
||||||
|
if (mi.Name.Contains("k__BackingField") && !propName.Any()) return false; // Skip if backing but no prop found
|
||||||
|
|
||||||
|
string name = string.IsNullOrEmpty(propName) ? mi.Name : propName;
|
||||||
|
|
||||||
|
// Add filtering logic if needed (based on whitelist, etc.)
|
||||||
|
|
||||||
|
var type = (mi is FieldInfo fi) ? fi.FieldType : ((PropertyInfo)mi).PropertyType;
|
||||||
|
bool isCollection = typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string);
|
||||||
|
Type? elementType = isCollection ? GetElementType(type) : null;
|
||||||
|
bool isPrimitive = Type.GetTypeCode(type) != TypeCode.Object && !isCollection;
|
||||||
|
|
||||||
|
members.Add(new GenMemberMeta(
|
||||||
|
mi, type, name, isPrimitive, isCollection, elementType, hasDo, hasDont
|
||||||
|
));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private (bool, string) IsBackingField(FieldInfo fi)
|
||||||
|
{
|
||||||
|
if (fi.Name.StartsWith("<") && fi.Name.EndsWith("BackingField"))
|
||||||
|
{
|
||||||
|
var gtIndex = fi.Name.IndexOf('>');
|
||||||
|
if (gtIndex > 1) {
|
||||||
|
return (true, fi.Name.Substring(1, gtIndex - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (false, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private (bool hasDo, bool hasDont, string propName) GetMemberAttributes(MemberInfo mi, out MemberInfo actualMi)
|
||||||
|
{
|
||||||
|
actualMi = mi;
|
||||||
|
string propName = "";
|
||||||
|
if (mi is FieldInfo fi && IsBackingField(fi).Item1)
|
||||||
|
{
|
||||||
|
propName = IsBackingField(fi).Item2;
|
||||||
|
var propInfo = mi.DeclaringType?.GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
if (propInfo != null) actualMi = propInfo;
|
||||||
|
} else if (mi is PropertyInfo) {
|
||||||
|
propName = mi.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
actualMi.GetCustomAttribute<lib.Do>() != null,
|
||||||
|
actualMi.GetCustomAttribute<lib.Dont>() != null,
|
||||||
|
propName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type GetElementType(Type collectionType)
|
||||||
|
{
|
||||||
|
if (collectionType.IsArray) return collectionType.GetElementType()!;
|
||||||
|
if (collectionType.IsGenericType) return collectionType.GetGenericArguments().Last(); // Usually last (e.g., List<T>, Dict<K,V>)
|
||||||
|
return typeof(object); // Fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add GetFilters and FilterField if needed, or simplify as above
|
||||||
|
}
|
||||||
74
ser/CodeGenerator.cs
Normal file
74
ser/CodeGenerator.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace ser;
|
||||||
|
|
||||||
|
public abstract class CodeGenerator
|
||||||
|
{
|
||||||
|
protected StringBuilder _sb = new StringBuilder();
|
||||||
|
protected int _indent = 0;
|
||||||
|
protected TypeStructureAnalyzer _analyzer;
|
||||||
|
protected CodeGenConfig _config;
|
||||||
|
protected HashSet<Type> _generatedTypes = new(); // Track to avoid re-generating
|
||||||
|
|
||||||
|
public CodeGenerator( CodeGenConfig config )
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
_analyzer = new TypeStructureAnalyzer( config );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main entry point
|
||||||
|
public string Generate( Type type, string ns = "GeneratedCode" )
|
||||||
|
{
|
||||||
|
_sb.Clear();
|
||||||
|
WriteLine( "using System;" );
|
||||||
|
WriteLine( "using System.Collections.Generic;" );
|
||||||
|
WriteLine( "using System.Linq;" );
|
||||||
|
WriteLine( "" );
|
||||||
|
WriteLine( $"namespace {ns};" );
|
||||||
|
WriteLine( "" );
|
||||||
|
GenerateForType( type );
|
||||||
|
return _sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core generation logic - needs to be recursive for dependencies
|
||||||
|
protected virtual void GenerateForType( Type type )
|
||||||
|
{
|
||||||
|
if( type == null || !CanGenerateFor( type ) || _generatedTypes.Contains( type ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
_generatedTypes.Add( type );
|
||||||
|
var info = _analyzer.Get( type );
|
||||||
|
|
||||||
|
// Generate dependencies first
|
||||||
|
foreach( var member in info.Members )
|
||||||
|
{
|
||||||
|
GenerateForType( member.Type );
|
||||||
|
if( member.IsCollection && member.CollectionElementType != null )
|
||||||
|
{
|
||||||
|
GenerateForType( member.CollectionElementType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the actual code
|
||||||
|
GenerateClassHeader( info );
|
||||||
|
BeginBlock();
|
||||||
|
GenerateClassBody( info );
|
||||||
|
EndBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract methods to be implemented by specific generators
|
||||||
|
protected abstract void GenerateClassHeader( TypeStructureInfo info );
|
||||||
|
protected abstract void GenerateClassBody( TypeStructureInfo info );
|
||||||
|
protected abstract bool CanGenerateFor( Type type ); // Check if we should generate for this type
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
protected void WriteLine( string line = "" ) => _sb.AppendLine( new string( '\t', _indent ) + line );
|
||||||
|
protected void BeginBlock() { WriteLine( "{" ); _indent++; }
|
||||||
|
protected void EndBlock() { _indent--; WriteLine( "}" ); }
|
||||||
|
protected string GetTypeName( Type t ) => t.IsGenericType
|
||||||
|
? $"{t.Name.Split( '`' )[0]}<{string.Join( ", ", t.GetGenericArguments().Select( GetTypeName ) )}>"
|
||||||
|
: t.Name; // Basic handling, needs improvement for full names/namespaces
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@ using System.Diagnostics;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
namespace serdes;
|
namespace ser;
|
||||||
|
|
||||||
#region Attributes & Enums (Mostly unchanged, ensure these exist)
|
#region Attributes & Enums (Mostly unchanged, ensure these exist)
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ namespace serdes;
|
|||||||
public enum Datastructure { Tree, Graph }
|
public enum Datastructure { Tree, Graph }
|
||||||
public enum BackingFieldNaming { Short, Regular }
|
public enum BackingFieldNaming { Short, Regular }
|
||||||
public enum POD { Attributes, Elements }
|
public enum POD { Attributes, Elements }
|
||||||
public record struct TypeProxy( Func<object, string> ser, Func<string, string, object> des );
|
public record struct TypeProxy( Func<object, string> fnSer, Func<string, string, object> fnDes );
|
||||||
|
|
||||||
public record XmlCfg : imm.Recorded<XmlCfg>
|
public record XmlCfg : imm.Recorded<XmlCfg>
|
||||||
{
|
{
|
||||||
@ -303,8 +303,8 @@ public class TypeResolver
|
|||||||
public interface ITypeHandler
|
public interface ITypeHandler
|
||||||
{
|
{
|
||||||
bool CanHandle( TypeSerializationInfo typeInfo, XmlElement? elem = null ); // Elem needed for Deser
|
bool CanHandle( TypeSerializationInfo typeInfo, XmlElement? elem = null ); // Elem needed for Deser
|
||||||
void WriteXml( XmlSer ser, XmlWriter writer, object? obj, string name, Type memberType, bool forceType );
|
void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType );
|
||||||
object? ReadXml( XmlSer ser, XmlElement elem, Type expectedType, object? existing );
|
object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing );
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Primitive Handler ---
|
// --- Primitive Handler ---
|
||||||
@ -312,15 +312,15 @@ public class PrimitiveHandler : ITypeHandler
|
|||||||
{
|
{
|
||||||
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) => Type.GetTypeCode( ti.Type ) != TypeCode.Object && !typeof( IEnumerable ).IsAssignableFrom( ti.Type );
|
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) => Type.GetTypeCode( ti.Type ) != TypeCode.Object && !typeof( IEnumerable ).IsAssignableFrom( ti.Type );
|
||||||
|
|
||||||
public object? ReadXml( XmlSer ser, XmlElement elem, Type expectedType, object? existing )
|
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
|
||||||
{
|
{
|
||||||
string val = elem.HasAttribute( "v" ) ? elem.GetAttribute( "v" ) : elem.InnerText;
|
string val = elem.HasAttribute( "v" ) ? elem.GetAttribute( "v" ) : elem.InnerText;
|
||||||
if( val == "null" )
|
if( val == "null" )
|
||||||
return null;
|
return null;
|
||||||
return ser._resolver.ConvertSimple( val, expectedType );
|
return xml._resolver.ConvertSimple( val, expectedType );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteXml( XmlSer ser, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
{
|
{
|
||||||
if( obj == null )
|
if( obj == null )
|
||||||
{
|
{
|
||||||
@ -330,14 +330,14 @@ public class PrimitiveHandler : ITypeHandler
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool writeElements = ser._cfg.POD == POD.Elements || forceType || !( writer is XmlTextWriter );
|
bool writeElements = xml._cfg.POD == POD.Elements || forceType || !( writer is XmlTextWriter );
|
||||||
if( !writeElements && writer is XmlTextWriter tw )
|
if( !writeElements && writer is XmlTextWriter tw )
|
||||||
writeElements = tw.WriteState != WriteState.Element;
|
writeElements = tw.WriteState != WriteState.Element;
|
||||||
|
|
||||||
if( writeElements )
|
if( writeElements )
|
||||||
writer.WriteStartElement( name );
|
writer.WriteStartElement( name );
|
||||||
|
|
||||||
if( forceType || ser._cfg.POD == POD.Elements )
|
if( forceType || xml._cfg.POD == POD.Elements )
|
||||||
{
|
{
|
||||||
if( forceType )
|
if( forceType )
|
||||||
writer.WriteAttributeString( "_.t", obj.GetType().FullName );
|
writer.WriteAttributeString( "_.t", obj.GetType().FullName );
|
||||||
@ -359,29 +359,29 @@ public class ProxyHandler : ITypeHandler
|
|||||||
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) => ti.IsProxy || ( elem?.HasAttribute( "proxy" ) ?? false );
|
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) => ti.IsProxy || ( elem?.HasAttribute( "proxy" ) ?? false );
|
||||||
|
|
||||||
|
|
||||||
public object? ReadXml( XmlSer ser, XmlElement elem, Type expectedType, object? existing )
|
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
|
||||||
{
|
{
|
||||||
var ti = ser._meta.Get( expectedType ); // Re-get to ensure we have proxy info
|
var ti = xml._meta.Get( expectedType ); // Re-get to ensure we have proxy info
|
||||||
if( !elem.HasAttribute( "proxy" ) || !ti.ProxyDef.HasValue )
|
if( !elem.HasAttribute( "proxy" ) || !ti.ProxyDef.HasValue )
|
||||||
{
|
{
|
||||||
log.warn( $"Proxy read failed for {expectedType.Name}. Fallback needed." );
|
log.warn( $"Proxy read failed for {expectedType.Name}. Fallback needed." );
|
||||||
return null; // Should fall back or throw
|
return null; // Should fall back or throw
|
||||||
}
|
}
|
||||||
var proxyVal = elem.GetAttribute( "proxy" );
|
var proxyVal = elem.GetAttribute( "proxy" );
|
||||||
return ti.ProxyDef.Value.des( expectedType.FullName, proxyVal );
|
return ti.ProxyDef.Value.fnDes( expectedType.FullName, proxyVal );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteXml( XmlSer ser, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
{
|
{
|
||||||
if( obj == null )
|
if( obj == null )
|
||||||
{ ser.GetHandler( typeof( object ) ).WriteXml( ser, writer, null, name, memberType, forceType ); return; }
|
{ xml.GetHandler( typeof( object ) ).WriteXml( xml, writer, null, name, memberType, forceType ); return; }
|
||||||
|
|
||||||
var ti = ser._meta.Get( obj.GetType() );
|
var ti = xml._meta.Get( obj.GetType() );
|
||||||
if( !ti.ProxyDef.HasValue )
|
if( !ti.ProxyDef.HasValue )
|
||||||
{ log.error( "Proxy write called without proxy def!" ); return; }
|
{ log.error( "Proxy write called without proxy def!" ); return; }
|
||||||
|
|
||||||
writer.WriteStartElement( name );
|
writer.WriteStartElement( name );
|
||||||
var proxyStr = ti.ProxyDef.Value.ser( obj );
|
var proxyStr = ti.ProxyDef.Value.fnSer( obj );
|
||||||
writer.WriteAttributeString( "proxy", proxyStr );
|
writer.WriteAttributeString( "proxy", proxyStr );
|
||||||
writer.WriteEndElement();
|
writer.WriteEndElement();
|
||||||
}
|
}
|
||||||
@ -392,11 +392,11 @@ public class ISerializableHandler : ITypeHandler
|
|||||||
{
|
{
|
||||||
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) => ti.IsISerializable;
|
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) => ti.IsISerializable;
|
||||||
|
|
||||||
public object? ReadXml( XmlSer ser, XmlElement elem, Type expectedType, object? existing )
|
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
|
||||||
{
|
{
|
||||||
// Create/Get instance (needs FormatterServices for ISerializable)
|
// Create/Get instance (needs FormatterServices for ISerializable)
|
||||||
object obj = existing ?? FormatterServices.GetUninitializedObject( expectedType );
|
object obj = existing ?? FormatterServices.GetUninitializedObject( expectedType );
|
||||||
long id = ser.TrackIfGraph( obj, elem ); // Track it
|
long id = xml.TrackIfGraph( obj, elem ); // Track it
|
||||||
|
|
||||||
var serInfo = new SerializationInfo( expectedType, new FormatterConverter() );
|
var serInfo = new SerializationInfo( expectedType, new FormatterConverter() );
|
||||||
|
|
||||||
@ -405,16 +405,16 @@ public class ISerializableHandler : ITypeHandler
|
|||||||
if( objNode is XmlElement childElem )
|
if( objNode is XmlElement childElem )
|
||||||
{
|
{
|
||||||
string childName = childElem.Name;
|
string childName = childElem.Name;
|
||||||
Type? childType = ser._resolver.FindType( childElem.GetAttribute( "_.t" ) );
|
Type? childType = xml._resolver.FindType( childElem.GetAttribute( "_.t" ) );
|
||||||
if( childType != null )
|
if( childType != null )
|
||||||
{
|
{
|
||||||
var desValue = ser.ReadNode( childElem, childType, null );
|
var desValue = xml.ReadNode( childElem, childType, null );
|
||||||
serInfo.AddValue( childName, desValue, childType );
|
serInfo.AddValue( childName, desValue, childType );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var context = new StreamingContext( StreamingContextStates.All ); // Or use ser.Context
|
var context = new StreamingContext( StreamingContextStates.All ); // Or use xml.Context
|
||||||
var cons = expectedType.GetConstructor(
|
var cons = expectedType.GetConstructor(
|
||||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
||||||
null, new[] { typeof( SerializationInfo ), typeof( StreamingContext ) }, null );
|
null, new[] { typeof( SerializationInfo ), typeof( StreamingContext ) }, null );
|
||||||
@ -434,7 +434,7 @@ public class ISerializableHandler : ITypeHandler
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteXml( XmlSer ser, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
{
|
{
|
||||||
if( obj == null )
|
if( obj == null )
|
||||||
{ /* Write null */ return; }
|
{ /* Write null */ return; }
|
||||||
@ -442,9 +442,9 @@ public class ISerializableHandler : ITypeHandler
|
|||||||
{ /* Error */ return; }
|
{ /* Error */ return; }
|
||||||
|
|
||||||
writer.WriteStartElement( name );
|
writer.WriteStartElement( name );
|
||||||
ser.WriteTypeAttr( writer, memberType, obj.GetType() );
|
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
|
||||||
|
|
||||||
if( ser.HandleGraphWrite( writer, obj, out bool first ) )
|
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
|
||||||
{
|
{
|
||||||
if( first )
|
if( first )
|
||||||
{
|
{
|
||||||
@ -454,7 +454,7 @@ public class ISerializableHandler : ITypeHandler
|
|||||||
|
|
||||||
foreach( var member in serInfo )
|
foreach( var member in serInfo )
|
||||||
{
|
{
|
||||||
ser.WriteNode( writer, member.Value, refl.TypeToIdentifier( member.Name ), member.ObjectType, true ); // Force type for ISer
|
xml.WriteNode( writer, member.Value, refl.TypeToIdentifier( member.Name ), member.ObjectType, true ); // Force type for ISer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -468,7 +468,7 @@ public class CollectionHandler : ITypeHandler
|
|||||||
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) =>
|
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) =>
|
||||||
typeof( IEnumerable ).IsAssignableFrom( ti.Type ) && ti.Type != typeof( string );
|
typeof( IEnumerable ).IsAssignableFrom( ti.Type ) && ti.Type != typeof( string );
|
||||||
|
|
||||||
public object? ReadXml( XmlSer ser, XmlElement elem, Type expectedType, object? existing )
|
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
|
||||||
{
|
{
|
||||||
// Determine element type
|
// Determine element type
|
||||||
Type elemType = GetElementType( expectedType );
|
Type elemType = GetElementType( expectedType );
|
||||||
@ -477,14 +477,14 @@ public class CollectionHandler : ITypeHandler
|
|||||||
var listType = typeof( List<> ).MakeGenericType( elemType );
|
var listType = typeof( List<> ).MakeGenericType( elemType );
|
||||||
var list = (IList)Activator.CreateInstance( listType )!;
|
var list = (IList)Activator.CreateInstance( listType )!;
|
||||||
|
|
||||||
ser.TrackIfGraph( list, elem ); // Track list if graph
|
xml.TrackIfGraph( list, elem ); // Track list if graph
|
||||||
|
|
||||||
// Populate the list
|
// Populate the list
|
||||||
foreach( XmlNode node in elem.ChildNodes )
|
foreach( XmlNode node in elem.ChildNodes )
|
||||||
{
|
{
|
||||||
if( node is XmlElement childElem )
|
if( node is XmlElement childElem )
|
||||||
{
|
{
|
||||||
list.Add( ser.ReadNode( childElem, elemType, null ) );
|
list.Add( xml.ReadNode( childElem, elemType, null ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,7 +492,7 @@ public class CollectionHandler : ITypeHandler
|
|||||||
return ConvertToFinalCollection( list, expectedType, elemType );
|
return ConvertToFinalCollection( list, expectedType, elemType );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteXml( XmlSer ser, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
{
|
{
|
||||||
if( obj == null )
|
if( obj == null )
|
||||||
{ /* Write null */ return; }
|
{ /* Write null */ return; }
|
||||||
@ -500,9 +500,9 @@ public class CollectionHandler : ITypeHandler
|
|||||||
{ /* Error */ return; }
|
{ /* Error */ return; }
|
||||||
|
|
||||||
writer.WriteStartElement( name );
|
writer.WriteStartElement( name );
|
||||||
ser.WriteTypeAttr( writer, memberType, obj.GetType() );
|
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
|
||||||
|
|
||||||
if( ser.HandleGraphWrite( writer, obj, out bool first ) )
|
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
|
||||||
{
|
{
|
||||||
if( first )
|
if( first )
|
||||||
{
|
{
|
||||||
@ -510,7 +510,7 @@ public class CollectionHandler : ITypeHandler
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
foreach( var item in collection )
|
foreach( var item in collection )
|
||||||
{
|
{
|
||||||
ser.WriteNode( writer, item, $"i{i++}", elemType, false );
|
xml.WriteNode( writer, item, $"i{i++}", elemType, false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -569,26 +569,26 @@ public class ObjectHandler : ITypeHandler
|
|||||||
{
|
{
|
||||||
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) => true; // Fallback
|
public bool CanHandle( TypeSerializationInfo ti, XmlElement? elem ) => true; // Fallback
|
||||||
|
|
||||||
public object? ReadXml( XmlSer ser, XmlElement elem, Type expectedType, object? existing )
|
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
|
||||||
{
|
{
|
||||||
var actualType = ser._resolver.Resolve( elem, expectedType );
|
var actualType = xml._resolver.Resolve( elem, expectedType );
|
||||||
var ti = ser._meta.Get( actualType );
|
var ti = xml._meta.Get( actualType );
|
||||||
|
|
||||||
// 1. Get/Create Instance
|
// 1. Get/Create Instance
|
||||||
var (obj, _) = GetOrCreateInstance( ser, elem, actualType, existing );
|
var (obj, _) = GetOrCreateInstance( xml, elem, actualType, existing );
|
||||||
if( obj == null )
|
if( obj == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Handle graph refs (if already processed)
|
// Handle graph refs (if already processed)
|
||||||
if( ser._cfg.Structure == Datastructure.Graph && elem.HasAttribute( "ref" ) )
|
if( xml._cfg.Structure == Datastructure.Graph && elem.HasAttribute( "ref" ) )
|
||||||
{
|
{
|
||||||
long id = long.Parse( elem.GetAttribute( "ref" ) );
|
long id = long.Parse( elem.GetAttribute( "ref" ) );
|
||||||
if( ser._processed.TryGetValue( id, out var processedObj ) )
|
if( xml._processed.TryGetValue( id, out var processedObj ) )
|
||||||
return processedObj;
|
return processedObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track if it's new
|
// Track if it's new
|
||||||
ser.TrackIfGraph( obj, elem );
|
xml.TrackIfGraph( obj, elem );
|
||||||
|
|
||||||
// 2. Hydrate
|
// 2. Hydrate
|
||||||
foreach( var memberMeta in ti.Members )
|
foreach( var memberMeta in ti.Members )
|
||||||
@ -602,11 +602,11 @@ public class ObjectHandler : ITypeHandler
|
|||||||
|
|
||||||
if( isAttribute )
|
if( isAttribute )
|
||||||
{
|
{
|
||||||
memberValue = ser._resolver.ConvertSimple( valueSource.Value!, memberMeta.Type );
|
memberValue = xml._resolver.ConvertSimple( valueSource.Value!, memberMeta.Type );
|
||||||
}
|
}
|
||||||
else // Child Element
|
else // Child Element
|
||||||
{
|
{
|
||||||
memberValue = ser.ReadNode( (XmlElement)valueSource, memberMeta.Type, currentMemberValue );
|
memberValue = xml.ReadNode( (XmlElement)valueSource, memberMeta.Type, currentMemberValue );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set value, respecting lib.Do/lib.Dont and pre-hydration
|
// Set value, respecting lib.Do/lib.Dont and pre-hydration
|
||||||
@ -626,16 +626,16 @@ public class ObjectHandler : ITypeHandler
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteXml( XmlSer ser, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
{
|
{
|
||||||
if( obj == null )
|
if( obj == null )
|
||||||
{ /* Write null */ return; }
|
{ /* Write null */ return; }
|
||||||
|
|
||||||
writer.WriteStartElement( name );
|
writer.WriteStartElement( name );
|
||||||
ser.WriteTypeAttr( writer, memberType, obj.GetType() );
|
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
|
||||||
var ti = ser._meta.Get( obj.GetType() );
|
var ti = xml._meta.Get( obj.GetType() );
|
||||||
|
|
||||||
if( ser.HandleGraphWrite( writer, obj, out bool first ) )
|
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
|
||||||
{
|
{
|
||||||
if( first )
|
if( first )
|
||||||
{
|
{
|
||||||
@ -651,7 +651,7 @@ public class ObjectHandler : ITypeHandler
|
|||||||
}
|
}
|
||||||
else // Else, write element
|
else // Else, write element
|
||||||
{
|
{
|
||||||
ser.WriteNode( writer, value, memberMeta.XmlName, memberMeta.Type, false );
|
xml.WriteNode( writer, value, memberMeta.XmlName, memberMeta.Type, false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -697,7 +697,7 @@ public class ObjectHandler : ITypeHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private (object? obj, long id) GetOrCreateInstance( XmlSer ser, XmlElement elem, Type type, object? existing )
|
private (object? obj, long id) GetOrCreateInstance( XmlSer xml, XmlElement elem, Type type, object? existing )
|
||||||
{
|
{
|
||||||
long id = -1;
|
long id = -1;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
@ -705,7 +705,7 @@ public class ObjectHandler : ITypeHandler
|
|||||||
// Check existing
|
// Check existing
|
||||||
if( existing != null && type.IsAssignableFrom( existing.GetType() ) )
|
if( existing != null && type.IsAssignableFrom( existing.GetType() ) )
|
||||||
{
|
{
|
||||||
id = ser._idGen.GetId( existing, out first );
|
id = xml._idGen.GetId( existing, out first );
|
||||||
return (existing, id);
|
return (existing, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,7 +728,7 @@ public class ObjectHandler : ITypeHandler
|
|||||||
return (null, -1);
|
return (null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
id = ser._idGen.GetId( newObj, out first );
|
id = xml._idGen.GetId( newObj, out first );
|
||||||
return (newObj, id);
|
return (newObj, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user