diff --git a/ReflectorNet.Tests/src/ReflectorTests/LazyReflectionConverterTests.cs b/ReflectorNet.Tests/src/ReflectorTests/LazyReflectionConverterTests.cs index 1aaf5275..b2510929 100644 --- a/ReflectorNet.Tests/src/ReflectorTests/LazyReflectionConverterTests.cs +++ b/ReflectorNet.Tests/src/ReflectorTests/LazyReflectionConverterTests.cs @@ -41,7 +41,7 @@ public void SerializationPriority_TypeExists_ReturnsHighPriority() { // Arrange var typeName = typeof(TestTarget).FullName!; - var converter = new LazyReflectionConverter(typeName); + var converter = new LazyGenericReflectionConverter(typeName); // Act var priority = converter.SerializationPriority(typeof(TestTarget)); @@ -56,7 +56,7 @@ public void SerializationPriority_TypeDoesNotExist_ReturnsZero() { // Arrange var typeName = "System.NonExistentType.ShouldNotExist"; - var converter = new LazyReflectionConverter(typeName); + var converter = new LazyGenericReflectionConverter(typeName); // Act var priority = converter.SerializationPriority(typeof(TestTarget)); @@ -71,7 +71,7 @@ public void SerializationPriority_DerivedType_ReturnsPositivePriority() { // Arrange var typeName = typeof(TestTarget).FullName!; - var converter = new LazyReflectionConverter(typeName); + var converter = new LazyGenericReflectionConverter(typeName); // Act var exactMatchPriority = converter.SerializationPriority(typeof(TestTarget)); @@ -90,7 +90,7 @@ public void Serialize_IgnoresConfiguredProperties() // Arrange var typeName = typeof(TestTarget).FullName!; var ignoredProps = new[] { "Secret" }; - var converter = new LazyReflectionConverter(typeName, ignoredProperties: ignoredProps); + var converter = new LazyGenericReflectionConverter(typeName, ignoredProperties: ignoredProps); var reflector = new Reflector(); // Register manually to ensure it's used @@ -119,7 +119,7 @@ public void Serialize_IgnoresConfiguredFields() // Arrange var typeName = typeof(TestTargetWithFields).FullName!; var ignoredFields = new[] { "SecretField" }; - var converter = new LazyReflectionConverter(typeName, ignoredFields: ignoredFields); + var converter = new LazyGenericReflectionConverter(typeName, ignoredFields: ignoredFields); var reflector = new Reflector(); // Register manually to ensure it's used @@ -145,9 +145,9 @@ public void Serialize_IgnoresConfiguredFields() [Fact] public void Constructor_NullOrEmptyTypeName_ThrowsArgumentException() { - Assert.Throws(() => new LazyReflectionConverter(null!)); - Assert.Throws(() => new LazyReflectionConverter("")); - Assert.Throws(() => new LazyReflectionConverter(" ")); + Assert.Throws(() => new LazyGenericReflectionConverter(null!)); + Assert.Throws(() => new LazyGenericReflectionConverter("")); + Assert.Throws(() => new LazyGenericReflectionConverter(" ")); } [Fact] @@ -156,7 +156,7 @@ public void Serialize_DelegatesToBackingConverter() // Arrange var typeName = typeof(TestTarget).FullName!; var mockConverter = new MockConverter(); - var converter = new LazyReflectionConverter(typeName, backingConverter: mockConverter); + var converter = new LazyGenericReflectionConverter(typeName, backingConverter: mockConverter); var reflector = new Reflector(); reflector.Converters.Add(converter); @@ -183,7 +183,7 @@ public void Serialize_DelegatesAndFilters() var mockConverter = new MockConverter(); // Serializes everything normally because it returns all properties // Should ignore "Secret" even though delegated - var converter = new LazyReflectionConverter( + var converter = new LazyGenericReflectionConverter( typeName, backingConverter: mockConverter, ignoredProperties: new[] { "Secret" }); @@ -210,16 +210,16 @@ public void Serialize_DelegatesAndFilters() [Fact] public void Constructor_BackingConverterWithIgnoredMembers_Succeeds() { - // This verifies the fix: we no longer throw exception for this combination - var typeName = typeof(TestTarget).FullName!; - var mockConverter = new MockConverter(); + // This verifies the fix: we no longer throw exception for this combination + var typeName = typeof(TestTarget).FullName!; + var mockConverter = new MockConverter(); - var converter = new LazyReflectionConverter( - typeName, - ignoredProperties: new[] { "Test" }, - backingConverter: mockConverter); + var converter = new LazyGenericReflectionConverter( + typeName, + ignoredProperties: new[] { "Test" }, + backingConverter: mockConverter); - Assert.NotNull(converter); + Assert.NotNull(converter); } class MockConverter : GenericReflectionConverter diff --git a/ReflectorNet/ReflectorNet.csproj b/ReflectorNet/ReflectorNet.csproj index b50f87cf..05e35f69 100644 --- a/ReflectorNet/ReflectorNet.csproj +++ b/ReflectorNet/ReflectorNet.csproj @@ -9,7 +9,7 @@ com.IvanMurzak.ReflectorNet - 3.8.1 + 3.9.0 Ivan Murzak Copyright © Ivan Murzak 2025 ReflectorNet is an advanced .NET reflection toolkit designed for AI-driven scenarios. Effortlessly search for C# methods using natural language queries, invoke any method by supplying arguments as JSON, and receive results as JSON. The library also provides a powerful API to inspect, modify, and manage in-memory object instances dynamically via JSON data. Ideal for automation, testing, and AI integration workflows. diff --git a/ReflectorNet/src/Converter/IReflectionConverter.cs b/ReflectorNet/src/Converter/IReflectionConverter.cs index c1f38df0..0a02586e 100644 --- a/ReflectorNet/src/Converter/IReflectionConverter.cs +++ b/ReflectorNet/src/Converter/IReflectionConverter.cs @@ -15,8 +15,34 @@ namespace com.IvanMurzak.ReflectorNet.Converter { public interface IReflectionConverter { + /// + /// Gets a value indicating whether this converter supports direct value setting operations. + /// When true, the converter can handle primitive-style value assignments. + /// When false, the converter only supports field and property-based population. + /// + bool AllowSetValue { get; } + + /// + /// Gets a value indicating whether this converter supports cascading serialization operations. + /// When true, nested objects and collections are recursively serialized. + /// When false, only shallow serialization is performed. + /// bool AllowCascadeSerialization { get; } + /// + /// Gets a value indicating whether this converter should recursively convert field values. + /// When true, field values that are complex objects are serialized recursively. + /// When false, field values are serialized as simple JSON representations. + /// + bool AllowCascadeFieldsConversion { get; } + + /// + /// Gets a value indicating whether this converter should recursively convert property values. + /// When true, property values that are complex objects are serialized recursively. + /// When false, property values are serialized as simple JSON representations. + /// + bool AllowCascadePropertiesConversion { get; } + int SerializationPriority(Type type, ILogger? logger = null); object? Deserialize( diff --git a/ReflectorNet/src/Converter/Reflection/LazyReflectionConverter.cs b/ReflectorNet/src/Converter/Reflection/LazyGenericReflectionConverter.cs similarity index 67% rename from ReflectorNet/src/Converter/Reflection/LazyReflectionConverter.cs rename to ReflectorNet/src/Converter/Reflection/LazyGenericReflectionConverter.cs index fa4a3fdb..43f66d55 100644 --- a/ReflectorNet/src/Converter/Reflection/LazyReflectionConverter.cs +++ b/ReflectorNet/src/Converter/Reflection/LazyGenericReflectionConverter.cs @@ -18,7 +18,36 @@ namespace com.IvanMurzak.ReflectorNet.Converter /// This is useful for optional dependencies where the target type might not be present at runtime. /// If the type is not found, this converter will remain inactive (priority 0). /// - public class LazyReflectionConverter : GenericReflectionConverter + public class LazyGenericReflectionConverter : LazyGenericReflectionConverter + { + /// + /// Initializes a new instance of the class. + /// + /// The full name of the type to handle. + /// Optional list of property names to ignore during serialization. + /// Optional list of field names to ignore during serialization. + /// Optional converter to delegate serialization to. + public LazyGenericReflectionConverter( + string targetTypeName, + IEnumerable? ignoredProperties = null, + IEnumerable? ignoredFields = null, + IReflectionConverter? backingConverter = null) + : base( + targetTypeName: targetTypeName, + ignoredProperties: ignoredProperties, + ignoredFields: ignoredFields, + backingConverter: backingConverter) + { + // empty + } + } + + /// + /// A reflection converter that resolves its target type lazily by name. + /// This is useful for optional dependencies where the target type might not be present at runtime. + /// If the type is not found, this converter will remain inactive (priority 0). + /// + public class LazyGenericReflectionConverter : GenericReflectionConverter { private readonly string _targetTypeName; private readonly HashSet _ignoredProperties; @@ -26,14 +55,19 @@ public class LazyReflectionConverter : GenericReflectionConverter private readonly IReflectionConverter? _backingConverter; private readonly Lazy _targetType; + public override bool AllowSetValue => _backingConverter?.AllowSetValue ?? base.AllowSetValue; + public override bool AllowCascadeSerialization => _backingConverter?.AllowCascadeSerialization ?? base.AllowCascadeSerialization; + public override bool AllowCascadeFieldsConversion => _backingConverter?.AllowCascadeFieldsConversion ?? base.AllowCascadeFieldsConversion; + public override bool AllowCascadePropertiesConversion => _backingConverter?.AllowCascadePropertiesConversion ?? base.AllowCascadePropertiesConversion; + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The full name of the type to handle. /// Optional list of property names to ignore during serialization. /// Optional list of field names to ignore during serialization. /// Optional converter to delegate serialization to. - public LazyReflectionConverter( + public LazyGenericReflectionConverter( string targetTypeName, IEnumerable? ignoredProperties = null, IEnumerable? ignoredFields = null,