Split out XmlSer stuff. Fix behaviour
This commit is contained in:
parent
baa65531a2
commit
b41748d0f0
@ -80,8 +80,6 @@ dotnet_analyzer_diagnostic.severity = none
|
|||||||
# .NET Style Rules
|
# .NET Style Rules
|
||||||
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#net-style-rules
|
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#net-style-rules
|
||||||
[*.{cs,csx,cake,vb,vbx}]
|
[*.{cs,csx,cake,vb,vbx}]
|
||||||
indent_style = tab
|
|
||||||
|
|
||||||
# "this." and "Me." qualifiers
|
# "this." and "Me." qualifiers
|
||||||
dotnet_style_qualification_for_field = false:none
|
dotnet_style_qualification_for_field = false:none
|
||||||
dotnet_style_qualification_for_property = false:none
|
dotnet_style_qualification_for_property = false:none
|
||||||
|
|||||||
@ -24,7 +24,7 @@ public record CodeGenConfig : imm.Recorded<CodeGenConfig>
|
|||||||
public ImmutableDictionary<string, ImmutableList<string>> WLFields { 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
|
// Default member types to process
|
||||||
public lib.Types TypesDefault { get; init; } = lib.Types.Fields | lib.Types.Props;
|
public ser.Types TypesDefault { get; init; } = ser.Types.Fields | ser.Types.Props;
|
||||||
|
|
||||||
// How to handle backing fields (might be less relevant for code gen)
|
// How to handle backing fields (might be less relevant for code gen)
|
||||||
public BackingFieldNaming Naming { get; init; } = BackingFieldNaming.Regular;
|
public BackingFieldNaming Naming { get; init; } = BackingFieldNaming.Regular;
|
||||||
@ -62,9 +62,9 @@ public class TypeStructureAnalyzer
|
|||||||
private TypeStructureInfo BuildTypeInfo(Type type)
|
private TypeStructureInfo BuildTypeInfo(Type type)
|
||||||
{
|
{
|
||||||
var members = new List<GenMemberMeta>();
|
var members = new List<GenMemberMeta>();
|
||||||
var typesTodo = type.GetCustomAttribute<lib.Ser>(true)?.Types ?? _cfg.TypesDefault;
|
var typesTodo = type.GetCustomAttribute<ser.Ser>(true)?.Types ?? _cfg.TypesDefault;
|
||||||
bool doFields = typesTodo.HasFlag(lib.Types.Fields);
|
bool doFields = typesTodo.HasFlag(ser.Types.Fields);
|
||||||
bool doProps = typesTodo.HasFlag(lib.Types.Props);
|
bool doProps = typesTodo.HasFlag(ser.Types.Props);
|
||||||
|
|
||||||
// Track processed names to avoid duplicates (e.g., field + prop)
|
// Track processed names to avoid duplicates (e.g., field + prop)
|
||||||
var processedNames = new HashSet<string>();
|
var processedNames = new HashSet<string>();
|
||||||
@ -156,8 +156,8 @@ public class TypeStructureAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
actualMi.GetCustomAttribute<lib.Do>() != null,
|
actualMi.GetCustomAttribute<ser.Do>() != null,
|
||||||
actualMi.GetCustomAttribute<lib.Dont>() != null,
|
actualMi.GetCustomAttribute<ser.Dont>() != null,
|
||||||
propName
|
propName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -39,60 +39,6 @@ using System.Net.Sockets;
|
|||||||
namespace lib
|
namespace lib
|
||||||
{
|
{
|
||||||
|
|
||||||
public interface I_Serialize
|
|
||||||
{
|
|
||||||
void OnSerialize() {}
|
|
||||||
object OnDeserialize( object enclosing ) => this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum Types
|
|
||||||
{
|
|
||||||
Fields = 0b_0001,
|
|
||||||
Props = 0b_0010,
|
|
||||||
Implied = 0b_0100,
|
|
||||||
Explicit = 0b_1000,
|
|
||||||
|
|
||||||
|
|
||||||
None = 0b_0000,
|
|
||||||
Default = Fields,
|
|
||||||
All = Fields | Props,
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Ser : Attribute
|
|
||||||
{
|
|
||||||
public Types Types { get; set; } = Types.Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Do : Attribute
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Dont : Attribute
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChildAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string[] Values { get; private set; }
|
|
||||||
|
|
||||||
public ChildAttribute( params string[] values )
|
|
||||||
{
|
|
||||||
this.Values = values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChildFieldsAttribute : ChildAttribute
|
|
||||||
{
|
|
||||||
public ChildFieldsAttribute( params string[] values ) : base( values ) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChildPropsAttribute : ChildAttribute
|
|
||||||
{
|
|
||||||
public ChildPropsAttribute( params string[] values ) : base( values ) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Datastructure
|
public enum Datastructure
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
@ -143,7 +89,7 @@ namespace lib
|
|||||||
public BackingFieldNaming Naming = BackingFieldNaming.Short;
|
public BackingFieldNaming Naming = BackingFieldNaming.Short;
|
||||||
public POD POD = POD.Attributes;
|
public POD POD = POD.Attributes;
|
||||||
|
|
||||||
public Types TypesDefault = Types.Fields;
|
public ser.Types TypesDefault = ser.Types.Fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class XmlFormatter2 : IFormatter
|
public class XmlFormatter2 : IFormatter
|
||||||
@ -328,7 +274,7 @@ namespace lib
|
|||||||
{
|
{
|
||||||
object obj = DeserializeObject( elem, mi, type, existing );
|
object obj = DeserializeObject( elem, mi, type, existing );
|
||||||
|
|
||||||
if( obj is I_Serialize iser )
|
if( obj is ser.I_Serialize iser )
|
||||||
{
|
{
|
||||||
if( _cfg.VerboseLogging ) log.info( $"" );
|
if( _cfg.VerboseLogging ) log.info( $"" );
|
||||||
obj = iser.OnDeserialize( null );
|
obj = iser.OnDeserialize( null );
|
||||||
@ -561,8 +507,8 @@ namespace lib
|
|||||||
|
|
||||||
string name = childFi.Name;
|
string name = childFi.Name;
|
||||||
|
|
||||||
var dontAtt = childFi.GetCustomAttributes<lib.Dont>();
|
var dontAtt = childFi.GetCustomAttributes<ser.Dont>();
|
||||||
var doAtt = childFi.GetCustomAttributes<lib.Do>();
|
var doAtt = childFi.GetCustomAttributes<ser.Do>();
|
||||||
|
|
||||||
string propName = "";
|
string propName = "";
|
||||||
|
|
||||||
@ -574,9 +520,9 @@ namespace lib
|
|||||||
|
|
||||||
var propInfo = narrowType.GetProperty( propName );
|
var propInfo = narrowType.GetProperty( propName );
|
||||||
|
|
||||||
dontAtt = propInfo.GetCustomAttributes<lib.Dont>();
|
dontAtt = propInfo.GetCustomAttributes<ser.Dont>();
|
||||||
|
|
||||||
doAtt = propInfo.GetCustomAttributes<lib.Do>();
|
doAtt = propInfo.GetCustomAttributes<ser.Do>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( dontAtt.Any() )
|
if( dontAtt.Any() )
|
||||||
@ -639,13 +585,13 @@ namespace lib
|
|||||||
foreach( var childPi in props )
|
foreach( var childPi in props )
|
||||||
{
|
{
|
||||||
string name = childPi.Name;
|
string name = childPi.Name;
|
||||||
var dontAtt = childPi.GetCustomAttributes<lib.Dont>();
|
var dontAtt = childPi.GetCustomAttributes<ser.Dont>();
|
||||||
if( dontAtt.Any() )
|
if( dontAtt.Any() )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var doAtt = childPi.GetCustomAttributes<lib.Do>();
|
var doAtt = childPi.GetCustomAttributes<ser.Do>();
|
||||||
|
|
||||||
|
|
||||||
name = refl.TypeToIdentifier( name );
|
name = refl.TypeToIdentifier( name );
|
||||||
@ -713,7 +659,7 @@ namespace lib
|
|||||||
{
|
{
|
||||||
if( doImpls )
|
if( doImpls )
|
||||||
{
|
{
|
||||||
if( mi.GetCustomAttribute<ChildAttribute>( true ) == null )
|
if( mi.GetCustomAttribute<ser.ChildAttribute>( true ) == null )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1353,8 +1299,8 @@ namespace lib
|
|||||||
foreach( var childFi in fields )
|
foreach( var childFi in fields )
|
||||||
{
|
{
|
||||||
string name = childFi.Name;
|
string name = childFi.Name;
|
||||||
var dontAtt = childFi.GetCustomAttributes<lib.Dont>();
|
var dontAtt = childFi.GetCustomAttributes<ser.Dont>();
|
||||||
var doAtt = childFi.GetCustomAttributes<lib.Do>();
|
var doAtt = childFi.GetCustomAttributes<ser.Do>();
|
||||||
|
|
||||||
string propName = "";
|
string propName = "";
|
||||||
if( name.StartsWith( "<" ) && name.EndsWith( "BackingField" ) )
|
if( name.StartsWith( "<" ) && name.EndsWith( "BackingField" ) )
|
||||||
@ -1365,8 +1311,8 @@ namespace lib
|
|||||||
|
|
||||||
var propInfo = narrowType.GetProperty( propName );
|
var propInfo = narrowType.GetProperty( propName );
|
||||||
|
|
||||||
dontAtt = propInfo.GetCustomAttributes<lib.Dont>();
|
dontAtt = propInfo.GetCustomAttributes<ser.Dont>();
|
||||||
doAtt = propInfo.GetCustomAttributes<lib.Do>();
|
doAtt = propInfo.GetCustomAttributes<ser.Do>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1411,7 +1357,7 @@ namespace lib
|
|||||||
{
|
{
|
||||||
string name = childPi.Name;
|
string name = childPi.Name;
|
||||||
|
|
||||||
var dontAtt = childPi.GetCustomAttributes<lib.Dont>();
|
var dontAtt = childPi.GetCustomAttributes<ser.Dont>();
|
||||||
if( dontAtt.Any() )
|
if( dontAtt.Any() )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -1441,19 +1387,19 @@ namespace lib
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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( ser.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>( true );
|
var custWLFields = mi?.GetCustomAttribute<ser.ChildFieldsAttribute>( true );
|
||||||
var custWLProps = mi?.GetCustomAttribute<ChildPropsAttribute>( true );
|
var custWLProps = mi?.GetCustomAttribute<ser.ChildPropsAttribute>( true );
|
||||||
|
|
||||||
filterFields = custWLFields != null;
|
filterFields = custWLFields != null;
|
||||||
filterProps = custWLProps != null;
|
filterProps = custWLProps != null;
|
||||||
|
|
||||||
var typesTodo = type.GetCustomAttribute<Ser>( true )?.Types ?? TypesDefault;
|
var typesTodo = type.GetCustomAttribute<ser.Ser>( true )?.Types ?? TypesDefault;
|
||||||
|
|
||||||
doImpls = typesTodo.HasFlag( Types.Implied );
|
doImpls = typesTodo.HasFlag( ser.Types.Implied );
|
||||||
doFields = filterFields || typesTodo.HasFlag( Types.Fields );
|
doFields = filterFields || typesTodo.HasFlag( ser.Types.Fields );
|
||||||
doProps = filterProps || typesTodo.HasFlag( Types.Props );
|
doProps = filterProps || typesTodo.HasFlag( ser.Types.Props );
|
||||||
whitelistFields = new( custWLFields?.Values ?? new string[0] );
|
whitelistFields = new( custWLFields?.Values ?? new string[0] );
|
||||||
whitelistProps = new( custWLProps?.Values ?? new string[0] );
|
whitelistProps = new( custWLProps?.Values ?? new string[0] );
|
||||||
}
|
}
|
||||||
863
ser/XmlSer.cs
863
ser/XmlSer.cs
File diff suppressed because it is too large
Load Diff
159
ser/XmlSer_Core.cs
Normal file
159
ser/XmlSer_Core.cs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace ser;
|
||||||
|
|
||||||
|
|
||||||
|
// --- Primitive Handler ---
|
||||||
|
public partial class PrimitiveHandler : ser.ITypeHandler
|
||||||
|
{
|
||||||
|
public bool CanHandle( TypeInfo ti, XmlElement? elem )
|
||||||
|
{
|
||||||
|
var typeCode = Type.GetTypeCode( ti.Type );
|
||||||
|
var typeNotObject = Type.GetTypeCode( ti.Type ) != TypeCode.Object;
|
||||||
|
var isString = ti.Type == typeof( string );
|
||||||
|
return typeNotObject | isString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Proxy Handler ---
|
||||||
|
public partial class ProxyHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public bool CanHandle( TypeInfo ti, XmlElement? elem ) => ti.IsProxy || ( elem?.HasAttribute( "proxy" ) ?? false );
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ISerializable Handler ---
|
||||||
|
public partial class ISerializableHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public bool CanHandle( TypeInfo ti, XmlElement? elem ) => ti.IsISerializable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Collection Handler ---
|
||||||
|
public partial class CollectionHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public bool CanHandle( TypeInfo ti, XmlElement? elem ) =>
|
||||||
|
typeof( IEnumerable ).IsAssignableFrom( ti.Type );
|
||||||
|
|
||||||
|
private Type GetElementType( Type collectionType )
|
||||||
|
{
|
||||||
|
if( collectionType.IsArray )
|
||||||
|
return collectionType.GetElementType()!;
|
||||||
|
if( collectionType.IsGenericType )
|
||||||
|
{
|
||||||
|
var args = collectionType.GetGenericArguments();
|
||||||
|
if( args.Length == 1 )
|
||||||
|
return args[0];
|
||||||
|
if( args.Length == 2 )
|
||||||
|
return typeof( KeyValuePair<,> ).MakeGenericType( args );
|
||||||
|
}
|
||||||
|
return typeof( object ); // Fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
private object ConvertToFinalCollection( IList list, Type expectedType, Type elemType )
|
||||||
|
{
|
||||||
|
if( expectedType.IsArray )
|
||||||
|
{
|
||||||
|
var arr = Array.CreateInstance( elemType, list.Count );
|
||||||
|
list.CopyTo( arr, 0 );
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
if( expectedType.IsGenericType )
|
||||||
|
{
|
||||||
|
var genDef = expectedType.GetGenericTypeDefinition();
|
||||||
|
if( genDef == typeof( ImmutableArray<> ) )
|
||||||
|
{
|
||||||
|
var method = typeof( ImmutableArray ).GetMethods()
|
||||||
|
.First( m => m.Name == "ToImmutableArray" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) )
|
||||||
|
.MakeGenericMethod( elemType );
|
||||||
|
return method.Invoke( null, new object[] { list } )!;
|
||||||
|
}
|
||||||
|
if( genDef == typeof( ImmutableList<> ) )
|
||||||
|
{
|
||||||
|
var method = typeof( ImmutableList ).GetMethods()
|
||||||
|
.First( m => m.Name == "ToImmutableList" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) )
|
||||||
|
.MakeGenericMethod( elemType );
|
||||||
|
return method.Invoke( null, new object[] { list } )!;
|
||||||
|
}
|
||||||
|
// Add more immutable/dictionary handlers here (using MakeImmutableDictionary etc.)
|
||||||
|
}
|
||||||
|
return list; // Default to List<T> if no specific match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Object Handler (Default/Complex) ---
|
||||||
|
public partial class ObjectHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public bool CanHandle( TypeInfo ti, XmlElement? elem ) => true; // Fallback
|
||||||
|
|
||||||
|
private (XmlNode? source, bool isAttribute) FindValueSource( XmlElement parent, string name )
|
||||||
|
{
|
||||||
|
if( parent.HasAttribute( name ) )
|
||||||
|
{
|
||||||
|
return (parent.Attributes[name], true);
|
||||||
|
}
|
||||||
|
foreach( XmlNode node in parent.ChildNodes )
|
||||||
|
{
|
||||||
|
if( node.NodeType == XmlNodeType.Element && node.Name == name )
|
||||||
|
{
|
||||||
|
return (node, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldSetValue( MemberMeta member, bool isHydrating )
|
||||||
|
{
|
||||||
|
// [Dont] members are filtered out during metadata generation.
|
||||||
|
// If a member is present in the metadata and a value is found in the XML,
|
||||||
|
// we should always set it. This handles both new creation and hydration/merge.
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private (object? obj, long id) GetOrCreateInstance( XmlSer xml, XmlElement elem, Type type, object? existing )
|
||||||
|
{
|
||||||
|
long id = -1;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
// Check existing
|
||||||
|
if( existing != null && type.IsAssignableFrom( existing.GetType() ) )
|
||||||
|
{
|
||||||
|
id = xml._idGen.GetId( existing, out first );
|
||||||
|
return (existing, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new
|
||||||
|
object? newObj = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if( type.GetConstructor( Type.EmptyTypes ) != null )
|
||||||
|
{
|
||||||
|
newObj = Activator.CreateInstance( type );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newObj = FormatterServices.GetUninitializedObject( type );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
log.error( $"Failed to create instance of {type.Name}: {ex.Message}" );
|
||||||
|
return (null, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
id = xml._idGen.GetId( newObj, out first );
|
||||||
|
return (newObj, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
188
ser/XmlSer_Read.cs
Normal file
188
ser/XmlSer_Read.cs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace ser;
|
||||||
|
|
||||||
|
|
||||||
|
// --- Primitive Handler ---
|
||||||
|
public partial class PrimitiveHandler : ser.ITypeHandler
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
// So this is an interesting one. Why not use the expected type? Well, we know we have
|
||||||
|
// data in the XML, it just wont convert to what we want.
|
||||||
|
return xml._resolver.ConvertSimple( val, expectedType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Proxy Handler ---
|
||||||
|
public partial class ProxyHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
|
||||||
|
{
|
||||||
|
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.fnDes( expectedType.FullName, proxyVal );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ISerializable Handler ---
|
||||||
|
public partial class ISerializableHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
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 = xml.TrackIfGraph( obj, elem ); // Track it
|
||||||
|
|
||||||
|
var serInfo = new SerializationInfo( expectedType, new FormatterConverter() );
|
||||||
|
|
||||||
|
foreach( XmlNode objNode in elem.ChildNodes )
|
||||||
|
{
|
||||||
|
if( objNode is XmlElement childElem )
|
||||||
|
{
|
||||||
|
string childName = childElem.Name;
|
||||||
|
Type? childType = xml._resolver.FindType( childElem.GetAttribute( "_.t" ) );
|
||||||
|
if( childType != null )
|
||||||
|
{
|
||||||
|
var desValue = xml.ReadNode( childElem, childType, null );
|
||||||
|
serInfo.AddValue( childName, desValue, childType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
if( cons != null )
|
||||||
|
{
|
||||||
|
cons.Invoke( obj, new object[] { serInfo, context } );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.error( $"ISerializable type {expectedType.Name} lacks the required constructor." );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( obj is IDeserializationCallback cb )
|
||||||
|
cb.OnDeserialization( obj );
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Collection Handler ---
|
||||||
|
public partial class CollectionHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
|
||||||
|
{
|
||||||
|
// Determine element type
|
||||||
|
Type elemType = GetElementType( expectedType );
|
||||||
|
|
||||||
|
// Create a temporary list
|
||||||
|
var listType = typeof( List<> ).MakeGenericType( elemType );
|
||||||
|
var list = (IList)Activator.CreateInstance( listType )!;
|
||||||
|
|
||||||
|
xml.TrackIfGraph( list, elem ); // Track list if graph
|
||||||
|
|
||||||
|
// Populate the list
|
||||||
|
foreach( XmlNode node in elem.ChildNodes )
|
||||||
|
{
|
||||||
|
if( node is XmlElement childElem )
|
||||||
|
{
|
||||||
|
list.Add( xml.ReadNode( childElem, elemType, null ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to the final expected type (Array, Immutable*, List)
|
||||||
|
return ConvertToFinalCollection( list, expectedType, elemType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Object Handler (Default/Complex) ---
|
||||||
|
public partial class ObjectHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing )
|
||||||
|
{
|
||||||
|
var actualType = xml._resolver.Resolve( elem, expectedType );
|
||||||
|
var ti = xml._meta.Get( actualType );
|
||||||
|
|
||||||
|
// 1. Get/Create Instance
|
||||||
|
var (obj, _) = GetOrCreateInstance( xml, elem, actualType, existing );
|
||||||
|
if( obj == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Handle graph refs (if already processed)
|
||||||
|
if( xml._cfg.Structure == Datastructure.Graph && elem.HasAttribute( "ref" ) )
|
||||||
|
{
|
||||||
|
long id = long.Parse( elem.GetAttribute( "ref" ) );
|
||||||
|
if( xml._processed.TryGetValue( id, out var processedObj ) )
|
||||||
|
return processedObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track if it's new
|
||||||
|
xml.TrackIfGraph( obj, elem );
|
||||||
|
|
||||||
|
// 2. Hydrate
|
||||||
|
foreach( var memberMeta in ti.Members )
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
var (valueSource, isAttribute) = FindValueSource( elem, memberMeta.XmlName );
|
||||||
|
|
||||||
|
if( valueSource != null )
|
||||||
|
{
|
||||||
|
object? memberValue;
|
||||||
|
object? currentMemberValue = memberMeta.GetValue( obj );
|
||||||
|
|
||||||
|
if( isAttribute )
|
||||||
|
{
|
||||||
|
memberValue = xml._resolver.ConvertSimple( valueSource.Value!, memberMeta.Type );
|
||||||
|
}
|
||||||
|
else // Child Element
|
||||||
|
{
|
||||||
|
memberValue = xml.ReadNode( (XmlElement)valueSource, memberMeta.Type, currentMemberValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set value, respecting ser.Do/ser.Dont and pre-hydration
|
||||||
|
if( ShouldSetValue( memberMeta, existing != null ) )
|
||||||
|
{
|
||||||
|
memberMeta.SetValue( obj, memberValue );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Post-processing
|
||||||
|
if( obj is ser.I_Serialize iSer )
|
||||||
|
obj = iSer.OnDeserialize( null );
|
||||||
|
if( ti.IsImm && obj is imm.Obj immObj )
|
||||||
|
return immObj.Record( $"From XML {elem.Name}" );
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
133
ser/XmlSer_Tests.cs
Normal file
133
ser/XmlSer_Tests.cs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
//using Org.BouncyCastle.Crypto.IO;
|
||||||
|
|
||||||
|
namespace ser;
|
||||||
|
|
||||||
|
|
||||||
|
public record class SimpleImmutable( string Name, int Age ) : imm.Timed<SimpleImmutable>;
|
||||||
|
|
||||||
|
static public class Test
|
||||||
|
{
|
||||||
|
|
||||||
|
public class ExternalClass
|
||||||
|
{
|
||||||
|
public string ActualProperty { get; set; } = "test_ActualProperty_set_inline";
|
||||||
|
//public string ActualProperty_NotSerialized { get; set; } = "ActualProperty_NotSerialized";
|
||||||
|
}
|
||||||
|
|
||||||
|
[ser.Ser( Types = ser.Types.Implied )]
|
||||||
|
public partial class LeaveWithExternalClasss
|
||||||
|
{
|
||||||
|
|
||||||
|
//[ser.Do]
|
||||||
|
//public bool _cccwp_doBool = true;
|
||||||
|
|
||||||
|
[ser.ChildPropsAttribute( "ActualProperty" )]
|
||||||
|
public ExternalClass _leaf_external = new();
|
||||||
|
|
||||||
|
//public string _cccwp_doNotSerialize = "test_do_not_serialize";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[ser.Ser]
|
||||||
|
public class TrunkClass
|
||||||
|
{
|
||||||
|
public LeaveWithExternalClasss _trunk_leaf = new();
|
||||||
|
//public int _chf_test = 10;
|
||||||
|
//private string _chf_priv_string = "test_priv_string";
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void Serialization()
|
||||||
|
{
|
||||||
|
ser.XmlCfg cfg = new()
|
||||||
|
{
|
||||||
|
Verbose = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
ser.TypeMetaCache metaCache = new( cfg );
|
||||||
|
//metaCache.AddType( typeof( ClassContainsClassWithProp ), "ActualProperty" );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TrunkClass trunk = new()
|
||||||
|
{
|
||||||
|
_trunk_leaf = new()
|
||||||
|
{
|
||||||
|
_leaf_external = new()
|
||||||
|
{
|
||||||
|
ActualProperty = "ActualProperty_set_in_cons"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "ActualProperty_set_in_cons" );
|
||||||
|
|
||||||
|
|
||||||
|
var memStream = new MemoryStream();
|
||||||
|
|
||||||
|
{
|
||||||
|
var xml = new ser.XmlSer( cfg, metaCache );
|
||||||
|
xml.Serialize( memStream, trunk );
|
||||||
|
}
|
||||||
|
|
||||||
|
memStream.Position = 0;
|
||||||
|
|
||||||
|
var strXml = System.Text.Encoding.UTF8.GetString( memStream.ToArray() );
|
||||||
|
|
||||||
|
var badXml = "<root>\n <prop doBool=\"True\" />\n</root>";
|
||||||
|
///*
|
||||||
|
var badXml_02 = @"""<root>
|
||||||
|
<prop doBool=""True"">
|
||||||
|
<propHolder>
|
||||||
|
<ActualProperty ActualProperty=""ActualProperty_set_in_cons"" />
|
||||||
|
<ActualProperty_NotSerialized ActualProperty_NotSerialized=""ActualProperty_NotSerialized"" />
|
||||||
|
</propHolder>
|
||||||
|
<doNotSerialize doNotSerialize=""test_do_not_serialize"" />
|
||||||
|
</prop>
|
||||||
|
</root>""";
|
||||||
|
//*/
|
||||||
|
Debug.Assert( strXml != badXml );
|
||||||
|
|
||||||
|
memStream.Position = 0;
|
||||||
|
|
||||||
|
var classHasFields2 = new TrunkClass();
|
||||||
|
//classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized = "ActualProperty_NotSerialized_set_in_test_01";
|
||||||
|
|
||||||
|
Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" );
|
||||||
|
//Debug.Assert( classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized" );
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
var xml = new ser.XmlSer(cfg, metaCache);
|
||||||
|
classHasFields2 = xml.Deserialize<TrunkClass>( memStream );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" );
|
||||||
|
//Debug.Assert( classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" );
|
||||||
|
|
||||||
|
memStream.Position = 0;
|
||||||
|
|
||||||
|
var classHasFields3 = new TrunkClass();
|
||||||
|
|
||||||
|
{
|
||||||
|
var xml = new ser.XmlSer(cfg, metaCache);
|
||||||
|
xml.DeserializeInto( memStream, classHasFields3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" );
|
||||||
|
//Debug.Assert( classHasFields3._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
193
ser/XmlSer_Write.cs
Normal file
193
ser/XmlSer_Write.cs
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace ser;
|
||||||
|
|
||||||
|
|
||||||
|
// --- Primitive Handler ---
|
||||||
|
public partial class PrimitiveHandler : ser.ITypeHandler
|
||||||
|
{
|
||||||
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
|
{
|
||||||
|
if( obj == null )
|
||||||
|
{
|
||||||
|
writer.WriteStartElement( name );
|
||||||
|
writer.WriteAttributeString( "v", "null" );
|
||||||
|
writer.WriteEndElement();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 || xml._cfg.POD == POD.Elements )
|
||||||
|
{
|
||||||
|
if( forceType )
|
||||||
|
writer.WriteAttributeString( "_.t", obj.GetType().FullName );
|
||||||
|
|
||||||
|
writer.WriteAttributeString( "v", obj.ToString() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteAttributeString( name, obj.ToString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( writeElements )
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Proxy Handler ---
|
||||||
|
public partial class ProxyHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
|
{
|
||||||
|
if( obj == null )
|
||||||
|
{ xml.GetHandler( typeof( object ) ).WriteXml( xml, writer, null, name, memberType, forceType ); return; }
|
||||||
|
|
||||||
|
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.fnSer( obj );
|
||||||
|
|
||||||
|
// TODO: Allow arbitrary writing here
|
||||||
|
writer.WriteAttributeString( "proxy", proxyStr );
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ISerializable Handler ---
|
||||||
|
public partial class ISerializableHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
|
{
|
||||||
|
if( obj == null )
|
||||||
|
{ /* Write null */ return; }
|
||||||
|
if( !( obj is ISerializable serObj ) )
|
||||||
|
{ /* Error */ return; }
|
||||||
|
|
||||||
|
writer.WriteStartElement( name );
|
||||||
|
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
|
||||||
|
|
||||||
|
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
|
||||||
|
{
|
||||||
|
if( first )
|
||||||
|
{
|
||||||
|
var serInfo = new SerializationInfo( obj.GetType(), new FormatterConverter() );
|
||||||
|
var context = new StreamingContext( StreamingContextStates.All );
|
||||||
|
serObj.GetObjectData( serInfo, context );
|
||||||
|
|
||||||
|
foreach( var member in serInfo )
|
||||||
|
{
|
||||||
|
xml.WriteNode( writer, member.Value, refl.TypeToIdentifier( member.Name ), member.ObjectType, true ); // Force type for ISer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Collection Handler ---
|
||||||
|
public partial class CollectionHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
|
{
|
||||||
|
if( obj == null )
|
||||||
|
{ /* Write null */ return; }
|
||||||
|
if( !( obj is IEnumerable collection ) )
|
||||||
|
{ /* Error */ return; }
|
||||||
|
|
||||||
|
writer.WriteStartElement( name );
|
||||||
|
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
|
||||||
|
|
||||||
|
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
|
||||||
|
{
|
||||||
|
if( first )
|
||||||
|
{
|
||||||
|
Type elemType = GetElementType( obj.GetType() );
|
||||||
|
int i = 0;
|
||||||
|
foreach( var item in collection )
|
||||||
|
{
|
||||||
|
xml.WriteNode( writer, item, $"i{i++}", elemType, false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Object Handler (Default/Complex) ---
|
||||||
|
public partial class ObjectHandler : ITypeHandler
|
||||||
|
{
|
||||||
|
public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType )
|
||||||
|
{
|
||||||
|
if( obj == null )
|
||||||
|
{ /* Write null */ return; }
|
||||||
|
|
||||||
|
writer.WriteStartElement( name );
|
||||||
|
xml.WriteTypeAttr( writer, memberType, obj.GetType() );
|
||||||
|
var ti = xml._meta.Get( obj.GetType() );
|
||||||
|
|
||||||
|
if( xml.HandleGraphWrite( writer, obj, out bool first ) )
|
||||||
|
{
|
||||||
|
if( first )
|
||||||
|
{
|
||||||
|
foreach( var memberMeta in ti.Members )
|
||||||
|
{
|
||||||
|
var value = memberMeta.GetValue( obj );
|
||||||
|
if( value != null )
|
||||||
|
{
|
||||||
|
// If POD-Attribute, write attribute
|
||||||
|
if( memberMeta.IsPodAttribute )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
writer.WriteAttributeString( memberMeta.XmlName, value.ToString() );
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
log.error( $"Writing Att {memberMeta.XmlName} = [{value}]" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
xml.WriteNode( writer, value, memberMeta.XmlName, memberMeta.Type, false );
|
||||||
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
log.error( $"Writing Node {memberMeta.XmlName} = [{value}]" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
125
tests/Tests.cs
Normal file
125
tests/Tests.cs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace test;
|
||||||
|
|
||||||
|
|
||||||
|
public record class SimpleImmutable( string Name, int Age ) : imm.Timed<SimpleImmutable>;
|
||||||
|
|
||||||
|
static public class XmlFormatter2
|
||||||
|
{
|
||||||
|
|
||||||
|
public class ClassWithProperties
|
||||||
|
{
|
||||||
|
public string ActualProperty { get; set; } = "test_ActualProperty_set_inline";
|
||||||
|
public string ActualProperty_NotSerialized { get; set; } = "ActualProperty_NotSerialized";
|
||||||
|
}
|
||||||
|
|
||||||
|
[ser.Ser( Types = ser.Types.Implied )]
|
||||||
|
public partial class ClassContainsClassWithProp
|
||||||
|
{
|
||||||
|
|
||||||
|
[ser.Do]
|
||||||
|
public bool doBool = true;
|
||||||
|
|
||||||
|
[ser.ChildPropsAttribute( "ActualProperty" )]
|
||||||
|
public ClassWithProperties propHolder = new();
|
||||||
|
|
||||||
|
public string doNotSerialize = "test_do_not_serialize";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[ser.Ser]
|
||||||
|
public class ClassHasFields
|
||||||
|
{
|
||||||
|
public ClassContainsClassWithProp prop = new();
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void Serialization()
|
||||||
|
{
|
||||||
|
|
||||||
|
lib.XmlFormatter2Cfg cfg = new()
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ClassHasFields classHasFields = new()
|
||||||
|
{
|
||||||
|
prop = new()
|
||||||
|
{
|
||||||
|
propHolder = new()
|
||||||
|
{
|
||||||
|
ActualProperty = "ActualProperty_set_in_cons"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Debug.Assert( classHasFields.prop.propHolder.ActualProperty == "ActualProperty_set_in_cons" );
|
||||||
|
|
||||||
|
|
||||||
|
var memStream = new MemoryStream();
|
||||||
|
|
||||||
|
{
|
||||||
|
var xml = new lib.XmlFormatter2 ( cfg );
|
||||||
|
xml.Serialize( memStream, classHasFields );
|
||||||
|
}
|
||||||
|
|
||||||
|
memStream.Position = 0;
|
||||||
|
|
||||||
|
var strXml = System.Text.Encoding.UTF8.GetString( memStream.ToArray() );
|
||||||
|
|
||||||
|
var badXml = "<root>\n <prop doBool=\"True\" />\n</root>";
|
||||||
|
/*
|
||||||
|
<root _.t="test.XmlFormatter2+ClassHasFields" _.version.="2">
|
||||||
|
<prop doBool="True">
|
||||||
|
<propHolder ActualProperty="ActualProperty_set_in_cons" ActualProperty_NotSerialized="ActualProperty_NotSerialized" ActualProperty="ActualProperty_set_in_cons" />
|
||||||
|
</prop>
|
||||||
|
</root>*/
|
||||||
|
Debug.Assert( strXml != badXml );
|
||||||
|
|
||||||
|
memStream.Position = 0;
|
||||||
|
|
||||||
|
var classHasFields2 = new ClassHasFields();
|
||||||
|
classHasFields2.prop.propHolder.ActualProperty_NotSerialized = "ActualProperty_NotSerialized_set_in_test_01";
|
||||||
|
|
||||||
|
Debug.Assert( classHasFields2.prop.propHolder.ActualProperty == "test_ActualProperty_set_inline" );
|
||||||
|
Debug.Assert( classHasFields2.prop.propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized" );
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
var xml = new lib.XmlFormatter2 ( cfg );
|
||||||
|
classHasFields2 = xml.Deserialize<ClassHasFields>( memStream );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Debug.Assert( classHasFields2.prop.propHolder.ActualProperty == "ActualProperty_set_in_cons" );
|
||||||
|
Debug.Assert( classHasFields2.prop.propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" );
|
||||||
|
|
||||||
|
memStream.Position = 0;
|
||||||
|
|
||||||
|
var classHasFields3 = new ClassHasFields();
|
||||||
|
|
||||||
|
{
|
||||||
|
var xml = new lib.XmlFormatter2 ( cfg );
|
||||||
|
xml.DeserializeInto( memStream, classHasFields3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert( classHasFields3.prop.propHolder.ActualProperty == "ActualProperty_set_in_cons" );
|
||||||
|
Debug.Assert( classHasFields3.prop.propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" );
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user