JSONPath in C#
.NET has no JSONPath of its own. In practice JSONPath in C# means Newtonsoft.Json: parse the JSON into a JToken and call SelectTokens. The code under the tool is exactly that. Two details that trip people up:
SelectToken(singular) returns the only match ornull, and throwsJsonException: Path returned multiple tokens.when there are more. UseSelectTokensfor wildcards,..and filters.- Missing properties are skipped, not reported. Pass
errorWhenNoMatch: trueto get aJsonExceptioninstead (tick the box above to see the message).
JSONPath syntax
| Syntax | Meaning |
|---|---|
| $ | The root. Newtonsoft also accepts a path without it: store.book. |
| .name, ['name'] | A property. Use brackets and single quotes for names with dots, spaces or quotes: $['a.b']. |
| ['a','b'] | Several properties, in the order written. |
| [0], [0,2] | Array elements by index. No negative indexes: [-1] throws. |
| [start:end:step] | A slice: [-2:] is the last two, [::-1] is reversed. A step of 0 throws. |
| *, [*] | .* is every property value of an object; [*] is every element of an array (and nothing on an object). |
| ..name, ..* | Recursive descent: that property at any depth, or every value below. |
| [?(expr)] | A filter over the children. @ is the current child, $ the root. |
| == != <> < <= > >= | Comparisons. Strings in single quotes; numbers, true, false, null. |
| === !== | Strict comparisons: the JSON types must match (1 === 1.0 is still true). |
| =~ /regex/i | A .NET regular expression with the options i, m, s and x. |
| &&, || | And, or. There are no parentheses inside a filter, and a mix nests in the order written: a && b || c means a && (b || c). |
| [?(@.name)] | Existence: the child has that property. |
Newtonsoft's JSONPath is not RFC 9535
JSONPath became a standard in 2024 (RFC 9535), years after Newtonsoft implemented it. Paths copied from another tool or written from the RFC can behave differently in .NET:
- Negative indexes:
$[-1]is the last element in RFC 9535 and throwsArgumentOutOfRangeExceptionin Newtonsoft. Use$[-1:]. - Wildcards and filters on objects: RFC 9535 applies
[*]and[?()]to an object's member values. Newtonsoft's[*]returns nothing on an object, and its filter tests the object's properties (JPropertytokens), so$.store[?(@.price)]usually matches nothing. - Comparisons convert types: a string on the left is converted to the right side's type, so
'10' > 9is true and'abc' > 9throwsFormatException.nullsorts below everything, so@.price < 10matches anullprice. Numbers are equal within a relative 2.2e-16, so0.1 + 0.2style values compare equal to0.3. - No functions:
length(),count(),match(),search()andvalue()are RFC 9535 only. Use=~for patterns. - Only single quotes inside brackets:
$["name"]fails,$['name']works. - Dates:
JToken.Parsereads ISO 8601 strings asDateTime, so@.start > '2026-01-01'compares dates, and the JSON comes back in ISO format (an offset becomes your local time).
System.Text.Json has no JSONPath
JsonNode and JsonDocument have no query method. Walk the nodes yourself, or keep System.Text.Json for your models and use Newtonsoft only for the query:
using System.Text.Json.Nodes;
using Newtonsoft.Json.Linq;
var json = """{ "store": { "book": [ { "title": "Moby Dick", "price": 8.99 }, { "title": "Sword of Honour", "price": 12.99 } ] } }""";
// 1. System.Text.Json: walk the nodes yourself (no wildcards, filters or ..).
JsonNode doc = JsonNode.Parse(json)!;
foreach (JsonNode? book in doc["store"]!["book"]!.AsArray())
{
if (book!["price"]!.GetValue<decimal>() < 10)
Console.WriteLine(book["title"]!.GetValue<string>()); // Moby Dick
}
// 2. Keep System.Text.Json for the model and use Newtonsoft only for the query.
JToken root = JToken.Parse(doc.ToJsonString());
foreach (JToken title in root.SelectTokens("$.store.book[?(@.price < 10)].title"))
Console.WriteLine(title); // Moby Dick
For standard RFC 9535 behaviour on JsonNode, third-party libraries such as JsonPath.Net implement it; their results differ from Newtonsoft's in the ways listed above.
FAQ
How do I use JSONPath in C#?
Install Newtonsoft.Json, parse with JToken.Parse(json) and call SelectTokens("$.path"). Each result is a JToken: token.Path is where it is, token.ToString() is its JSON, and token.Value<decimal>() converts a value.
Does System.Text.Json support JSONPath?
No. Walk the JsonNode tree in code, or use Newtonsoft's SelectTokens for the query as shown above.
Why does $.items[-1] throw?
Newtonsoft has no negative indexes, and a negative index on an array reaches List<T>'s indexer, which throws ArgumentOutOfRangeException. $.items[-1:] returns the last element.
How do I filter by a property value?
$.orders[?(@.status == 'shipped')]. Strings take single quotes; combine conditions with && and ||. The filter goes on the array, and @ is each element.
What does errorWhenNoMatch do?
SelectTokens(path, errorWhenNoMatch: true) throws a JsonException such as Property 'x' does not exist on JObject. when a step finds nothing, instead of returning fewer results. With a list of names (['a','b']) it always throws after the first one, a Newtonsoft bug the tester reproduces.
Is my JSON uploaded?
No. The JSONPath engine runs in your browser, so nothing is sent anywhere.