Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 109 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Traditional reflection is brittle and requires exact matches. ReflectorNet is bu
* **🔍 Fuzzy Matching**: Discover methods and types even with incomplete names or parameters (configurable match levels 0-6).
* **📦 Type-Safe Serialization**: Preserves full type information, supporting complex nested objects, collections, and custom types.
* **🔄 In-Place Modification**: Update existing object instances from serialized data without breaking references.
* **🎯 Atomic Path-Based Modification**: Navigate directly to any field, array element, or dictionary entry by path and modify only that — without touching anything else.
* **🩹 JSON Patch**: Apply a JSON document to modify multiple fields at different depths in a single call, following JSON Merge Patch (RFC 7396) semantics.
* **📄 JSON Schema Generation**: Automatically generate schemas for your types and methods to feed into LLM context windows.

## 📦 Installation
Expand Down Expand Up @@ -79,7 +81,113 @@ var existingInstance = new MyComplexClass();
bool success = reflector.TryModify(ref existingInstance, serialized);
```

### 5. Dynamic Method Invocation
### 5. Atomic Path-Based Modification

Navigate directly to a specific field, array element, or dictionary entry by path and modify **only that target** — no surrounding data is affected.

**Path format:**

| Segment | Meaning |
| --- | --- |
| `fieldName` | Field or property by name |
| `[i]` | Array / list element at index `i` |
| `[key]` | Dictionary entry with key `key` (any key type) |

A leading `#/` is stripped automatically for compatibility with `SerializationContext` paths.

```csharp
var reflector = new Reflector();
object? system = new SolarSystem
{
globalOrbitSpeedMultiplier = 1f,
celestialBodies = new[]
{
new CelestialBody { orbitRadius = 10f, orbitSpeed = 1f },
new CelestialBody { orbitRadius = 20f, orbitSpeed = 2f },
}
};

// Modify a root field
reflector.TryModifyAt<float>(ref system, "globalOrbitSpeedMultiplier", 5f);

// Modify a nested field — only that one field changes
reflector.TryModifyAt<float>(ref system, "celestialBodies/[0]/orbitRadius", 999f);

// Dictionary entry — string or integer keys both work
object? container = new Config { settings = new Dictionary<string, int> { ["timeout"] = 10 } };
reflector.TryModifyAt<int>(ref container, "settings/[timeout]", 60);
```

For partial updates of a complex object at a path, supply a `SerializedMember` that lists only the fields to change:

```csharp
var patch = new SerializedMember { typeName = typeof(CelestialBody).GetTypeId() };
patch.SetFieldValue(reflector, "orbitRadius", 777f); // only orbitRadius changes

var logs = new Logs();
reflector.TryModifyAt(ref system, "celestialBodies/[1]", patch, logs: logs);
// celestialBodies[1].orbitSpeed is untouched
```

Errors are collected in the `Logs` object — nothing is thrown:

```csharp
var logs = new Logs();
bool ok = reflector.TryModifyAt<float>(ref system, "doesNotExist", 5f, logs: logs);
// ok == false; logs contains:
// "Segment 'doesNotExist' not found on type 'SolarSystem'.
// Available fields: globalOrbitSpeedMultiplier, celestialBodies, ..."
```

### 6. JSON Patch

Apply a JSON document to modify multiple fields at different depths in a single call.
Follows **JSON Merge Patch** (RFC 7396) semantics, extended with bracket-notation keys for arrays and dictionaries.

```csharp
var reflector = new Reflector();
object? system = new SolarSystem { /* ... */ };

// Modify several fields at once — untouched fields are preserved
var logs = new Logs();
bool ok = reflector.TryPatch(ref system, """
{
"globalOrbitSpeedMultiplier": 5.0,
"globalSizeMultiplier": 2.0,
"celestialBodies": {
"[0]": { "orbitRadius": 42.0 }
}
}
""", logs: logs);
```

**Patch document rules:**

* A JSON **object** key navigates into that field (`"fieldName"`) or element (`"[i]"` / `"[key]"`)
* A JSON **non-object** value sets the field directly
* `null` sets the field to `null`
* `"$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

```csharp
// Replace a base-type field with a derived type and set its fields
reflector.TryPatch(ref system, """
{
"star": {
"$type": "MyNamespace.NeutronStar",
"mass": 2.5
}
}
""");
```

A `JsonElement` overload is also available when you already have a parsed document:

```csharp
using var doc = JsonDocument.Parse(@"{ ""globalOrbitSpeedMultiplier"": 9.0 }");
reflector.TryPatch(ref system, doc.RootElement, logs: logs);
```

### 7. Dynamic Method Invocation

Allow AI to find and call methods without knowing the exact signature.

Expand Down
Loading