| name | nojson |
| description | Work with the nojson Rust crate: parse and generate JSON via the `Json<T>` wrapper, `RawJson`/`RawJsonOwned`/`RawJsonValue` for low-level access, the `DisplayJson` trait, and in-place builders `json()` / `object()` / `array()`. Implement `DisplayJson` + `TryFrom<RawJsonValue<'_, '_>>` to round-trip custom types; navigate objects with `to_member` / `to_path_member` and `required()` / `optional()`; produce rich error context via `RawJsonValue::invalid()` and `JsonParseError::get_line_and_column_numbers`; handle JSONC with `parse_jsonc`. Use when the task mentions nojson, its no-macro/no-dependency JSON API, or no_std JSON work with this crate.
|
| license | MIT |
| compatibility | Requires Rust 1.88+ and cargo. |
nojson
Use this skill when integrating nojson into Rust code. Focus on the crate's
actual API shape and usage patterns, not generic JSON background.
What this crate exposes
Json<T> — wrapper giving FromStr / Display for any T that implements
the corresponding nojson traits
DisplayJson — JSON analogue of core::fmt::Display
RawJson<'text> / RawJsonOwned — a parsed, validated JSON text that has
not been converted to Rust types yet
RawJsonValue<'text, 'raw> — a node inside a RawJson; the entry point for
reading values
RawJsonMember<'text, 'raw, 'a> — result of to_member; resolves with
required() / optional() / TryInto<Option<T>>
JsonFormatter — the formatter passed to DisplayJson::fmt and to the
nojson::json(|f| …) closure
JsonArrayFormatter / JsonObjectFormatter — supplied inside
f.array(|f| …) / f.object(|f| …) (and via the nojson::array /
nojson::object shortcuts) for adding elements / members
- Free functions
json(), object(), array() — return values that
impl Display + DisplayJson
JsonValueKind, JsonParseError
Choosing the right API
- Typed round-trip — use
Json<T> with
T: DisplayJson + for<'t, 'r> TryFrom<RawJsonValue<'t, 'r>, Error = JsonParseError>.
Parse via text.parse::<Json<T>>(), format via Json(&value).to_string().
- Inline output (incl. pretty-printing) — use
json(), object(), or
array() with a closure. Good for one-off logging / CLI output without
defining a struct.
- Imperative navigation / validation —
RawJson::parse(text)?, then
traverse with RawJsonValue methods. Use this when you need positions,
conditional fields, or rich error context.
- Owned JSON detached from the input lifetime — use
RawJsonOwned or
RawJsonValue::extract().into_owned().
- JSON with comments / trailing commas —
RawJson::parse_jsonc /
RawJsonOwned::parse_jsonc. Returns (RawJson, Vec<Range<usize>>) where
the ranges are comment byte spans in the original text.
Usage gotchas
- The crate is
#![no_std] + alloc. The default std feature enables
DisplayJson / TryFrom impls for HashMap, HashSet, PathBuf,
IpAddr, SocketAddr, etc. Turn it off for no_std builds.
- Custom parsing implements
TryFrom<RawJsonValue<'text, 'raw>> with
Error = JsonParseError — not FromStr. FromStr on Json<T> delegates
to this TryFrom impl.
to_member(name) does a linear scan. If you read many siblings, prefer
to_object() once and match keys yourself.
to_path_member(&["a","b","c"]) is a convenience. Intermediate keys are
required; only the last may be handled as optional.
as_string_str() returns Err when the JSON string has escapes; it never
allocates. For general decoding use to_unquoted_string_str() — it returns
Cow<'text, str> (borrowed when no escapes).
f32 / f64 that are not finite (NaN, ±Infinity) serialize as null. JSON
has no NaN literal; do not expect round-trips through floats.
() serializes to / deserializes from null. Option<T>::None also maps to
null.
- Pretty-printing is controlled on the formatter:
f.set_indent_size(n) + f.set_spacing(true). Settings apply to the current
depth and deeper; inner nojson::json(|f| …) can override locally.
RawJsonValue is Copy; most traversal methods consume self by value on
purpose — pass it around freely.
RawJsonValue::index() is stable within one RawJson. Cache it and re-fetch
with get_value_by_index for O(1) access after validation.
API patterns to preserve
Typed round-trip for a custom struct:
struct Person { name: String, age: u32 }
impl nojson::DisplayJson for Person {
fn fmt(&self, f: &mut nojson::JsonFormatter<'_, '_>) -> std::fmt::Result {
f.object(|f| {
f.member("name", &self.name)?;
f.member("age", self.age)
})
}
}
impl<'text, 'raw> TryFrom<nojson::RawJsonValue<'text, 'raw>> for Person {
type Error = nojson::JsonParseError;
fn try_from(v: nojson::RawJsonValue<'text, 'raw>) -> Result<Self, Self::Error> {
Ok(Person {
name: v.to_member("name")?.required()?.try_into()?,
age: v.to_member("age")?.required()?.try_into()?,
})
}
}
let p: nojson::Json<Person> = r#"{"name":"a","age":1}"#.parse()?;
let s = nojson::Json(&p.0).to_string();
Inline JSON object (most common shortcut):
let req = nojson::object(|f| {
f.member("user", "alice")?;
f.member("ids", &[1, 2, 3])?;
f.member("active", true)
}).to_string();
nojson::array(|f| ...) exists for symmetry, but is rarely needed: anything
that already implements DisplayJson (&[T], Vec<T>, [T; N], iterators
collected into these, etc.) can be passed directly to f.member / f.value,
so an array literal usually suffices.
Inline pretty-printed output (when you need indent / spacing):
let pretty = nojson::json(|f| {
f.set_indent_size(2);
f.set_spacing(true);
f.object(|f| {
f.member("items", &[1, 2, 3])?;
f.member("enabled", true)
})
});
let text = pretty.to_string();
Optional members:
let obj = json.value();
let city: Option<String> = obj.to_member("city")?.try_into()?;
let age: Option<u32> = obj.to_member("age")?.map(|v| v.try_into())?;
Validation with precise error context:
let json = nojson::RawJson::parse(text)?;
let v = json.value();
let n: u32 = v.as_number_str()?.parse().map_err(|e| v.invalid(e))?;
if n == 0 {
return Err(v.invalid("must be positive"));
}
Error reporting with line / column:
if let Err(err) = nojson::RawJson::parse(text) {
if let Some((line, col)) = err.get_line_and_column_numbers(text) {
eprintln!("{err} at {line}:{col}");
}
if let Some(line) = err.get_line(text) {
eprintln!(" {line}");
}
}
JSONC:
let (json, comments) = nojson::RawJson::parse_jsonc(text)?;
Sub-tree extraction:
let user = json.value().to_member("user")?.required()?;
let sub: nojson::RawJson<'_> = user.extract();
let owned = sub.into_owned();
Runnable references
Both live under examples/ in the crate root — read them when you need a
complete working pattern rather than a snippet:
examples/parse_error.rs — full template for turning a JsonParseError
into a CLI-style diagnostic (line / column, offending line, caret) using
get_line_and_column_numbers + get_line.
examples/jsonc_pretty.rs — RawJson::parse_jsonc + RawJsonValue
traversal for a JSONC pretty-printer that preserves comments. Simpler
than a full formatter (always multi-line, comments on their own lines);
if you need trailing-comment fidelity, point the user at jcfmt.
Practical hints
- Reach for
Json<T> first for straightforward typed parsing; drop to
RawJson only when you need position info, conditional fields, or custom
validation.
- Nested generics usually work via the blanket impls already in the crate:
Vec<T>, [T; N], Option<T>, BTreeMap<K, V>, HashMap<K, V>,
Rc<T>, Arc<T>, etc. You rarely need to hand-write these.
- Map keys parse via
K: FromStr. The JSON key is always a string, so
BTreeMap<u32, _> is valid when the keys are numeric strings.
- For deeply nested single-field lookups use
to_path_member. For multiple
siblings, resolve the parent once and call to_member for each.
- The root value of a
RawJson is always index 0 (json.value() ==
json.get_value_by_index(0).unwrap()).
RawJsonOwned::object(|f| …) / ::array(|f| …) / ::json(|f| …) are
convenience constructors — they build, serialize, and re-parse; use them
when you want an owned JSON value without going through to_string.