A faithful MoonBit port of jmespath.py, a query language for JSON.
///|
test "search" {
let data : Json = {
"people": [
{ "name": "alice", "age": 30 },
{ "name": "bob", "age": 25 },
{ "name": "carol", "age": 35 },
],
}
json_inspect(@jmespath.search("people[?age > `26`].name", data), content=[
"alice", "carol",
])
json_inspect(
@jmespath.search("max_by(people, &age).name", data),
content="carol",
)
json_inspect(
@jmespath.search("people[*].{n: name, adult: age >= `18`} | [0]", data),
content={ "n": "alice", "adult": true },
)
}///|
test "compile" {
let expr = @jmespath.compile("sort_by(@, &to_number(v))[*].k")
json_inspect(expr.search([{ "k": "a", "v": "10" }, { "k": "b", "v": "9" }]), content=[
"b", "a",
])
inspect(expr.expression, content="sort_by(@, &to_number(v))[*].k")
}///|
test "errors" {
try @jmespath.compile("foo]bar") catch {
e =>
inspect(
e,
content=(
#|Unexpected token: ]: Parse error at column 3, token "]" (RBRACKET), for expression:
#|"foo]bar"
#| ^
),
)
} noraise {
_ => fail("expected a parse error")
}
try @jmespath.search("length(@)", Json::number(2.0)) catch {
JMESPathTypeError(function_name~, actual_type~, ..) as e => {
inspect(function_name, content="length")
inspect(actual_type, content="number")
inspect(
e,
content="In function length(), invalid type for value: 2, expected one of: ['string', 'array', 'object'], received: \"number\"",
)
}
e => fail("unexpected error: \{e}")
} noraise {
_ => fail("expected a type error")
}
}///|
test "custom functions" {
let functions = @jmespath.Functions::new()
functions.register_function(
"count_if",
[@jmespath.ArgSpec::new(["expref"]), @jmespath.ArgSpec::new(["array"])],
args => {
guard args is [Expref(expref), Data(Array(items))] else { Json::null() }
let mut n = 0
for item in items {
if expref.visit(item) is True {
n 1
}
}
Json::number(n.to_double())
},
)
let options = @jmespath.Options::new(custom_functions=functions)
json_inspect(
@jmespath.search("count_if(&(@ > `1`), @)", [1, 2, 3], options~),
content=2,
)
// The signature is enforced like for builtins.
try @jmespath.search("count_if(@, @)", [1], options~) catch {
e =>
inspect(
e,
content="In function count_if(), invalid type for value: [1], expected one of: ['expref'], received: \"array\"",
)
} noraise {
_ => fail("expected a type error")
}
}///|
test "python numbers" {
let data = @jmespath.loads("{\"a\": 1, \"b\": 1.0, \"c\": \"\u{e9}\"}")
inspect(
@jmespath.dumps(@jmespath.search("[a, b, c]", data)),
content="[1,1.0,\"\\u00e9\"]",
)
inspect(
@jmespath.dumps(
@jmespath.search("[to_string(b), avg(`[1, 2]`), sum(`[1, 2]`)]", data),
),
content=(
#|["1.0",1.5,3]
),
)
}| jmespath.py | MoonBit |
|---|---|
| jmespath.search(expr, data, opts) | @jmespath.search(expr, data, options?) |
| jmespath.compile(expr) | @jmespath.compile(expr) |
| ParsedResult.search(data, opts) | ParsedResult::search(data, options?) |
| ParsedResult._render_dot_file() | ParsedResult::render_dot_file() |
| parser.Parser().parse(expr) | Parser::new().parse(expr) |
| Parser.purge(), _MAX_SIZE | Parser::purge(), parser_max_size |
| lexer.Lexer().tokenize(expr) | Lexer::new().tokenize(expr) (Token) |
| ast.field(name), ... | Node::Field(name), ... (Node::to_json gives the dict form) |
| visitor.TreeInterpreter(options) | TreeInterpreter::new(options?) |
| visitor.Options(custom_functions) | Options::new(custom_functions?) |
| visitor._Expression | Expression (Expression::visit(value)) |
| functions.Functions, @signature | Functions, ArgSpec, register_function |
| exceptions.*Error | JMESPathError::*Error constructors |
pub(all) suberror JMESPathError {
ParseError(lex_position~ : Int, token_value~ : String, token_type~ : String, msg~ : String, expression~ : String?)
IncompleteExpressionError(lex_position~ : Int, token_value~ : String?, token_type~ : String?, expression~ : String?)
LexerError(lexer_position~ : Int, lexer_value~ : String, message~ : String, expression~ : String?)
ArityError(expected_arity~ : Int, actual_arity~ : Int, function_name~ : String)
VariadictArityError(expected_arity~ : Int, actual_arity~ : Int, function_name~ : String)
JMESPathTypeError(function_name~ : String, current_value~ : Value, actual_type~ : String, expected_types~ : Array[String])
EmptyExpressionError
UnknownFunctionError(String)
ValueError(String)
TypeError(String)
OverflowError(String)
}impl Show for JMESPathErrorpub(all) suberror JSONDecodeError {
JSONDecodeError(msg~ : String, doc~ : String, pos~ : Int)
}impl Show for JSONDecodeErrorpub struct Functions {
// private fields
}test {
let functions = @jmespath.Functions::new()
functions.register_function("double", [@jmespath.ArgSpec::new(["number"])], args => {
guard args[0] is Data(Number(n, ..)) else { Json::null() }
Json::number(n * 2.0)
})
let options = @jmespath.Options::new(custom_functions=functions)
let result = @jmespath.search("double(`21`)", Json::null(), options~)
json_inspect(result, content=42)
}pub struct Lexer {
// private fields
}pub(all) enum Node {
Comparator(String, Node, Node)
Current
Expref(Node)
FunctionExpression(String, Array[Node])
Field(String)
FilterProjection(Node, Node, Node)
Flatten(Node)
Identity
Index(Int)
IndexExpression(Array[Node])
KeyValPair(String, Node)
Literal(Json)
MultiSelectDict(Array[Node])
MultiSelectList(Array[Node])
OrExpression(Node, Node)
AndExpression(Node, Node)
NotExpression(Node)
Pipe(Node, Node)
Projection(Node, Node)
Subexpression(Array[Node])
Slice(Int?, Int?, Int?)
ValueProjection(Node, Node)
} derive(Eq, Debug)impl Show for ParsedResultfn ParsedResult::search(self : ParsedResult, value : Json, options? : Options) -> Json raise JMESPathErrorpub struct Parser {
// private fields
}pub struct TreeInterpreter {
// private fields
}fn TreeInterpreter::visit(self : TreeInterpreter, node : Node, value : Json) -> Json raise JMESPathErrortest {
let parsed = @jmespath.compile("foo.bar")
json_inspect(parsed.search({ "foo": { "bar": "baz" } }), content="baz")
}test {
let v = @jmespath.loads("[1, 1.0, 1e3, \"\\u2713\"]")
inspect(@jmespath.dumps(v), content="[1,1.0,1000.0,\"\\u2713\"]")
}let parser_max_size : Inttest {
let data : Json = { "foo": [{ "a": 1 }, { "a": 2 }, { "b": 3 }] }
json_inspect(@jmespath.search("foo[*].a", data), content=[1, 2])
}Install
Download zipA faithful MoonBit port of jmespath.py, a query language for JSON.