Ondřej Hruška e564922c0a replace failure with thiserror, fix error not implementing Error 4 months ago
src replace failure with thiserror, fix error not implementing Error 4 months ago
.gitignore initial version 8 months ago
Cargo.toml replace failure with thiserror, fix error not implementing Error 4 months ago
README.md replace failure with thiserror, fix error not implementing Error 4 months ago

README.md

json_dotpath

Access members of nested JSON arrays and objects using "dotted paths".

Changes

1.0.3

Replaced failure with thiserror, implement std::error::Error for the error type.

1.0.0

The API changed to return Result<Option<T>> instead of panicking internally on error. The library is now much safer to use.

Further, all logic has been adjusted to be more robust and consistent.

Array append and prepend operators now use << and >> instead of overloading < and >, which now work the same way in all array accesses (getting the first and last element).

Dotted path

Dotted path represents a path from the root of a JSON object to one of its nodes. Such path is represented by joining the object and array keys by dots:

Consider this example JSON:

{
  "fruit": [
    {"name": "lemon", "color":  "yellow"},
    {"name": "apple", "color":  "green"},
    {"name": "cherry", "color":  "red"}
  ]
}

The following paths represent its parts:

  • "" ... the whole object
  • "fruit" ... the fruits array
  • "fruit.0" ... the first fruit object, {"name": "lemon", "color": "yellow"}
  • "fruit.1.name" ... the second (index is 0-based) fruit's name, "apple"

Special patterns may be used for object manipulation as well (see below).

Object operations

Five principal methods are added by the DotPaths trait to serde_json::Value, Vec<Value> and serde_json::Map<String, Value> (the inner structs of Value::Object and Value::Array).

  • dot_get(path) - get a value by path
  • dot_get_mut(path) - get a mutable reference to an element of the JSON object
  • dot_set(path, value) - set a new value, dropping the original (if any)
  • dot_replace(path, value) - set a new value, returning the original (if any)
  • dot_take(path) - remove a value by path, returning it (if any)

dot_set() supports array manipulation syntax not found in the other methods, namely the >n and <n pattern to insert an element before or after an index, shifting the rest of the Vec.

Additional convenience methods

  • dot_remove(path) - remove a value by path
  • dot_get_or(path, def) - get value, or a custom default
  • dot_get_or_default(path) - get value, or Default::default()

All methods are generic and take care of serializing and deserializing the stored / retrieved data. dot_get_mut() is an exception and returns &mut Value.

dot_set() and dot_remove() do not deserialize the original object, which makes them more efficient than dot_replace() and dot_take() when the original value is not needed.

Error reporting

All methods return Result<json_dotpath::Error, T> (aliased to json_dotpath::Result<T>), either as json_dotpath::Result<()>, or json_dotpath::Result<Option<T>> when a value is expected.

These results should be handled carefully, as they report structural errors (meaning the requested operation could not be performed), or the path given was invalid.

Dynamic object building

When a path that does not exist but could (e.g. an appended array element, a new object key), and one of the assignment methods or dot_get_mut() are used, this element will be created automatically, including its parent elements as needed.

This is well illustrated in one of the unit tests:

let mut obj = Value::Null;
let _ = obj.dot_get_mut("foo.0").unwrap(); // get mut, here only for side effects
assert_eq!(json!({"foo": [null]}), obj);

Null can flexibly become an array or object in such situations (see "Special handling of Null" below).

Dotted Path Syntax

Path is simply a sequence of path segment joined by periods (.).

Some symbols are ascribed special meaning by the library, depending on the method they're used in. All symbols (including .) may be escaped using a backslash if their literal value is needed as part of the path.

Array Patterns

Array keys must be numeric (integer), or one of the special patterns listed below.

  • < ... first element
  • > ... last element
  • - or << ... prepend
  • + or >> ... append
  • <n, e.g. <5 ... insert before the n-th element
  • >n, e.g. >5 ... insert after the n-th element

It's possible to create nested arrays or objects by setting a non-existent path, provided the key syntax rules are maintained.

See unit tests for more examples.

Special handling of Null

JSON null in an object can transparently become an array or object by setting it's members (even nested), as if it was an empty array or object. Whether it should become an array or object depends on the key used to index into it.

  • numeric key turns null into an array (only 0 and the special array operators are allowed, as other numbers are out of range for an empty array)
  • any other key turns it into a map
  • any key starting with an escape creates a map as well (e.g. \0.aaa turns null into {"0": {"aaa": …} } )

JSON null is considered an empty value and is transformed into Ok(None) when retrieved, as it can not be deserialized.

Setting a value to Value::Null works as expected and places a JSON null in the object.