Skip to content

Commit

Permalink
Add preserve order on objects (#1)
Browse files Browse the repository at this point in the history
* Add preserve order on objects

* Add README
  • Loading branch information
callms authored Aug 17, 2022
1 parent 93a173b commit df78688
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 23 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# JMESPath for Rust

## This fork preserves order

This is a fork of [jmespath.rs](https://github.com/jmespath/jmespath.rs) that
preserves order. It is highly incompatible with the code upstream, probably
someone who wants to take over and add this as a feature, this would be a nice
addition to the library.


Rust implementation of [JMESPath](http://jmespath.org), a query language for JSON.

[Documentation](https://docs.rs/jmespath/)
Expand Down
3 changes: 2 additions & 1 deletion jmespath/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ edition = "2018"

[dependencies]
serde = { version = "1", features = ["rc"] }
serde_json = "1"
indexmap = {version = "1.9.1", features = ["serde-1"]}
serde_json = { version = "1", features = ["preserve_order"] }
lazy_static = "1.4"

[build-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions jmespath/src/functions.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! JMESPath functions.

use std::cmp::{max, min};
use std::collections::BTreeMap;
use std::fmt;

use crate::interpreter::{interpret, SearchResult};
use crate::variable::{JmespathType, Variable};
use crate::{Context, ErrorReason, JmespathError, Rcvar, RuntimeError};
use indexmap::IndexMap;
use serde_json::Number;

/// Represents a JMESPath function.
Expand Down Expand Up @@ -614,7 +614,7 @@ defn!(MergeFn, vec![arg!(object)], Some(arg!(object)));
impl Function for MergeFn {
fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> SearchResult {
self.signature.validate(args, ctx)?;
let mut result = BTreeMap::new();
let mut result = IndexMap::new();
for arg in args {
result.extend(
arg.as_object()
Expand Down
5 changes: 2 additions & 3 deletions jmespath/src/interpreter.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
//! Interprets JMESPath expressions.

use std::collections::BTreeMap;

use super::ast::Ast;
use super::variable::Variable;
use super::Context;
use super::{ErrorReason, JmespathError, Rcvar, RuntimeError};
use indexmap::IndexMap;

/// Result of searching data using a JMESPath Expression.
pub type SearchResult = Result<Rcvar, JmespathError>;
Expand Down Expand Up @@ -135,7 +134,7 @@ pub fn interpret(data: &Rcvar, node: &Ast, ctx: &mut Context<'_>) -> SearchResul
if data.is_null() {
Ok(Rcvar::new(Variable::Null))
} else {
let mut collected = BTreeMap::new();
let mut collected = IndexMap::new();
for kvp in elements {
let value = interpret(data, &kvp.value, ctx)?;
collected.insert(kvp.key.clone(), value);
Expand Down
33 changes: 16 additions & 17 deletions jmespath/src/variable.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Module for JMESPath runtime variables.

use indexmap::IndexMap;
use serde::de::IntoDeserializer;
use serde::*;
use serde_json::error::Error;
use serde_json::value::Value;
use std::cmp::{max, Ordering};
use std::collections::BTreeMap;
use std::fmt;
use std::iter::Iterator;
use std::string::ToString;
Expand Down Expand Up @@ -55,7 +55,7 @@ pub enum Variable {
Bool(bool),
Number(Number),
Array(Vec<Rcvar>),
Object(BTreeMap<String, Rcvar>),
Object(IndexMap<String, Rcvar>),
Expref(Ast),
}

Expand Down Expand Up @@ -180,7 +180,7 @@ fn convert_map<'a, T>(value: T) -> Result<Variable, JmespathError>
where
T: Iterator<Item = (&'a String, &'a Value)>,
{
let mut map: BTreeMap<String, Rcvar> = BTreeMap::new();
let mut map: IndexMap<String, Rcvar> = IndexMap::new();
for kvp in value {
map.insert(kvp.0.to_owned(), kvp.1.to_jmespath()?);
}
Expand Down Expand Up @@ -264,9 +264,9 @@ impl Variable {
self.as_object().is_some()
}

/// If the value is an Object, returns the associated BTreeMap.
/// If the value is an Object, returns the associated IndexMap.
/// Returns None otherwise.
pub fn as_object(&self) -> Option<&BTreeMap<String, Rcvar>> {
pub fn as_object(&self) -> Option<&IndexMap<String, Rcvar>> {
match self {
Variable::Object(map) => Some(map),
_ => None,
Expand Down Expand Up @@ -604,7 +604,7 @@ impl<'de> de::Deserialize<'de> for Variable {
where
V: de::MapAccess<'de>,
{
let mut values = BTreeMap::new();
let mut values = IndexMap::new();

while let Some((key, value)) = visitor.next_entry()? {
values.insert(key, value);
Expand Down Expand Up @@ -868,7 +868,7 @@ impl<'de> de::SeqAccess<'de> for SeqDeserializer {
}

struct MapDeserializer {
iter: <BTreeMap<String, Rcvar> as IntoIterator>::IntoIter,
iter: <IndexMap<String, Rcvar> as IntoIterator>::IntoIter,
value: Option<Variable>,
}

Expand Down Expand Up @@ -959,12 +959,12 @@ pub struct TupleVariantState {
#[doc(hidden)]
pub struct StructVariantState {
name: String,
map: BTreeMap<String, Rcvar>,
map: IndexMap<String, Rcvar>,
}

#[doc(hidden)]
pub struct MapState {
map: BTreeMap<String, Rcvar>,
map: IndexMap<String, Rcvar>,
next_key: Option<String>,
}

Expand Down Expand Up @@ -1096,7 +1096,7 @@ impl ser::Serializer for Serializer {
where
T: ser::Serialize,
{
let mut values = BTreeMap::new();
let mut values = IndexMap::new();
values.insert(String::from(variant), Rcvar::new(to_variable(&value)?));
Ok(Variable::Object(values))
}
Expand Down Expand Up @@ -1141,7 +1141,7 @@ impl ser::Serializer for Serializer {

fn serialize_map(self, _len: Option<usize>) -> Result<MapState, Error> {
Ok(MapState {
map: BTreeMap::new(),
map: IndexMap::new(),
next_key: None,
})
}
Expand All @@ -1159,7 +1159,7 @@ impl ser::Serializer for Serializer {
) -> Result<StructVariantState, Error> {
Ok(StructVariantState {
name: String::from(variant),
map: BTreeMap::new(),
map: IndexMap::new(),
})
}
}
Expand Down Expand Up @@ -1226,7 +1226,7 @@ impl ser::SerializeTupleVariant for TupleVariantState {
}

fn end(self) -> Result<Variable, Error> {
let mut object = BTreeMap::new();
let mut object = IndexMap::new();
object.insert(self.name, Rcvar::new(Variable::Array(self.vec)));
Ok(Variable::Object(object))
}
Expand Down Expand Up @@ -1295,7 +1295,7 @@ impl ser::SerializeStructVariant for StructVariantState {
}

fn end(self) -> Result<Variable, Error> {
let mut object = BTreeMap::new();
let mut object = IndexMap::new();
object.insert(self.name, Rcvar::new(Variable::Object(self.map)));
Ok(Variable::Object(object))
}
Expand All @@ -1307,7 +1307,6 @@ mod tests {
use crate::ast::{Ast, Comparator};
use crate::Rcvar;
use serde_json::{self, Number, Value};
use std::collections::BTreeMap;

#[test]
fn creates_variable_from_str() {
Expand Down Expand Up @@ -1533,8 +1532,8 @@ mod tests {
#[test]
fn test_parses_json_object() {
let var = Variable::from_json("{\"a\": 1, \"b\": {\"c\": true}}").unwrap();
let mut expected = BTreeMap::new();
let mut sub_obj = BTreeMap::new();
let mut expected = IndexMap::new();
let mut sub_obj = IndexMap::new();
expected.insert(
"a".to_string(),
Rcvar::new(Variable::Number(Number::from_f64(1.0).unwrap())),
Expand Down

0 comments on commit df78688

Please sign in to comment.