Ser updates

This commit is contained in:
Marc Hernandez 2025-08-17 10:57:44 -07:00
parent 3f850cc9b0
commit 4563db15a6
4 changed files with 304 additions and 51 deletions

View File

@ -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
View 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
View 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
}

View File

@ -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);
} }
} }