From df7868841be1bdf5a6e811e0bd22f4bd78704b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20All=C3=A8ne?= <66381046+callms@users.noreply.github.com> Date: Wed, 17 Aug 2022 18:17:55 +0200 Subject: [PATCH] Add preserve order on objects (#1) * Add preserve order on objects * Add README --- README.md | 8 ++++++++ jmespath/Cargo.toml | 3 ++- jmespath/src/functions.rs | 4 ++-- jmespath/src/interpreter.rs | 5 ++--- jmespath/src/variable.rs | 33 ++++++++++++++++----------------- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8a4774af..913b57b5 100644 --- a/README.md +++ b/README.md @@ -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/) diff --git a/jmespath/Cargo.toml b/jmespath/Cargo.toml index f7f4451d..f752a745 100644 --- a/jmespath/Cargo.toml +++ b/jmespath/Cargo.toml @@ -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] diff --git a/jmespath/src/functions.rs b/jmespath/src/functions.rs index 1eb64d48..ffab3a2c 100644 --- a/jmespath/src/functions.rs +++ b/jmespath/src/functions.rs @@ -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. @@ -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() diff --git a/jmespath/src/interpreter.rs b/jmespath/src/interpreter.rs index 97a9060d..51dfc369 100644 --- a/jmespath/src/interpreter.rs +++ b/jmespath/src/interpreter.rs @@ -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; @@ -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); diff --git a/jmespath/src/variable.rs b/jmespath/src/variable.rs index 8df87487..55754711 100644 --- a/jmespath/src/variable.rs +++ b/jmespath/src/variable.rs @@ -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; @@ -55,7 +55,7 @@ pub enum Variable { Bool(bool), Number(Number), Array(Vec), - Object(BTreeMap), + Object(IndexMap), Expref(Ast), } @@ -180,7 +180,7 @@ fn convert_map<'a, T>(value: T) -> Result where T: Iterator, { - let mut map: BTreeMap = BTreeMap::new(); + let mut map: IndexMap = IndexMap::new(); for kvp in value { map.insert(kvp.0.to_owned(), kvp.1.to_jmespath()?); } @@ -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> { + pub fn as_object(&self) -> Option<&IndexMap> { match self { Variable::Object(map) => Some(map), _ => None, @@ -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); @@ -868,7 +868,7 @@ impl<'de> de::SeqAccess<'de> for SeqDeserializer { } struct MapDeserializer { - iter: as IntoIterator>::IntoIter, + iter: as IntoIterator>::IntoIter, value: Option, } @@ -959,12 +959,12 @@ pub struct TupleVariantState { #[doc(hidden)] pub struct StructVariantState { name: String, - map: BTreeMap, + map: IndexMap, } #[doc(hidden)] pub struct MapState { - map: BTreeMap, + map: IndexMap, next_key: Option, } @@ -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)) } @@ -1141,7 +1141,7 @@ impl ser::Serializer for Serializer { fn serialize_map(self, _len: Option) -> Result { Ok(MapState { - map: BTreeMap::new(), + map: IndexMap::new(), next_key: None, }) } @@ -1159,7 +1159,7 @@ impl ser::Serializer for Serializer { ) -> Result { Ok(StructVariantState { name: String::from(variant), - map: BTreeMap::new(), + map: IndexMap::new(), }) } } @@ -1226,7 +1226,7 @@ impl ser::SerializeTupleVariant for TupleVariantState { } fn end(self) -> Result { - let mut object = BTreeMap::new(); + let mut object = IndexMap::new(); object.insert(self.name, Rcvar::new(Variable::Array(self.vec))); Ok(Variable::Object(object)) } @@ -1295,7 +1295,7 @@ impl ser::SerializeStructVariant for StructVariantState { } fn end(self) -> Result { - let mut object = BTreeMap::new(); + let mut object = IndexMap::new(); object.insert(self.name, Rcvar::new(Variable::Object(self.map))); Ok(Variable::Object(object)) } @@ -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() { @@ -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())),