StjTokenKit 1.0.0
dotnet add package StjTokenKit --version 1.0.0
NuGet\Install-Package StjTokenKit -Version 1.0.0
<PackageReference Include="StjTokenKit" Version="1.0.0" />
<PackageVersion Include="StjTokenKit" Version="1.0.0" />
<PackageReference Include="StjTokenKit" />
paket add StjTokenKit --version 1.0.0
#r "nuget: StjTokenKit, 1.0.0"
#:package StjTokenKit@1.0.0
#addin nuget:?package=StjTokenKit&version=1.0.0
#tool nuget:?package=StjTokenKit&version=1.0.0
StjTokenKit
Predictable, typed helpers for working with System.Text.Json, Newtonsoft.Json, and CLR object graphs / dynamic values.
Both libraries are capable, but everyday DOM access still gets noisy fast: chained property lookups, awkward nested reads, and unclear behavior around missing values, null, and wrong types. StjTokenKit keeps the raw DOM model but gives it a cleaner, more explicit read surface.
Scope
Version 1 is intentionally small and read-focused across all supported access models:
- simple path selection
- typed primitive reads
- serializer-backed object reads
- explicit missing/null/wrong-type behavior
Out of scope:
- mutation helpers
- full JSONPath support
- schema validation
- document transforms
Path syntax
Supported path grammar:
- dotted property access:
user.name - array indexes:
items[0].id - root arrays:
[1].id - quoted property names:
user['full.name'],["key with spaces"].id - empty path: selects the current element
Quoted property names support both single (') and double (") quotes and allow keys that contain dots or other characters that conflict with the path grammar.
Rules:
- case-sensitive
- negative indexes are invalid
- malformed paths throw
ArgumentException - missing properties and out-of-range indexes are treated as missing
Behavior contract
Every access API treats these states consistently:
- Missing: the path does not exist
- Null: the path exists and the value is
null - Wrong type: the path exists but cannot be read as the requested type
- Valid value: the read succeeds
API families:
TryGet...returnsfalsefor missing, null, and wrong typeGetRequired...throws for missing, null, and wrong typeGet...OrDefaultreturns the supplied default for missing and null, and throws for wrong typeTrySelectexposes traversal without conversionSelectRequiredexposes traversal with path-aware exceptions
Examples
System.Text.Json
using System.Text.Json;
using StjTokenKit;
using var document = JsonDocument.Parse(
"""
{
"user": {
"name": "Ada",
"age": 42,
"id": "f9c265be-f4a2-4140-ad95-9ed57cd6ef09"
},
"items": [
{ "id": 1 },
{ "id": 2 }
]
}
""");
var root = document.RootElement;
var name = root.GetRequiredString("user.name");
var age = root.GetInt32OrDefault("user.age", 0);
var ok = root.TryGetGuid("user.id", out var id);
var itemId = document.GetRequiredInt32("items[1].id");
Newtonsoft.Json
using Newtonsoft.Json.Linq;
using StjTokenKit;
var root = JToken.Parse(
"""
{
"user": {
"name": "Ada",
"age": 42,
"id": "f9c265be-f4a2-4140-ad95-9ed57cd6ef09"
},
"items": [
{ "id": 1 },
{ "id": 2 }
]
}
""");
var name = root.GetRequiredString("user.name");
var age = root.GetInt32OrDefault("user.age", 0);
var ok = root.TryGetGuid("user.id", out var id);
var itemId = root.GetRequiredInt32("items[1].id");
CLR objects and dynamic
using System.Dynamic;
using StjTokenKit;
dynamic root = new ExpandoObject();
root.User = new ExpandoObject();
root.User.DisplayName = "Ada";
root.Items = new[]
{
new { Id = 1 },
new { Id = 2 }
};
var value = DynamicValue.From(root, ignoreCase: true);
var name = value.GetRequiredString("user.displayname");
var itemId = value.GetRequiredInt32("items[1].id");
Selection helpers
if (root.TrySelect("items[0]", out var firstItem))
{
Console.WriteLine(firstItem.GetRawText());
}
var user = root.SelectRequired("user");
var exists = root.Exists("user.name");
var isNull = root.IsNull("user.middle");
// DOM-specific kind
var kind = root.GetValueKind("user.name"); // JsonValueKind (System.Text.Json only)
// DOM-agnostic kind — works on JsonElement, JToken, and DynamicValueAccessor
var unifiedKind = root.GetKind("user.name"); // StjValueKind
StjValueKind is a unified enum with values Object, Array, String, Number, Boolean, Null, and Missing. It lets you inspect value types without importing DOM-specific enums.
Primitive readers
Available reader families exist for:
JsonElementJsonDocumentJToken(includingJObjectandJArray)DynamicValueAccessorover CLR object graphs anddynamicvaluesStringBooleanInt32Int64DecimalDoubleGuidDateTimeDateTimeOffset
Example:
var enabled = document.GetBooleanOrDefault("flags.enabled", false);
var created = document.GetRequiredDateTimeOffset("meta.createdAt");
if (!document.TryGetDecimal("pricing.total", out var total))
{
total = 0m;
}
Nullable primitive readers
When a field is optional but explicitly nullable (present-and-null is valid, missing is an error), use the nullable family:
TryGetNullable{Type}— returnsfalsefor missing or wrong type; returnstruewithnullfor a null valueGetRequiredNullable{Type}— throwsJsonPathNotFoundExceptionfor missing, throwsJsonValueTypeExceptionfor wrong type, returnsnullfor a null value
// Field is required to exist but may be null
int? count = document.GetRequiredNullableInt32("pagination.count");
// Field is optional; distinguish null from missing
if (document.TryGetNullableInt32("pagination.count", out int? maybeCount))
{
// path existed (value may be null)
}
Available for: String, Boolean, Int32, Int64, Decimal, Double, Guid, DateTime, DateTimeOffset — across JsonElement, JsonDocument, JToken, and DynamicValueAccessor.
Object readers
Object reads use the native serializer for the underlying DOM:
System.Text.Json:JsonSerializer.Deserialize<T>()Newtonsoft.Json:JToken.ToObject<T>(JsonSerializer)- CLR object graphs: direct assignment when the selected value already is
T, otherwise aSystem.Text.Jsonroundtrip
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var person = document.GetRequiredObject<Person>("payload.person", options);
var fallback = document.GetObjectOrDefault("payload.optionalPerson", new Person("Unknown", 0), options);
StjTokenKit does not change serializer semantics. If you need custom binding or converters, pass the appropriate serializer/options object for the DOM you are using.
Dynamic accessors
For CLR objects, configure lookup behavior once and then reuse the accessor:
var accessor = someObject.AsDynamicValue(ignoreCase: true);
var exists = accessor.Exists("user.name");
var isNull = accessor.IsNull("user.middle");
var type = accessor.GetValueType("user.name");
var dto = accessor.GetRequiredObject<Person>("user.profile");
Exceptions
Required APIs throw path-aware exceptions:
JsonPathNotFoundExceptionJsonValueNullExceptionJsonValueTypeException
Example:
try
{
var age = document.GetRequiredInt32("user.age");
}
catch (JsonValueTypeException ex)
{
Console.WriteLine($"{ex.Path}: expected {ex.ExpectedType}, actual {ex.ActualKind}");
}
Notes
JsonElementvalues are still tied to the lifetime of their owningJsonDocumentJTokenhelpers operate onJToken, so the same API works onJObject,JArray, and nested values- CLR object helpers operate on public properties, public fields, dictionaries,
ExpandoObject, and indexable lists/arrays - CLR object lookup is case-sensitive by default and can be made case-insensitive with
ignoreCase: true - no implicit string-to-number or string-to-bool coercion is performed
- numeric readers only succeed for numeric token kinds
Guidreaders only succeed for JSON stringsSystem.Text.Jsondate readers expect supported string formatsNewtonsoft.Jsondate readers accept both string dates and parsedDatetokens
| Product | Versions 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. |
-
net8.0
- Newtonsoft.Json (>= 13.0.4)
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 |
|---|---|---|
| 1.0.0 | 87 | 4/21/2026 |