Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ build-backend = "scikit_build_core.build"

[project]
name = "pybind11_jsoncons"
version = "0.1.1"
description="python binding for jsoncons (only what I needed for now)"
version = "0.1.2"
description="python binding for jsoncons + msgpack + jmespath (only what I needed for now)"
readme = "README.md"
authors = [
{ name = "zhixiong.tang", email = "dvorak4tzx@email.com" },
Expand Down
49 changes: 48 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using json = jsoncons::ojson; // using json = jsoncons::json;
namespace jmespath = jsoncons::jmespath;
namespace msgpack = jsoncons::msgpack;
using jmespath_expr_type = jmespath::jmespath_expression<json>;

namespace py = pybind11;
using rvp = py::return_value_policy;
Expand Down Expand Up @@ -225,6 +226,19 @@ struct JsonQueryRepl {
return result.to_string();
}

/**
* Evaluate a JMESPath expression against the JSON document.
* @param expr JMESPath expression
* @return Result of the evaluation as a json object
*/
json eval_expr(const jmespath_expr_type &expr) const {
auto result = expr.evaluate(doc, params_);
if (debug) {
std::cerr << pretty_print(result) << std::endl;
}
return result;
}

/**
* Add parameters for JMESPath evaluation.
* @param key Parameter key
Expand Down Expand Up @@ -394,9 +408,13 @@ struct JsonQuery {
std::vector<std::unique_ptr<jmespath::jmespath_expression<json>>> transforms_expr_;
std::map<std::string, json> params_;


std::deque<std::vector<json>> outputs_;

/**
* Internal method to check if a JSON document matches the predicate.
* @param msg JSON document to check
* @return True if the document matches the predicate, false otherwise
*/
bool __matches(const json &msg) const {
auto ret = predicate_expr_->evaluate(msg, params_);
return /*ret.is_bool() && */ ret.as_bool();
Expand Down Expand Up @@ -533,6 +551,15 @@ PYBIND11_MODULE(_core, m) {
Returns:
str: Result of the evaluation as a string
)pbdoc")
.def("eval_expr", &JsonQueryRepl::eval_expr, "expr"_a, R"pbdoc(
Evaluate a JMESPath expression against the JSON document.

Args:
expr: JMESPath expression

Returns:
json: Result of the evaluation as a json object
)pbdoc")
.def("add_params", &JsonQueryRepl::add_params, "key"_a, "value"_a, R"pbdoc(
Add parameters for JMESPath evaluation.

Expand Down Expand Up @@ -661,6 +688,26 @@ PYBIND11_MODULE(_core, m) {
str: JSON string representation
)pbdoc");

py::class_<jmespath_expr_type>(m, "JMESPathExpr", py::module_local(), py::dynamic_attr()) //
.def("evaluate", [](const jmespath_expr_type &self, const json &doc) -> json {
return self.evaluate(doc);
}, "doc"_a, R"pbdoc(
Evaluate the JMESPath expression against a JSON document.

Args:
doc: JSON document

Returns:
json: Result of the evaluation
)pbdoc")
//
.def_static("build", [](const std::string &expr_text) -> jmespath_expr_type {
return jmespath::make_expression<json>(expr_text);
}, "expr_text"_a, R"pbdoc(
Create a new JMESPath expression.
)pbdoc")
;

// m.def("dumps", [](const json &json_val) -> std::string {
// return json_val.to_string();
// });
Expand Down
2 changes: 2 additions & 0 deletions src/pybind11_jsoncons/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from ._core import (
JMESPathExpr,
Json,
JsonQuery,
JsonQueryRepl,
Expand All @@ -15,6 +16,7 @@
"__version__",
"JsonQuery",
"JsonQueryRepl",
"JMESPathExpr",
"Json",
"msgpack_decode",
"msgpack_encode",
Expand Down
62 changes: 61 additions & 1 deletion src/pybind11_jsoncons/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,38 @@ class Json:
"""
Convert a Python object to a JSON object.

This method converts various Python types to their JSON equivalents:
- None -> null
- bool -> boolean
- int -> integer
- float -> number
- str -> string
- list/tuple -> array
- dict -> object

Args:
obj: Python object to convert

Returns:
Json: Reference to self
Json: Reference to self with converted data

Raises:
RuntimeError: If the Python object contains circular references or unsupported types
"""

def to_python(self) -> Any:
"""
Convert a JSON object to a Python object.

This method converts JSON types to their Python equivalents:
- null -> None
- boolean -> bool
- integer -> int
- number -> float
- string -> str
- array -> list
- object -> dict

Returns:
Any: Python object representation of the JSON data
"""
Expand Down Expand Up @@ -122,6 +143,17 @@ class JsonQueryRepl:
str: Result of the evaluation as a string
"""

def eval_expr(self, expr: JMESPathExpr) -> Json:
"""
Evaluate a JMESPath expression against the JSON document.

Args:
expr: JMESPath expression

Returns:
Json: Result of the evaluation as a json object
"""

def add_params(self, key: str, value: str) -> None:
"""
Add parameters for JMESPath evaluation.
Expand Down Expand Up @@ -241,6 +273,34 @@ class JsonQuery:
Clear all processed data.
"""

class JMESPathExpr:
"""
A class representing a compiled JMESPath expression.
"""

def evaluate(self, doc: Json) -> Json:
"""
Evaluate the JMESPath expression against a JSON document.

Args:
doc: JSON document

Returns:
Json: Result of the evaluation
"""

@staticmethod
def build(expr_text: str) -> JMESPathExpr:
"""
Create a new JMESPath expression.

Args:
expr_text: JMESPath expression text

Returns:
JMESPathExpr: Compiled JMESPath expression
"""

def msgpack_decode(msgpack_bytes: bytes) -> str:
"""
Convert MessagePack binary data to a JSON string.
Expand Down
14 changes: 13 additions & 1 deletion tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


def test_version():
assert m.__version__ == "0.1.1"
assert m.__version__ == "0.1.2"


def test_repl():
Expand Down Expand Up @@ -95,6 +95,18 @@ def test_json_query():
assert json.loads(repl.eval("name")) == "Baby"
assert not json.loads(repl.eval("age >= `18`"))

assert repl.doc.to_json() == '{"age":5,"other":"too young","name":"Baby"}'
repl.doc.from_python(people[1])
assert repl.doc.to_json() == '{"age":20,"other":"foo","name":"Bob"}'
repl.debug = False
assert not repl.debug
assert repl.eval("age == `20`") == "true"

expr = m.JMESPathExpr.build("age == `20`")
assert isinstance(expr, m.JMESPathExpr)
assert expr.evaluate(m.Json().from_python(people[1])).to_python()
assert repl.eval_expr(expr).to_python()

jql = m.JsonQuery()
with pytest.raises(RuntimeError) as excinfo:
jql.setup_predicate("[*].[")
Expand Down
Loading