You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add atomic modification API and JSON patch support with enhancements (#71)
* Add atomic path-based modification API and JSON patch support
- Introduced `TryModifyAt` method for direct modification of fields, array elements, and dictionary entries without constructing full objects.
- Implemented path parsing logic to navigate to specific members using a string-based path format.
- Added `TryPatch` method to apply JSON Merge Patch documents, allowing multiple fields to be modified in a single call.
- Created new partial files: `Reflector.ModifyAt.cs` for atomic modifications and `Reflector.Patch.cs` for JSON patching.
- Enhanced error handling with detailed messages for navigation failures.
- Added tests to verify the functionality of the new modification methods and JSON patching capabilities.
* refactor: remove unused System.Collections.Generic namespace import
* test: add unit tests for TryPatch method and enhance error logging in Reflector
* feat: add View and TryReadAt methods for enhanced object serialization and navigation
* refactor: replace HashSet<int> with HashSet<object> for circular reference detection
* feat: add View method with TypeFilter and enhance ViewQuery descriptions
* feat: add View and Grep methods for enhanced read-side navigation and filtering
* feat: implement TryLookupMember and TryLookupBracketedElement methods for enhanced member resolution
* feat: enhance View and Grep methods with additional test cases for null inputs and invalid patterns
* Add extensive ReflectorNet unit tests
Add numerous unit tests to AtomicModifyTests.cs and ViewTests.cs covering edge cases for TryModifyAt, TryPatch, TryReadAt, Grep and View behaviors. New tests exercise null-intermediate paths, negative indices, dictionary key add/type-conversion, bracket notation misuse, read-only properties, mixed dictionary/array nesting, invalid JSON patches, deep array-based patches, null-at-root patches, hash-prefixed paths, circular-reference handling, property discovery, max-depth pruning, empty-path handling, List<T> element matching, and maxDepth=0 behavior. Also add small test helper types (ReadOnlyPropertyContainer, DictOfArraysContainer, ListContainer, CircularNode) and include logging assertions to verify diagnostic messages.
Copy file name to clipboardExpand all lines: README.md
+238-2Lines changed: 238 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,6 +21,9 @@ Traditional reflection is brittle and requires exact matches. ReflectorNet is bu
21
21
***🔍 Fuzzy Matching**: Discover methods and types even with incomplete names or parameters (configurable match levels 0-6).
22
22
***📦 Type-Safe Serialization**: Preserves full type information, supporting complex nested objects, collections, and custom types.
23
23
***🔄 In-Place Modification**: Update existing object instances from serialized data without breaking references.
24
+
***🎯 Atomic Path-Based Modification**: Navigate directly to any field, array element, or dictionary entry by path and modify only that — without touching anything else.
25
+
***🩹 JSON Patch**: Apply a JSON document to modify multiple fields at different depths in a single call, following JSON Merge Patch (RFC 7396) semantics.
26
+
***🔎 View & Grep**: Read exactly what you need — navigate to a subtree, filter by name pattern or type, or grep the entire live object graph for all fields matching a regex. The read-side counterpart to path-based modification.
24
27
***📄 JSON Schema Generation**: Automatically generate schemas for your types and methods to feed into LLM context windows.
25
28
26
29
## 📦 Installation
@@ -79,7 +82,240 @@ var existingInstance = new MyComplexClass();
// "Segment 'doesNotExist' not found on type 'SolarSystem'.
140
+
// Available fields: globalOrbitSpeedMultiplier, celestialBodies, ..."
141
+
```
142
+
143
+
### 6. JSON Patch
144
+
145
+
Apply a JSON document to modify multiple fields at different depths in a single call.
146
+
Follows **JSON Merge Patch** (RFC 7396) semantics, extended with bracket-notation keys for arrays and dictionaries.
147
+
148
+
```csharp
149
+
varreflector=newReflector();
150
+
object?system=newSolarSystem { /* ... */ };
151
+
152
+
// Modify several fields at once — untouched fields are preserved
153
+
varlogs=newLogs();
154
+
boolok=reflector.TryPatch(refsystem, """
155
+
{
156
+
"globalOrbitSpeedMultiplier": 5.0,
157
+
"globalSizeMultiplier": 2.0,
158
+
"celestialBodies": {
159
+
"[0]": { "orbitRadius": 42.0 }
160
+
}
161
+
}
162
+
""", logs: logs);
163
+
```
164
+
165
+
**Patch document rules:**
166
+
167
+
* A JSON **object** key navigates into that field (`"fieldName"`) or element (`"[i]"` / `"[key]"`)
168
+
* A JSON **non-object** value sets the field directly
169
+
*`null` sets the field to `null`
170
+
*`"$type"` key inside a JSON object specifies a desired subtype — the existing instance is replaced with a fresh instance of the new type before applying the remaining keys
171
+
172
+
```csharp
173
+
// Replace a base-type field with a derived type and set its fields
174
+
reflector.TryPatch(refsystem, """
175
+
{
176
+
"star": {
177
+
"$type": "MyNamespace.NeutronStar",
178
+
"mass": 2.5
179
+
}
180
+
}
181
+
""");
182
+
```
183
+
184
+
A `JsonElement` overload is also available when you already have a parsed document:
Read exactly the data you need from a live object — navigate to a specific subtree, filter by name or type, or grep the entire graph for every field matching a pattern.
194
+
This is the read-side counterpart to [Atomic Path-Based Modification](#5-atomic-path-based-modification).
195
+
196
+
---
197
+
198
+
#### `reflector.View` — filtered serialization
199
+
200
+
Returns a `SerializedMember` tree with optional path navigation and post-filters applied.
When a filter produces no matches the **root envelope is still returned** with an empty fields collection so that `result.typeName` always identifies the navigated node's type.
246
+
247
+
**`ViewQuery` options:**
248
+
249
+
| Option | Type | Default | Description |
250
+
| --- | --- | --- | --- |
251
+
|`Path`|`string?`|`null`| Navigate to this path before serializing (same format as `TryModifyAt`) |
252
+
|`MaxDepth`|`int?`|`null`| Maximum depth of returned tree (`0` = root node only, no children) |
253
+
|`NamePattern`|`string?`|`null`| .NET regex matched against field / property names (case-insensitive) |
254
+
|`TypeFilter`|`Type?`|`null`| Keep only branches whose resolved runtime type is assignable to this type |
// logs: "Segment 'doesNotExist' not found on type 'SolarSystem'.
281
+
// Available fields: globalOrbitSpeedMultiplier, celestialBodies, ..."
282
+
```
283
+
284
+
---
285
+
286
+
#### `reflector.Grep` — grep the live object graph
287
+
288
+
Walks the entire live object graph and returns a flat list of every field / property whose name matches the given regex pattern — like the `grep` command, but for in-RAM objects.
289
+
290
+
```csharp
291
+
// Find every field whose name starts with "orbit"
*`Path` — full slash-delimited path, e.g. `"celestialBodies/[0]/orbitRadius"`
312
+
*`Value` — `SerializedMember` of the matched field, ready for `GetValue<T>(reflector)`
313
+
314
+
> **Grep vs. View + NamePattern**: `Grep` walks the **live object graph** and can find fields inside array elements. `View` + `NamePattern` filters the `SerializedMember` tree after serialization; array element contents are stored as JSON and are not individually filterable by name. Use `Grep` when you need to search inside arrays.
315
+
316
+
---
317
+
318
+
### 8. Dynamic Method Invocation
83
319
84
320
Allow AI to find and call methods without knowing the exact signature.
85
321
@@ -110,7 +346,7 @@ string result = reflector.MethodCall(
0 commit comments