PartialObjectExtractor 2.0.0

dotnet add package PartialObjectExtractor --version 2.0.0
                    
NuGet\Install-Package PartialObjectExtractor -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PartialObjectExtractor" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PartialObjectExtractor" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="PartialObjectExtractor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PartialObjectExtractor --version 2.0.0
                    
#r "nuget: PartialObjectExtractor, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PartialObjectExtractor@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PartialObjectExtractor&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=PartialObjectExtractor&version=2.0.0
                    
Install as a Cake Tool

Partial Object Extractor

A high-performance C# library for extracting partial objects using JSONPath expressions while preserving the original structure. Think GraphQL but with JSONPath syntax.

Overview

This library allows you to query complex object graphs and extract only the data you need, maintaining the hierarchical structure of the original object. Unlike traditional JSONPath implementations that return flat arrays of leaf values, this extractor preserves the object structure, making it ideal for API projections and selective serialization.

Dependency-free: no transitive dependencies to worry about.

High Performance: Uses reflection to traverse objects and extract only requested properties before serialization, avoiding the overhead of serializing entire object graphs. Only the extracted data is converted to JSON, making it significantly faster for large objects when you only need a subset of data.

Usage example in CloudGbxQuery

Installation

dotnet add package PartialObjectExtractor
using PartialObjectExtractor;

var extractor = new PartialExtractor();

Basic Usage

var data = new {
    User = new {
        Name = "John",
        Email = "john@example.com",
        Age = 30
    }
};

var result = extractor.ExtractPaths(data, ["$.User.Name", "$.User.Email"]);
Console.WriteLine(expected.ToString());

Prints:

{
  "User": {
    "Name": "John",
    "Email": "email@example.com"
  }
}

Features

1. Property Access

Access nested properties using dot notation:

extractor.ExtractPaths(data, ["$.Parent.Child1"]);

Case Insensitive: Property names are matched case-insensitively.

extractor.ExtractPaths(data, ["$.parent.child1"]);  // Works!

2. Array Operations

Single Index
extractor.ExtractPaths(data, ["$.Items[0]"]);
Multiple Indices
extractor.ExtractPaths(data, ["$.Items[0,2,4]"]);
Negative Indices
extractor.ExtractPaths(data, ["$.Items[-1]"]);  // Last item
extractor.ExtractPaths(data, ["$.Items[-3,-1]"]);  // Multiple negative indices
Array Slicing
extractor.ExtractPaths(data, ["$.Items[1:3]"]);   // Items at index 1 and 2
extractor.ExtractPaths(data, ["$.Items[:]"]);     // All items
extractor.ExtractPaths(data, ["$.Items[:5]"]);    // First 5 items
extractor.ExtractPaths(data, ["$.Items[5:]"]);    // From index 5 to end
extractor.ExtractPaths(data, ["$.Items[-3:]"]);    // Last 3

3. Wildcard Selection

Array Wildcard
extractor.ExtractPaths(data, ["$.Products[*].Name"]);
// Extracts Name from all products
Object Wildcard
extractor.ExtractPaths(data, ["$.Parent[*]"]);
// Extracts all properties of Parent

4. Multi-Field Selection

Select multiple fields at once using bracket notation:

extractor.ExtractPaths(data, ["$.Products[*]['Name', 'Price']"]);
// Extracts only Name and Price from all products

5. Recursive Descent

Search for properties at any depth in the structure:

extractor.ExtractPaths(data, ["$..Value"]);
// Finds all "Value" properties at any nesting level
Combined Recursive Operations
extractor.ExtractPaths(data, ["$..Level1..Value"]);
// Finds Level1 at any depth, then Value at any depth under it

6. Nested Arrays

Handle deeply nested array structures:

extractor.ExtractPaths(data, ["$.NestedArrays[0][0][1]"]);
extractor.ExtractPaths(data, ["$.NestedArrays[*][0][0]"]);

7. Multiple Paths

Extract multiple paths in a single operation:

var result = extractor.ExtractPaths(data, [
    "$.Items[0]",
    "$.Items[1:3]",
    "$.Products[*].Name",
    "$..Child1",
    "$.Parent[*]"
]);

Overlapping paths are automatically merged into a single coherent structure.

8. Custom Serialization Settings

Respects [JsonProperty] attributes:

public class MyClass {
    [JsonPropertyName("display_name")]
    public string DisplayName { get; set; }
}

extractor.ExtractPaths(obj, ["$.CustomData.display_name"]);

9. Custom Serializer

Example with Newtonsoft.Json:

private class NewtonsoftJsonSerializer(JsonSerializerSettings? settings = null) : IJsonSerializer {
    private readonly JsonSerializer serializer = JsonSerializer.Create(settings ?? new JsonSerializerSettings());

    public JsonNode? Serialize(object? value) {
        if (value is null) {
            return null;
        }

        var jToken = JToken.FromObject(value, serializer);
        return JsonNode.Parse(jToken.ToString(Formatting.None));
    }

    public string GetJsonPropertyName(PropertyInfo property) {
        var jsonProp = property.GetCustomAttribute<JsonPropertyAttribute>();
        if (jsonProp?.PropertyName != null) {
            return jsonProp.PropertyName;
        }

        if (serializer.ContractResolver is CamelCasePropertyNamesContractResolver) {
            return ToCamelCase(property.Name);
        }

        return property.Name;
    }

    private static string ToCamelCase(string str) =>
        string.IsNullOrEmpty(str) || char.IsLower(str[0]) ? str : char.ToLower(str[0]) + str[1..];
}

Usage example:

// Default Newtonsoft.Json settings
var extractor = new PartialExtractor(new NewtonsoftJsonSerializer());

// With custom settings (e.g., camel case)
var settings = new JsonSerializerSettings {
    ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var camelExtractor = new PartialExtractor(new NewtonsoftJsonSerializer(settings));

var result = camelExtractor.ExtractPaths(data, ["$.User.Name"]);

More examples with responses in tests

Supported Path Syntax

Syntax Description Example
.property Dot notation property access $.User.Name
..property Recursive descent $..Name
['<property>' (, '<property>')] Bracket notation property access $['User']['Name'] or $.User['Name','Email']
[<number> (, <number>)] Array index or indices $.Items[0] or $.Items[0,2,4]
[start:end] Array slice $.Items[1:5]
[*] Wildcard (all elements) $.Products[*]

Edge Cases & Error Handling

  • Non-existent properties: Returns empty object
  • Array index out of bounds: Gracefully skipped
  • Invalid paths: Throws ArgumentException
  • Empty collections: Returns the appropriate empty structure
  • Null source: Returns empty JObject

Performance Considerations

  • Reflection-first approach: Uses reflection to traverse the object graph and extract only requested properties
  • Lazy serialization: Only extracted values are serialized to JSON, not the entire source object
  • Efficient for large objects: When you need a small subset of a large object, this is orders of magnitude faster than serialize-then-query approaches
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 222 11/2/2025
1.0.1 187 10/17/2025
1.0.0 200 10/15/2025