# serde_field_result
[](https://docs.rs/serde_field_result)
[](https://github.com/gramistella/serde-field-result/actions/workflows/ci.yml)
Field-level Serde recovery for schema-drift-tolerant API clients.
```toml
serde_field_result = "0.1"
```
The core type is `Field<T>`:
```rust
use serde::Deserialize;
use serde_field_result::Field;
#[derive(Deserialize)]
struct Row {
#[serde(default)] // required if absent fields should become Field::Missing
price: Field<f64>,
}
```
A field can be:
```rust
Field::Missing
Field::Valid(value)
Field::Invalid(error)
```
This lets one bad field fail softly without destroying an otherwise useful
response.
You can inspect invalid fields without pattern matching every variant:
```rust
let mut warnings = Vec::new();
if let Some(error) = row.price.error() {
warnings.push(format!("bad price: {error}"));
}
```
`Field<T>` is intentionally a three-state type, not a JSON patch type or a
generic `T: Deserialize` buffering wrapper. For ordinary `Field<T>` values, an
absent field, `null`, and Serde unit all decode as `Field::Missing`. Use
`Field<Option<T>>` when explicit `null` or unit should be a valid `None` while
an absent field remains `Field::Missing`.
For JSON-specific raw capture, enable the `json` feature. Use `JsonField<T>`
when the decoded value should own its data and invalid fields should retain an
owned `Box<serde_json::value::RawValue>`. Use `BorrowedJsonField<'de, T>` when
the original JSON input outlives the decoded value and you want invalid raw JSON
and valid nested values to borrow from that input. `BorrowedJsonField` has the
same borrowing limitations as `serde_json`: escaped strings may not deserialize
into `&'de str`, so use `String` or `Cow<'de, str>` when string values may need
unescaping.
`JsonField<Option<T>>` and `BorrowedJsonField<Option<T>>` do not distinguish
explicit JSON `null` from missing; JSON `null` is always `Missing`.
```toml
serde_field_result = { version = "0.1", features = ["json"] }
```
Default features enable `std`. For `no_std` environments with `alloc`, disable
default features and enable `alloc` explicitly:
```toml
serde_field_result = { version = "0.1", default-features = false, features = ["alloc"] }
```
The `json` feature also works without `std`; it enables `alloc` automatically.
```rust
use serde::Deserialize;
use serde_field_result::{BorrowedJsonField, JsonField};
#[derive(Deserialize)]
struct OwnedNested {
value: String,
}
#[derive(Deserialize)]
struct BorrowedNested<'a> {
#[serde(borrow)]
value: &'a str,
}
#[derive(Deserialize)]
struct OwnedRow {
#[serde(default)]
nested: JsonField<OwnedNested>,
}
#[derive(Deserialize)]
struct BorrowedRow<'a> {
#[serde(default, borrow)]
nested: BorrowedJsonField<'a, BorrowedNested<'a>>,
}
```
For custom scalar-like types, prefer implementing `ScalarFieldDecode`. Implement
`FieldDecode` directly only when you are prepared to consume and drain every
unexpected Serde shape. In custom visitors, prefer `invalid_seq` and
`invalid_map` for unexpected arrays or objects; they drain the compound value
before returning `Field::Invalid`, preserving following sibling fields.
Default scalar type-mismatch errors are stored without formatting an owned error
message up front. Custom decoders can still use `FieldError::new` when an error
needs to include dynamic details.
The built-in primitive integer and float decoders accept JSON numbers, not
numeric strings. Implement a custom `ScalarFieldDecode` wrapper when an API
uses strings such as `"42"` or `"12.5"` for numbers. The built-in `f32` and
`f64` field decoders reject non-finite values. Integer-to-float conversions
follow Rust cast semantics and may lose precision.