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.Text;
|
||||
using System.Threading;
|
||||
@ -95,7 +101,7 @@ namespace db
|
||||
int m_processed = 0;
|
||||
//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.Linq.Expressions;
|
||||
|
||||
namespace serdes;
|
||||
namespace ser;
|
||||
|
||||
#region Attributes & Enums (Mostly unchanged, ensure these exist)
|
||||
|
||||
@ -24,7 +24,7 @@ namespace serdes;
|
||||
public enum Datastructure { Tree, Graph }
|
||||
public enum BackingFieldNaming { Short, Regular }
|
||||
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>
|
||||
{
|
||||
@ -303,8 +303,8 @@ public class TypeResolver
|
||||
public interface ITypeHandler
|
||||
{
|
||||
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 );
|
||||
object? ReadXml( XmlSer ser, XmlElement elem, Type expectedType, object? existing );
|
||||
void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType );
|
||||
object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing );
|
||||
}
|
||||
|
||||
// --- 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 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;
|
||||
if( val == "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 )
|
||||
{
|
||||
@ -330,14 +330,14 @@ public class PrimitiveHandler : ITypeHandler
|
||||
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 )
|
||||
writeElements = tw.WriteState != WriteState.Element;
|
||||
|
||||
if( writeElements )
|
||||
writer.WriteStartElement( name );
|
||||
|
||||
if( forceType || ser._cfg.POD == POD.Elements )
|
||||
if( forceType || xml._cfg.POD == POD.Elements )
|
||||
{
|
||||
if( forceType )
|
||||
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 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 )
|
||||
{
|
||||
log.warn( $"Proxy read failed for {expectedType.Name}. Fallback needed." );
|
||||
return null; // Should fall back or throw
|
||||
}
|
||||
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 )
|
||||
{ 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 )
|
||||
{ log.error( "Proxy write called without proxy def!" ); return; }
|
||||
|
||||
writer.WriteStartElement( name );
|
||||
var proxyStr = ti.ProxyDef.Value.ser( obj );
|
||||
var proxyStr = ti.ProxyDef.Value.fnSer( obj );
|
||||
writer.WriteAttributeString( "proxy", proxyStr );
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
@ -392,11 +392,11 @@ public class ISerializableHandler : ITypeHandler
|
||||
{
|
||||
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)
|
||||
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() );
|
||||
|
||||
@ -405,16 +405,16 @@ public class ISerializableHandler : ITypeHandler
|
||||
if( objNode is XmlElement childElem )
|
||||
{
|
||||
string childName = childElem.Name;
|
||||
Type? childType = ser._resolver.FindType( childElem.GetAttribute( "_.t" ) );
|
||||
Type? childType = xml._resolver.FindType( childElem.GetAttribute( "_.t" ) );
|
||||
if( childType != null )
|
||||
{
|
||||
var desValue = ser.ReadNode( childElem, childType, null );
|
||||
var desValue = xml.ReadNode( childElem, childType, null );
|
||||
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(
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
||||
null, new[] { typeof( SerializationInfo ), typeof( StreamingContext ) }, null );
|
||||
@ -434,7 +434,7 @@ public class ISerializableHandler : ITypeHandler
|
||||
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 )
|
||||
{ /* Write null */ return; }
|
||||
@ -442,9 +442,9 @@ public class ISerializableHandler : ITypeHandler
|
||||
{ /* Error */ return; }
|
||||
|
||||
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 )
|
||||
{
|
||||
@ -454,7 +454,7 @@ public class ISerializableHandler : ITypeHandler
|
||||
|
||||
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 ) =>
|
||||
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
|
||||
Type elemType = GetElementType( expectedType );
|
||||
@ -477,14 +477,14 @@ public class CollectionHandler : ITypeHandler
|
||||
var listType = typeof( List<> ).MakeGenericType( elemType );
|
||||
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
|
||||
foreach( XmlNode node in elem.ChildNodes )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
|
||||
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 )
|
||||
{ /* Write null */ return; }
|
||||
@ -500,9 +500,9 @@ public class CollectionHandler : ITypeHandler
|
||||
{ /* Error */ return; }
|
||||
|
||||
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 )
|
||||
{
|
||||
@ -510,7 +510,7 @@ public class CollectionHandler : ITypeHandler
|
||||
int i = 0;
|
||||
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 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 ti = ser._meta.Get( actualType );
|
||||
var actualType = xml._resolver.Resolve( elem, expectedType );
|
||||
var ti = xml._meta.Get( actualType );
|
||||
|
||||
// 1. Get/Create Instance
|
||||
var (obj, _) = GetOrCreateInstance( ser, elem, actualType, existing );
|
||||
var (obj, _) = GetOrCreateInstance( xml, elem, actualType, existing );
|
||||
if( obj == null )
|
||||
return null;
|
||||
|
||||
// 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" ) );
|
||||
if( ser._processed.TryGetValue( id, out var processedObj ) )
|
||||
if( xml._processed.TryGetValue( id, out var processedObj ) )
|
||||
return processedObj;
|
||||
}
|
||||
|
||||
// Track if it's new
|
||||
ser.TrackIfGraph( obj, elem );
|
||||
xml.TrackIfGraph( obj, elem );
|
||||
|
||||
// 2. Hydrate
|
||||
foreach( var memberMeta in ti.Members )
|
||||
@ -602,11 +602,11 @@ public class ObjectHandler : ITypeHandler
|
||||
|
||||
if( isAttribute )
|
||||
{
|
||||
memberValue = ser._resolver.ConvertSimple( valueSource.Value!, memberMeta.Type );
|
||||
memberValue = xml._resolver.ConvertSimple( valueSource.Value!, memberMeta.Type );
|
||||
}
|
||||
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
|
||||
@ -626,16 +626,16 @@ public class ObjectHandler : ITypeHandler
|
||||
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 )
|
||||
{ /* Write null */ return; }
|
||||
|
||||
writer.WriteStartElement( name );
|
||||
ser.WriteTypeAttr( writer, memberType, obj.GetType() );
|
||||
var ti = ser._meta.Get( obj.GetType() );
|
||||
xml.WriteTypeAttr( writer, memberType, 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 )
|
||||
{
|
||||
@ -651,7 +651,7 @@ public class ObjectHandler : ITypeHandler
|
||||
}
|
||||
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;
|
||||
bool first = true;
|
||||
@ -705,7 +705,7 @@ public class ObjectHandler : ITypeHandler
|
||||
// Check existing
|
||||
if( existing != null && type.IsAssignableFrom( existing.GetType() ) )
|
||||
{
|
||||
id = ser._idGen.GetId( existing, out first );
|
||||
id = xml._idGen.GetId( existing, out first );
|
||||
return (existing, id);
|
||||
}
|
||||
|
||||
@ -728,7 +728,7 @@ public class ObjectHandler : ITypeHandler
|
||||
return (null, -1);
|
||||
}
|
||||
|
||||
id = ser._idGen.GetId( newObj, out first );
|
||||
id = xml._idGen.GetId( newObj, out first );
|
||||
return (newObj, id);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user