Skip to content

Commit 14bb137

Browse files
authored
Merge pull request #58 from IvanMurzak/fix/lazy-reflection-converter
test: enhance LazyReflectionConverterTests with serialization and filtering scenarios
2 parents 12b7fea + 064b2c8 commit 14bb137

4 files changed

Lines changed: 99 additions & 54 deletions

File tree

ReflectorNet.Tests/src/ReflectorTests/LazyReflectionConverterTests.cs

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,28 +166,74 @@ public void Serialize_DelegatesToBackingConverter()
166166
var serialized = reflector.Serialize(obj);
167167

168168
// Assert
169-
Assert.True(mockConverter.WasCalled);
170-
Assert.Equal("{}", serialized.valueJsonElement?.ToString());
169+
Assert.True(mockConverter.MetadataWasAccessed, "Backing converter metadata should be accessed.");
170+
171+
// "Name" should be present as normal serialization happens using the metadata
172+
Assert.NotNull(serialized.props);
173+
Assert.Contains(serialized.props, p => p.name == "Name");
174+
// "Value" should also be present since we are using TestTarget
175+
Assert.Contains(serialized.props, p => p.name == "Value");
176+
}
177+
178+
[Fact]
179+
public void Serialize_DelegatesAndFilters()
180+
{
181+
// Arrange
182+
var typeName = typeof(TestTarget).FullName!;
183+
var mockConverter = new MockConverter(); // Serializes everything normally because it returns all properties
184+
185+
// Should ignore "Secret" even though delegated
186+
var converter = new LazyReflectionConverter(
187+
typeName,
188+
backingConverter: mockConverter,
189+
ignoredProperties: new[] { "Secret" });
190+
191+
var reflector = new Reflector();
192+
reflector.Converters.Add(converter);
193+
194+
var obj = new TestTarget { Name = "Filtered", Secret = "ShouldBeGone" };
195+
196+
// Act
197+
var serialized = reflector.Serialize(obj);
198+
199+
// Assert
200+
Assert.True(mockConverter.MetadataWasAccessed, "Backing converter metadata should be accessed.");
201+
Assert.NotNull(serialized.props);
202+
203+
// "Name" should remain
204+
Assert.Contains(serialized.props, p => p.name == "Name");
205+
206+
// "Secret" should be removed by LazyReflectionConverter filtering logic
207+
Assert.DoesNotContain(serialized.props, p => p.name == "Secret");
208+
}
209+
210+
[Fact]
211+
public void Constructor_BackingConverterWithIgnoredMembers_Succeeds()
212+
{
213+
// This verifies the fix: we no longer throw exception for this combination
214+
var typeName = typeof(TestTarget).FullName!;
215+
var mockConverter = new MockConverter();
216+
217+
var converter = new LazyReflectionConverter(
218+
typeName,
219+
ignoredProperties: new[] { "Test" },
220+
backingConverter: mockConverter);
221+
222+
Assert.NotNull(converter);
171223
}
172224

173225
class MockConverter : GenericReflectionConverter<TestTarget>
174226
{
175-
public bool WasCalled { get; private set; }
227+
public bool MetadataWasAccessed { get; private set; }
176228

177-
protected override SerializedMember InternalSerialize(
229+
protected override IEnumerable<System.Reflection.PropertyInfo>? GetSerializablePropertiesInternal(
178230
Reflector reflector,
179-
object? obj,
180-
Type type,
181-
string? name = null,
182-
bool recursive = true,
231+
Type objType,
183232
System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic,
184-
int depth = 0,
185-
Logs? logs = null,
186-
ILogger? logger = null,
187-
SerializationContext? context = null)
233+
ILogger? logger = null)
188234
{
189-
WasCalled = true;
190-
return SerializedMember.FromJson(type, "{}", name);
235+
MetadataWasAccessed = true;
236+
return base.GetSerializablePropertiesInternal(reflector, objType, flags, logger);
191237
}
192238
}
193239
}

ReflectorNet/ReflectorNet.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<!-- NuGet Package Information -->
1111
<PackageId>com.IvanMurzak.ReflectorNet</PackageId>
12-
<Version>3.8.0</Version>
12+
<Version>3.8.1</Version>
1313
<Authors>Ivan Murzak</Authors>
1414
<Copyright>Copyright © Ivan Murzak 2025</Copyright>
1515
<Description>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.</Description>

ReflectorNet/src/Converter/Reflection/Base/BaseReflectionConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ public virtual int SerializationPriority(Type type, ILogger? logger = null)
218218
ILogger? logger = null)
219219
{
220220
return objType.GetProperties(flags)
221-
.Where(prop => prop.GetCustomAttribute<ObsoleteAttribute>() == null)
222221
.Where(prop => prop.CanRead)
222+
.Where(prop => prop.GetCustomAttribute<ObsoleteAttribute>() == null)
223223
.Where(prop => prop.GetIndexParameters().Length == 0); // Filter out indexer properties
224224
}
225225

ReflectorNet/src/Converter/Reflection/LazyReflectionConverter.cs

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using System;
99
using System.Collections.Generic;
1010
using System.Reflection;
11-
using com.IvanMurzak.ReflectorNet.Model;
1211
using com.IvanMurzak.ReflectorNet.Utils;
1312
using Microsoft.Extensions.Logging;
1413

@@ -55,11 +54,9 @@ public LazyReflectionConverter(
5554
_targetType = new Lazy<Type?>(() => TypeUtils.GetType(_targetTypeName));
5655
}
5756

58-
private Type? GetTargetType() => _targetType.Value;
59-
6057
public override int SerializationPriority(Type type, ILogger? logger = null)
6158
{
62-
var targetType = GetTargetType();
59+
var targetType = _targetType.Value;
6360

6461
// If the target type cannot be resolved, this converter is inactive.
6562
if (targetType == null)
@@ -77,48 +74,50 @@ public override int SerializationPriority(Type type, ILogger? logger = null)
7774
: 0;
7875
}
7976

80-
protected override SerializedMember InternalSerialize(
77+
protected override IEnumerable<PropertyInfo>? GetSerializablePropertiesInternal(
8178
Reflector reflector,
82-
object? obj,
83-
Type type,
84-
string? name = null,
85-
bool recursive = true,
86-
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
87-
int depth = 0,
88-
Logs? logs = null,
89-
ILogger? logger = null,
90-
SerializationContext? context = null)
79+
Type objType,
80+
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
81+
ILogger? logger = null)
9182
{
9283
if (_backingConverter != null)
9384
{
94-
return _backingConverter.Serialize(
95-
reflector: reflector,
96-
obj: obj,
97-
fallbackType: type,
98-
name: name,
99-
recursive: recursive,
100-
flags: flags,
101-
depth: depth,
102-
logs: logs,
103-
logger: logger,
104-
context: context);
85+
return _backingConverter.GetSerializableProperties(reflector, objType, flags, logger);
10586
}
10687

107-
return base.InternalSerialize(
108-
reflector: reflector,
109-
obj: obj,
110-
type: type,
111-
name: name,
112-
recursive: recursive,
113-
flags: flags,
114-
depth: depth,
115-
logs: logs,
116-
logger: logger,
117-
context: context);
88+
return base.GetSerializablePropertiesInternal(reflector, objType, flags, logger);
11889
}
11990

120-
protected override IEnumerable<string> GetIgnoredProperties() => _ignoredProperties;
91+
protected override IEnumerable<FieldInfo>? GetSerializableFieldsInternal(
92+
Reflector reflector,
93+
Type objType,
94+
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
95+
ILogger? logger = null)
96+
{
97+
if (_backingConverter != null)
98+
{
99+
return _backingConverter.GetSerializableFields(reflector, objType, flags, logger);
100+
}
101+
102+
return base.GetSerializableFieldsInternal(reflector, objType, flags, logger);
103+
}
121104

122-
protected override IEnumerable<string> GetIgnoredFields() => _ignoredFields;
105+
protected override IEnumerable<string> GetIgnoredProperties()
106+
{
107+
foreach (var prop in base.GetIgnoredProperties())
108+
yield return prop;
109+
110+
foreach (var prop in _ignoredProperties)
111+
yield return prop;
112+
}
113+
114+
protected override IEnumerable<string> GetIgnoredFields()
115+
{
116+
foreach (var field in base.GetIgnoredFields())
117+
yield return field;
118+
119+
foreach (var field in _ignoredFields)
120+
yield return field;
121+
}
123122
}
124123
}

0 commit comments

Comments
 (0)