Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

112 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

valq   Docs.rs shield crates.io shield

valq provides macros for querying semi-structured ("JSON-ish") data with the JavaScript-like syntax.

Look & Feel:

let obj: serde_json::Value = ...;

// without valq: tedious and_then() chain...
let deep = obj
    .get("path")
    .and_then(|v| v.get("to"))
    .and_then(|v| v.get("value"))
    .and_then(|v| v.get("at"))
    .and_then(|v| v.get("deep"));

// with valq: very concise and readable!
use valq::query_value;
let deep = query_value!(obj.path.to.value.at.deep);

Installation

Add this to the Cargo.toml in your project:

[dependencies]
valq = "*"

What's provided

The principal macro provided by this crate is query_value!. There is also a Result-returning variant of query_value!, called query_value_reosult!.

query_value! macro

A macro for querying, extracting and converting inner value of semi-structured data.

Basic Queries

// get field `foo` from JSON object `obj`
let foo = query_value!(obj.foo);

// get the first item of the nested JSON array `arr` in `obj`
let head = query_value!(obj.arr[0]);

// more complex query, just works!
let abyss = query_value!(obj.path.to.matrix[0][1].abyss);

Extracting Mutable Reference to Inner Value

use serde_json::{json, Value}

let mut obj = json!({"foo": { "bar": { "x": 1, "y": 2 }}});
{
    // prefixed `mut` means extracting mutable reference
    let bar: &mut Value = query_value!(mut obj.foo.bar).unwrap();
    *bar = json!({"x": 100, "y": 200});
}
// with `->` syntax, you can cast `Value` as typed value (see below)
assert_eq!(query_value!(obj.foo.bar.x -> u64), Some(100));
assert_eq!(query_value!(obj.foo.bar.y -> u64), Some(200));

Casting & Deserializing to Specified Type

// try to cast the queried value into `u64` using `as_u64()` method on that value.
// results in `None` in case of type mismatch
let foo_u64: Option<u64> = query_value!(obj.foo -> u64);

// in the context of mutable reference extraction (see below), `as_xxx_mut()` method is used instead.
let arr_vec: Option<&mut Vec<Value>> = query_value!(mut obj.arr -> array);
use serde::Deserialize;
use serde_json::json;
use valq::query_value;

#[derive(Deserialize)]
struct Person {
    name: String,
    age: u8,
}

let j = json!({"author": {"name": "jiftechnify", "age": 31}});

// try to deserialize the queried value into a value of type `Person`.
let author: Option<Person> = query_value!(j.author >> (Person));

Unwrapping Query Results with Default Values

use serde_json::json;
use valq::query_value;

let obj = json!({"foo": {"bar": "not a number"}});
assert_eq!(query_value!(obj.foo.bar -> str ?? "failed!"), "not a number");
assert_eq!(query_value!(obj.foo.bar -> u64 ?? 42), 42); // explicitly provided default
assert_eq!(query_value!(obj.foo.bar -> u64 ?? default), 0u64); // using u64::default()

query_value_result! macro

A variant of query_value! that returns Result<T, valq::Error> instead of Option<T>.

use serde::Deserialize;
use serde_json::json;
use valq::{query_value_result, Error};

let obj = json!({"foo": {"bar": 42}});

// Error::ValueNotFoundAtPath: querying non-existent path
let result = query_value_result!(obj.foo.baz);
assert!(matches!(result, Err(Error::ValueNotFoundAtPath(_))));

// Error::AsCastFailed: type casting failure
let result = query_value_result!(obj.foo.bar -> str);
assert!(matches!(result, Err(Error::AsCastFailed(_))));

// Error::DeserializationFailed: deserialization failure
let result = query_value_result!(obj.foo >> (Vec<u8>));
assert!(matches!(result, Err(Error::DeserializationFailed(_))));

Helper: transpose_tuple! macro

Transposes a tuple of Options/Results into an Option/Result of a tuple. This is meant to be used with query_value!/query_value_result! macros, for "cherry-picking" deep value from data.

use serde_json::json;
use valq::{query_value, transpose_tuple};

let data = json!({"name": "valq", "version": "0.2.0"});

// Combine multiple Option results into Option<tuple>
let picks = transpose_tuple!(
    query_value!(data.name -> str),
    query_value!(data.version -> str),
);
assert_eq!(picks, Some(("valq", "0.2.0")));

// For Result variant, "Result;" prefix is needed
let picks = transpose_tuple!(
    Result;
    query_value_result!(data.name -> str),
    query_value_result!(data.version -> str),
);
assert!(picks.is_ok());

Compatibility

The query_value! macro can be used with arbitrary data structure(to call, Value) that supports get(&self, idx) -> Option<&Value> method that retrieves a value at idx.

Extracting mutable reference is also supported if your Value supports get_mut(&mut self, idx) -> Option<&Value>.

Instances of compatible data structures:

About

macros for querying semi-structured data with the JavaScript-like syntax

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages