jomini/json/
mod.rs

1//! Handles conversion of plaintext clausewitz format to JSON
2//!
3//! ```
4//! use jomini::{TextTape, json::{JsonOptions, DuplicateKeyMode}};
5//!
6//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
7//! let tape = TextTape::from_slice(b"core=a core=b")?;
8//! let reader = tape.windows1252_reader();
9//!
10//! let options = JsonOptions::new()
11//!     .with_prettyprint(false)
12//!     .with_duplicate_keys(DuplicateKeyMode::Preserve);
13//!
14//! // These are the default options
15//! assert_eq!(options, JsonOptions::default());
16//!
17//! let actual = reader.json()
18//!     .with_options(options)
19//!     .to_string();
20//! assert_eq!(actual, r#"{"core":"a","core":"b"}"#);
21//! # Ok(())
22//! # }
23//! ```
24//!
25//! The scope of the JSON can be narrowed to an inner value. This comes in handy
26//! when the parsed document is large but only a small subset of it needs to be
27//! exposed with JSON.
28//!
29//! ```
30//! use jomini::{TextTape};
31//!
32//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
33//! let tape = TextTape::from_slice(b"nums={1 2 3 4}")?;
34//! let reader = tape.windows1252_reader();
35//! let mut fields = reader.fields();
36//! let (_key, _op, value) = fields.next().unwrap();
37//! let array = value.read_array()?;
38//! let actual = array.json().to_string();
39//! let expected = r#"[1,2,3,4]"#;
40//! assert_eq!(&actual, expected);
41//! # Ok(())
42//! # }
43//! ```
44
45use crate::{
46    Encoding, TextToken,
47    text::{ArrayReader, GroupEntry, ObjectReader, Operator, ScalarReader, ValueReader},
48};
49use serde::{
50    Serialize, Serializer,
51    ser::{SerializeMap, SerializeSeq},
52};
53use std::ops::Deref;
54
55/// Customizes the JSON output
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub struct JsonOptions {
58    /// Controls if the JSON should be pretty printed
59    pretty: bool,
60
61    /// Controls the how duplicate keys are formatted
62    duplicate_keys: DuplicateKeyMode,
63
64    /// Controls how values are narrowed to a more specific type
65    type_narrowing: TypeNarrowing,
66}
67
68impl JsonOptions {
69    /// Creates the structure with default options
70    pub fn new() -> Self {
71        JsonOptions::default()
72    }
73
74    /// Sets if the JSON should be pretty printed or minified
75    pub fn with_prettyprint(mut self, pretty: bool) -> JsonOptions {
76        self.pretty = pretty;
77        self
78    }
79
80    /// Sets how duplicate keys are formatted
81    pub fn with_duplicate_keys(mut self, duplicate_keys: DuplicateKeyMode) -> JsonOptions {
82        self.duplicate_keys = duplicate_keys;
83        self
84    }
85
86    /// Sets when a value is attempted to be narrowed to a more specific type
87    pub fn with_type_narrowing(mut self, type_narrowing: TypeNarrowing) -> JsonOptions {
88        self.type_narrowing = type_narrowing;
89        self
90    }
91
92    /// Returns the factor to multiply the token length for a size estimate.
93    ///
94    /// The numbers were found empirically by taking CK3 and EU4 meta data
95    /// from saves and seeing how the JSON output compared with the token length
96    pub(crate) fn output_len_factor(&self) -> usize {
97        match (self.pretty, self.duplicate_keys) {
98            (false, DuplicateKeyMode::Group | DuplicateKeyMode::Preserve) => 10,
99            (true, DuplicateKeyMode::Group | DuplicateKeyMode::Preserve) => 20,
100            (false, DuplicateKeyMode::KeyValuePairs) => 15,
101            (true, DuplicateKeyMode::KeyValuePairs) => 60,
102        }
103    }
104}
105
106impl Default for JsonOptions {
107    fn default() -> Self {
108        Self {
109            pretty: false,
110            duplicate_keys: DuplicateKeyMode::Preserve,
111            type_narrowing: TypeNarrowing::All,
112        }
113    }
114}
115
116/// Controls when a value is attempted to be narrowed to a more specific type
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum TypeNarrowing {
119    /// Attempt to narrow all values to more specific type
120    ///
121    /// ```
122    /// use jomini::{TextTape, json::{JsonOptions, TypeNarrowing}};
123    ///
124    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
125    /// let tape = TextTape::from_slice(br#"a="01" b=02 c="yes" d=no"#)?;
126    /// let reader = tape.windows1252_reader();
127    ///
128    /// let options = JsonOptions::new()
129    ///     .with_type_narrowing(TypeNarrowing::All);
130    ///
131    /// let actual = reader.json()
132    ///     .with_options(options)
133    ///     .to_string();
134    /// assert_eq!(actual, r#"{"a":1,"b":2,"c":true,"d":false}"#);
135    /// # Ok(())
136    /// # }
137    /// ```
138    All,
139
140    /// Only attempt to narrow unquoted values to a more specific type
141    ///
142    /// ```
143    /// use jomini::{TextTape, json::{JsonOptions, TypeNarrowing}};
144    ///
145    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
146    /// let tape = TextTape::from_slice(br#"a="01" b=02 c="yes" d=no"#)?;
147    /// let reader = tape.windows1252_reader();
148    ///
149    /// let options = JsonOptions::new()
150    ///     .with_type_narrowing(TypeNarrowing::Unquoted);
151    ///
152    /// let actual = reader.json()
153    ///     .with_options(options)
154    ///     .to_string();
155    /// assert_eq!(actual, r#"{"a":"01","b":2,"c":"yes","d":false}"#);
156    /// # Ok(())
157    /// # }
158    /// ```
159    Unquoted,
160
161    /// Don't attempt any narrowing (all values will be strings)
162    ///
163    /// ```
164    /// use jomini::{TextTape, json::{JsonOptions, TypeNarrowing}};
165    ///
166    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
167    /// let tape = TextTape::from_slice(br#"a="01" b=02 c="yes" d=no"#)?;
168    /// let reader = tape.windows1252_reader();
169    ///
170    /// let options = JsonOptions::new()
171    ///     .with_type_narrowing(TypeNarrowing::None);
172    ///
173    /// let actual = reader.json()
174    ///     .with_options(options)
175    ///     .to_string();
176    /// assert_eq!(actual, r#"{"a":"01","b":"02","c":"yes","d":"no"}"#);
177    /// # Ok(())
178    /// # }
179    /// ```
180    None,
181}
182
183/// Controls JSON structure when duplicate keys are encountered
184///
185/// It's debatable whether [duplicate keys is valid
186/// JSON](https://stackoverflow.com/q/21832701), so this allows one to customize
187/// output depending how flexible a downstream client is at handling JSON.
188///
189/// The options are either:
190///
191/// - Group values into an array under a single field
192/// - Preserve the duplicate keys
193/// - Rewrite objects as an array of key value pairs
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub enum DuplicateKeyMode {
196    /// Group values into an array under a single field
197    ///
198    /// ```
199    /// use jomini::{TextTape, json::{JsonOptions, DuplicateKeyMode}};
200    ///
201    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
202    /// let tape = TextTape::from_slice(b"a={b=1} c={b=1 b=2}")?;
203    /// let reader = tape.windows1252_reader();
204    ///
205    /// let options = JsonOptions::new()
206    ///     .with_duplicate_keys(DuplicateKeyMode::Group);
207    ///
208    /// let actual = reader.json()
209    ///     .with_options(options)
210    ///     .to_string();
211    /// assert_eq!(actual, r#"{"a":{"b":1},"c":{"b":[1,2]}}"#);
212    /// # Ok(())
213    /// # }
214    /// ```
215    ///
216    /// As shown above, downstream clients will need to be flexible enough to
217    /// handle grouped and ungrouped keys, and may prove cumbersome or
218    /// challenging to leverage automatic deserialization logic. Python or
219    /// Javascript clients may like this output due to their more dynamic typing
220    /// nature.
221    ///
222    /// Grouping keys together will have a small but measurable impact on
223    /// performance
224    Group,
225
226    /// Preserve the duplicate keys (the default behavior)
227    ///
228    /// ```
229    /// use jomini::{TextTape, json::{JsonOptions, DuplicateKeyMode}};
230    ///
231    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
232    /// let tape = TextTape::from_slice(b"a={b=1} c={b=1 b=2}")?;
233    /// let reader = tape.windows1252_reader();
234    ///
235    /// let actual = reader.json()
236    ///     .with_options(JsonOptions::new())
237    ///     .to_string();
238    /// assert_eq!(actual, r#"{"a":{"b":1},"c":{"b":1,"b":2}}"#);
239    /// # Ok(())
240    /// # }
241    /// ```
242    ///
243    /// Preserving duplicate keys is the default mode as it represents the most
244    /// concise output and most closely matches the input.
245    ///
246    /// Insertion order of an object's keys are maintained.
247    ///
248    /// Python and Javascript clients may not like this output as their builtin
249    /// JSON modules don't handle duplicate keys well. However, lower level JSON
250    /// parsers like simd-json tend to handle duplicate keys just fine.
251    Preserve,
252
253    /// Rewrite objects as an array of 2 element arrays (the key and the value).
254    ///
255    /// ```
256    /// use jomini::{TextTape, json::{JsonOptions, DuplicateKeyMode}};
257    ///
258    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
259    /// let tape = TextTape::from_slice(b"c=0 b={1 2}")?;
260    /// let reader = tape.windows1252_reader();
261    ///
262    /// let options = JsonOptions::new()
263    ///     .with_duplicate_keys(DuplicateKeyMode::KeyValuePairs);
264    ///
265    /// let actual = reader.json()
266    ///     .with_options(options)
267    ///     .to_string();
268    /// assert_eq!(actual, r#"{"type":"obj","val":[["c",0],["b",{"type":"array","val":[1,2]}]]}"#);
269    /// # Ok(())
270    /// # }
271    /// ```
272    ///
273    /// Objects and arrays are now transformed into adjacently tagged objects
274    /// (to borrow [a term from
275    /// serde](https://serde.rs/enum-representations.html#adjacently-tagged)).
276    /// Objects have a type of `obj` and arrays have a type of `array`. This
277    /// adjacently tagged object is needed to disambiguate between objects and
278    /// arrays if both are going to be represented with JSON arrays.
279    ///
280    /// This output has the largest departure from the input and is the most
281    /// verbose, but it allows one to use inflexible DOM parsers like those seen
282    /// in Python and Javascript and still maintain the positioning of duplicate
283    /// keys. Preserving positioning is important when interpretting an object
284    /// is dependant on the order of the keys and duplicate keys may affect
285    /// subsequent fields.
286    KeyValuePairs,
287}
288
289fn writer_json<W, S>(writer: W, pretty: bool, ser: S) -> Result<(), std::io::Error>
290where
291    W: std::io::Write,
292    S: serde::Serialize,
293{
294    let result = if pretty {
295        serde_json::to_writer_pretty(writer, &ser)
296    } else {
297        serde_json::to_writer(writer, &ser)
298    };
299
300    result.map_err(|e| e.into())
301}
302
303fn vec_json<F>(mut out: Vec<u8>, write_fn: F) -> Vec<u8>
304where
305    F: FnOnce(&mut Vec<u8>) -> Result<(), std::io::Error>,
306{
307    // Since we control the writer (and writing to a vec shouldn't fail) and
308    // the type that is being serialized, any error that arises from here
309    // would be a programmer error and doesn't need to propagate
310    if let Err(e) = write_fn(&mut out) {
311        panic!("failed to serialize json to vector: {}", e)
312    } else {
313        out
314    }
315}
316
317fn string_json(json: Vec<u8>) -> String {
318    // From serde_json source: "we don't generate invalid utf-8"
319    unsafe { String::from_utf8_unchecked(json) }
320}
321
322/// Creates JSON from an object reader
323pub struct JsonObjectBuilder<'data, 'tokens, E> {
324    reader: ObjectReader<'data, 'tokens, E>,
325    options: JsonOptions,
326}
327
328/// Check if values iterator contains only key-operator-value patterns
329fn is_key_value_pattern<E>(reader: &ArrayReader<'_, '_, E>) -> bool
330where
331    E: Encoding + Clone,
332{
333    let mut values = reader.values();
334    let mut has_key_value_pairs = false;
335
336    while let Some(current) = values.next() {
337        // Skip mixed containers - they don't contribute to key-value patterns
338        if current.token() == &TextToken::MixedContainer {
339            continue;
340        }
341
342        if current.read_scalar().is_err() {
343            return false;
344        }
345
346        let Some(next) = values.next() else {
347            return false;
348        };
349
350        if !matches!(next.token(), TextToken::Operator(_)) {
351            return false;
352        }
353
354        has_key_value_pairs = true;
355        values.next(); // Skip the value part
356    }
357
358    has_key_value_pairs
359}
360
361/// Serialize key-value patterns from an ArrayReader as object entries
362fn serialize_key_value_patterns<S, E>(
363    reader: &ArrayReader<'_, '_, E>,
364    map: &mut <S as Serializer>::SerializeMap,
365    options: JsonOptions,
366) -> Result<(), S::Error>
367where
368    S: Serializer,
369    E: Encoding + Clone,
370{
371    let mut values = reader.values();
372
373    while let Some(key_val) = values.next() {
374        // Skip mixed containers
375        if key_val.token() == &TextToken::MixedContainer {
376            continue;
377        }
378
379        let Ok(key_scalar) = key_val.read_scalar() else {
380            break;
381        };
382
383        let Some(op_val) = values.next() else {
384            break;
385        };
386
387        let TextToken::Operator(op) = op_val.token() else {
388            break;
389        };
390
391        let Some(value_val) = values.next() else {
392            break;
393        };
394
395        let v = OperatorValue {
396            operator: if *op == crate::text::Operator::Equal {
397                None
398            } else {
399                Some(*op)
400            },
401            value: value_val,
402            options,
403        };
404        map.serialize_entry(&key_scalar.to_string(), &v)?;
405    }
406
407    Ok(())
408}
409
410impl<E> JsonObjectBuilder<'_, '_, E>
411where
412    E: Encoding + Clone,
413{
414    /// Output JSON with the set of options
415    pub fn with_options(mut self, options: JsonOptions) -> Self {
416        self.options = options;
417        self
418    }
419
420    /// Output JSON to the given writer
421    pub fn to_writer<W>(self, writer: W) -> Result<(), std::io::Error>
422    where
423        W: std::io::Write,
424    {
425        writer_json(writer, self.options.pretty, &self)
426    }
427
428    /// Output JSON to vec that contains UTF-8 data
429    pub fn to_vec(self) -> Vec<u8> {
430        let out = Vec::with_capacity(self.reader.tokens_len() * self.options.output_len_factor());
431        vec_json(out, |x| self.to_writer(x))
432    }
433
434    /// Output JSON to a string
435    #[allow(clippy::inherent_to_string)]
436    pub fn to_string(self) -> String {
437        string_json(self.to_vec())
438    }
439}
440
441impl<'data, 'tokens, E> ObjectReader<'data, 'tokens, E>
442where
443    E: Encoding + Clone,
444{
445    /// Converts the object to its JSON representation
446    pub fn json(&self) -> JsonObjectBuilder<'data, 'tokens, E> {
447        JsonObjectBuilder {
448            reader: self.clone(),
449            options: JsonOptions::default(),
450        }
451    }
452}
453
454/// Creates JSON from an array reader
455pub struct JsonArrayBuilder<'data, 'tokens, E> {
456    reader: ArrayReader<'data, 'tokens, E>,
457    options: JsonOptions,
458}
459
460impl<E> JsonArrayBuilder<'_, '_, E>
461where
462    E: Encoding + Clone,
463{
464    /// Output JSON with the set of options
465    pub fn with_options(mut self, options: JsonOptions) -> Self {
466        self.options = options;
467        self
468    }
469
470    /// Output JSON to the given writer
471    pub fn to_writer<W>(self, writer: W) -> Result<(), std::io::Error>
472    where
473        W: std::io::Write,
474    {
475        writer_json(writer, self.options.pretty, &self)
476    }
477
478    /// Output JSON to vec that contains UTF-8 data
479    pub fn to_vec(self) -> Vec<u8> {
480        let out = Vec::with_capacity(self.reader.tokens_len() * self.options.output_len_factor());
481        vec_json(out, |x| self.to_writer(x))
482    }
483
484    /// Output JSON to a string
485    #[allow(clippy::inherent_to_string)]
486    pub fn to_string(self) -> String {
487        string_json(self.to_vec())
488    }
489}
490
491impl<'data, 'tokens, E> ArrayReader<'data, 'tokens, E>
492where
493    E: Encoding + Clone,
494{
495    /// Converts the object to its JSON representation
496    pub fn json(&self) -> JsonArrayBuilder<'data, 'tokens, E> {
497        JsonArrayBuilder {
498            reader: self.clone(),
499            options: JsonOptions::default(),
500        }
501    }
502}
503
504/// Creates JSON from a value reader
505pub struct JsonValueBuilder<'data, 'tokens, E> {
506    reader: ValueReader<'data, 'tokens, E>,
507    options: JsonOptions,
508}
509
510impl<E> JsonValueBuilder<'_, '_, E>
511where
512    E: Encoding + Clone,
513{
514    /// Output JSON with the set of options
515    pub fn with_options(mut self, options: JsonOptions) -> Self {
516        self.options = options;
517        self
518    }
519
520    /// Output JSON to the given writer
521    pub fn to_writer<W>(self, writer: W) -> Result<(), std::io::Error>
522    where
523        W: std::io::Write,
524    {
525        writer_json(writer, self.options.pretty, &self)
526    }
527
528    /// Output JSON to vec that contains UTF-8 data
529    pub fn to_vec(self) -> Vec<u8> {
530        let out = Vec::with_capacity(self.reader.tokens_len() * self.options.output_len_factor());
531        vec_json(out, |x| self.to_writer(x))
532    }
533
534    /// Output JSON to a string
535    #[allow(clippy::inherent_to_string)]
536    pub fn to_string(self) -> String {
537        string_json(self.to_vec())
538    }
539}
540
541impl<'data, 'tokens, E> ValueReader<'data, 'tokens, E>
542where
543    E: Encoding + Clone,
544{
545    /// Converts the value to its JSON representation
546    pub fn json(&self) -> JsonValueBuilder<'data, 'tokens, E> {
547        JsonValueBuilder {
548            reader: self.clone(),
549            options: JsonOptions::default(),
550        }
551    }
552}
553
554fn serialize_scalar<E, S>(reader: &ValueReader<E>, s: S) -> Result<S::Ok, S::Error>
555where
556    S: Serializer,
557    E: Encoding + Clone,
558{
559    let scalar = reader.read_scalar().unwrap();
560    if let Ok(x) = scalar.to_bool() {
561        return s.serialize_bool(x);
562    }
563
564    let signed = scalar.to_i64();
565    let unsigned = scalar.to_u64();
566    let float = scalar.to_f64();
567
568    // We only want to serialize numbers that are perfectly representable
569    // with 64 bit floating point, else the value will be stringified
570    match (signed, unsigned, float) {
571        (Ok(x), _, Ok(_)) => s.serialize_i64(x),
572        (_, Ok(x), Ok(_)) => s.serialize_u64(x),
573        (_, _, Ok(f)) => s.serialize_f64(f),
574        _ => s.serialize_str(reader.read_str().unwrap().deref()),
575    }
576}
577
578fn serialize_parameter<S>(body: &str, defined: bool, s: S) -> Result<S::Ok, S::Error>
579where
580    S: Serializer,
581{
582    let mut result = String::with_capacity(body.len() + 3);
583    result.push('[');
584
585    if !defined {
586        result.push('!');
587    }
588
589    result.push_str(body.as_ref());
590    result.push(']');
591    s.serialize_str(&result)
592}
593
594impl<E> Serialize for JsonValueBuilder<'_, '_, E>
595where
596    E: Encoding + Clone,
597{
598    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
599    where
600        S: Serializer,
601    {
602        match self.reader.token() {
603            TextToken::Unquoted(_) if self.options.type_narrowing != TypeNarrowing::None => {
604                serialize_scalar(&self.reader, serializer)
605            }
606            TextToken::Quoted(_) if self.options.type_narrowing == TypeNarrowing::All => {
607                serialize_scalar(&self.reader, serializer)
608            }
609            TextToken::Unquoted(_) | TextToken::Quoted(_) => {
610                serializer.serialize_str(self.reader.read_str().unwrap().deref())
611            }
612            TextToken::Array { .. } => {
613                let array_reader = self.reader.read_array().unwrap();
614                array_reader
615                    .json()
616                    .with_options(self.options)
617                    .serialize(serializer)
618            }
619            TextToken::Object { .. } => {
620                let object_reader = self.reader.read_object().unwrap();
621                object_reader
622                    .json()
623                    .with_options(self.options)
624                    .serialize(serializer)
625            }
626            TextToken::Header(_) => {
627                let arr = self.reader.read_array().unwrap();
628                let mut values = arr.values();
629                let key_reader = values.next().unwrap();
630                let value_reader = values.next().unwrap();
631
632                let mut map = serializer.serialize_map(None)?;
633                map.serialize_entry(
634                    &key_reader.read_str().unwrap(),
635                    &value_reader.json().with_options(self.options),
636                )?;
637                map.end()
638            }
639            TextToken::End(_)
640            | TextToken::Operator(_)
641            | TextToken::Parameter(_)
642            | TextToken::UndefinedParameter(_)
643            | TextToken::MixedContainer => serializer.serialize_none(),
644        }
645    }
646}
647
648impl<E> Serialize for JsonObjectBuilder<'_, '_, E>
649where
650    E: Encoding + Clone,
651{
652    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
653    where
654        S: Serializer,
655    {
656        match self.options.duplicate_keys {
657            DuplicateKeyMode::Group => {
658                let mut field_groups = self.reader.field_groups();
659                let mut map = serializer.serialize_map(None)?;
660
661                for (key, group) in field_groups.by_ref() {
662                    match group {
663                        GroupEntry::One((op, val)) => {
664                            let v = OperatorValue {
665                                operator: op,
666                                value: val,
667                                options: self.options,
668                            };
669                            map.serialize_entry(&KeyScalarWrapper { reader: key }, &v)?;
670                        }
671                        GroupEntry::Multiple(values) => {
672                            let values: Vec<_> = values
673                                .iter()
674                                .map(|(op, val)| OperatorValue {
675                                    operator: *op,
676                                    value: val.clone(),
677                                    options: self.options,
678                                })
679                                .collect();
680                            map.serialize_entry(&KeyScalarWrapper { reader: key }, &values)?;
681                        }
682                    }
683                }
684
685                let rest = field_groups.remainder();
686                if !rest.is_empty() {
687                    map.serialize_entry(
688                        "remainder",
689                        &InnerSerArray {
690                            reader: rest,
691                            options: self.options,
692                        },
693                    )?;
694                }
695
696                map.end()
697            }
698            DuplicateKeyMode::Preserve => {
699                let mut map = serializer.serialize_map(None)?;
700                let mut fields = self.reader.fields();
701
702                // Process all key-value pairs first
703                for (key, op, val) in fields.by_ref() {
704                    let v = OperatorValue {
705                        operator: op,
706                        value: val,
707                        options: self.options,
708                    };
709                    map.serialize_entry(&KeyScalarWrapper { reader: key }, &v)?;
710                }
711
712                // Check if remainder contains object-like key-value pairs
713                let remainder = fields.remainder();
714                if !remainder.is_empty() {
715                    let is_object_like = is_key_value_pattern(&remainder);
716                    if is_object_like {
717                        // Process remainder as key-operator-value patterns
718                        serialize_key_value_patterns::<S, E>(&remainder, &mut map, self.options)?;
719                    } else {
720                        // Use remainder field for non-object-like content
721                        map.serialize_entry(
722                            "remainder",
723                            &InnerSerArray {
724                                reader: remainder,
725                                options: self.options,
726                            },
727                        )?;
728                    }
729                }
730
731                map.end()
732            }
733            DuplicateKeyMode::KeyValuePairs => {
734                let mut map = serializer.serialize_map(None)?;
735                map.serialize_entry("type", "obj")?;
736                map.serialize_entry(
737                    "val",
738                    &SerTapeTyped {
739                        reader: self.reader.clone(),
740                        options: self.options,
741                    },
742                )?;
743                map.end()
744            }
745        }
746    }
747}
748
749pub(crate) struct OperatorValue<'data, 'tokens, E> {
750    operator: Option<Operator>,
751    value: ValueReader<'data, 'tokens, E>,
752    options: JsonOptions,
753}
754
755impl<E> Serialize for OperatorValue<'_, '_, E>
756where
757    E: Encoding + Clone,
758{
759    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
760    where
761        S: Serializer,
762    {
763        match self.operator {
764            Some(Operator::Equal) | None => self
765                .value
766                .json()
767                .with_options(self.options)
768                .serialize(serializer),
769            Some(op) => {
770                let mut map = serializer.serialize_map(None)?;
771                let reader = &self.value;
772                map.serialize_entry(op.name(), &reader.json().with_options(self.options))?;
773                map.end()
774            }
775        }
776    }
777}
778
779pub(crate) struct InnerSerArray<'data, 'tokens, E> {
780    reader: ArrayReader<'data, 'tokens, E>,
781    options: JsonOptions,
782}
783
784impl<E> Serialize for InnerSerArray<'_, '_, E>
785where
786    E: Encoding + Clone,
787{
788    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
789    where
790        S: Serializer,
791    {
792        // Check if this array contains only key-operator-value patterns
793        if is_key_value_pattern(&self.reader) {
794            // Serialize as object instead of array for consistency
795            let mut map = serializer.serialize_map(None)?;
796            serialize_key_value_patterns::<S, E>(&self.reader, &mut map, self.options)?;
797            return map.end();
798        }
799
800        let mut seq = serializer.serialize_seq(None)?;
801        let mut iter = self.reader.values();
802        let mut window = [iter.next(), iter.next(), iter.next()];
803
804        while let Some(first_val) = &window[0] {
805            if first_val.token() == &TextToken::MixedContainer {
806                window.swap(1, 0);
807                window.swap(2, 1);
808                window[2] = iter.next();
809                continue;
810            }
811
812            if let Some(op_reader) = &window[1] {
813                if let TextToken::Operator(op) = op_reader.token() {
814                    if let Some(value) = &window[2] {
815                        seq.serialize_element(&SingleObject {
816                            key: first_val.clone(),
817                            op: *op,
818                            value: value.clone(),
819                            options: self.options,
820                        })?;
821
822                        window = [iter.next(), iter.next(), iter.next()];
823                        continue;
824                    }
825                }
826            }
827
828            let v = OperatorValue {
829                operator: None,
830                value: first_val.clone(),
831                options: self.options,
832            };
833            seq.serialize_element(&v)?;
834
835            window.swap(1, 0);
836            window.swap(2, 1);
837            window[2] = iter.next();
838        }
839
840        seq.end()
841    }
842}
843
844impl<E> Serialize for JsonArrayBuilder<'_, '_, E>
845where
846    E: Encoding + Clone,
847{
848    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
849    where
850        S: Serializer,
851    {
852        let inner = InnerSerArray {
853            reader: self.reader.clone(),
854            options: self.options,
855        };
856
857        if self.options.duplicate_keys != DuplicateKeyMode::KeyValuePairs {
858            inner.serialize(serializer)
859        } else {
860            let mut map = serializer.serialize_map(None)?;
861            map.serialize_entry("type", "array")?;
862            map.serialize_entry("val", &inner)?;
863            map.end()
864        }
865    }
866}
867
868pub(crate) struct SingleObject<'data, 'tokens, E> {
869    key: ValueReader<'data, 'tokens, E>,
870    op: Operator,
871    value: ValueReader<'data, 'tokens, E>,
872    options: JsonOptions,
873}
874
875impl<E> Serialize for SingleObject<'_, '_, E>
876where
877    E: Encoding + Clone,
878{
879    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
880    where
881        S: Serializer,
882    {
883        let mut map = serializer.serialize_map(None)?;
884        let value = OperatorValue {
885            operator: Some(self.op),
886            value: self.value.clone(),
887            options: self.options,
888        };
889
890        if let Ok(x) = self.key.read_str() {
891            map.serialize_key(&x)?;
892        } else {
893            map.serialize_key("__invalid_key")?;
894        }
895
896        map.serialize_value(&value)?;
897        map.end()
898    }
899}
900
901pub(crate) struct SerTapeTyped<'data, 'tokens, E> {
902    reader: ObjectReader<'data, 'tokens, E>,
903    options: JsonOptions,
904}
905
906impl<E> Serialize for SerTapeTyped<'_, '_, E>
907where
908    E: Encoding + Clone,
909{
910    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
911    where
912        S: Serializer,
913    {
914        let mut seq = serializer.serialize_seq(None)?;
915        let mut fields = self.reader.fields();
916        for (key, op, val) in fields.by_ref() {
917            let v = OperatorValue {
918                operator: op,
919                value: val,
920                options: self.options,
921            };
922            seq.serialize_element(&(KeyScalarWrapper { reader: key }, &v))?;
923        }
924
925        let remainder = fields.remainder();
926        if !remainder.is_empty() {
927            let trailer_array = InnerSerArray {
928                reader: remainder,
929                options: self.options,
930            };
931
932            seq.serialize_element(&trailer_array)?;
933        }
934
935        seq.end()
936    }
937}
938
939pub(crate) struct KeyScalarWrapper<'data, E> {
940    reader: ScalarReader<'data, E>,
941}
942
943impl<E> Serialize for KeyScalarWrapper<'_, E>
944where
945    E: Encoding + Clone,
946{
947    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
948    where
949        S: Serializer,
950    {
951        let body = self.reader.read_str();
952        match self.reader.token() {
953            TextToken::Parameter(_) => serialize_parameter(&body, true, serializer),
954            TextToken::UndefinedParameter(_) => serialize_parameter(&body, false, serializer),
955            _ => serializer.serialize_str(&body),
956        }
957    }
958}
959
960#[cfg(test)]
961mod tests {
962    use super::*;
963    use crate::{TextTape, Windows1252Encoding};
964
965    fn serialize_with(data: &[u8], options: JsonOptions) -> String {
966        let tape = TextTape::from_slice(data).unwrap();
967        let reader = tape.windows1252_reader();
968        reader.json().with_options(options).to_string()
969    }
970
971    fn serialize(data: &[u8]) -> String {
972        serialize_with(data, JsonOptions::default())
973    }
974
975    #[test]
976    fn test_serialize_to_json() {
977        let json = serialize(b"foo=bar");
978        assert_eq!(&json, r#"{"foo":"bar"}"#);
979    }
980
981    #[test]
982    fn test_simple_types() {
983        let json = serialize(b"foo=bar num=1 bool=no bool2=yes pi=3.14");
984        let expected = r#"{"foo":"bar","num":1,"bool":false,"bool2":true,"pi":3.14}"#;
985        assert_eq!(&json, expected);
986    }
987
988    #[test]
989    fn test_object() {
990        let json = serialize(b"foo={prop=a bar={num=1}}");
991        let expected = r#"{"foo":{"prop":"a","bar":{"num":1}}}"#;
992        assert_eq!(&json, expected);
993    }
994
995    #[test]
996    fn test_array() {
997        let json = serialize(b"nums={1 2 3 4}");
998        let expected = r#"{"nums":[1,2,3,4]}"#;
999        assert_eq!(&json, expected);
1000    }
1001
1002    #[test]
1003    fn test_duplicate_fields() {
1004        let json = serialize(b"core=AAA core=BBB");
1005        let expected = r#"{"core":"AAA","core":"BBB"}"#;
1006        assert_eq!(&json, expected);
1007    }
1008
1009    #[test]
1010    fn test_duplicate_fields_grouped() {
1011        let json = serialize_with(
1012            b"core=AAA core=BBB",
1013            JsonOptions {
1014                duplicate_keys: DuplicateKeyMode::Group,
1015                ..JsonOptions::default()
1016            },
1017        );
1018        let expected = r#"{"core":["AAA","BBB"]}"#;
1019        assert_eq!(&json, expected);
1020    }
1021
1022    #[test]
1023    fn test_duplicate_fields_typed() {
1024        let json = serialize_with(
1025            b"core=AAA core=BBB",
1026            JsonOptions {
1027                duplicate_keys: DuplicateKeyMode::KeyValuePairs,
1028                ..JsonOptions::default()
1029            },
1030        );
1031        let expected = r#"{"type":"obj","val":[["core","AAA"],["core","BBB"]]}"#;
1032        assert_eq!(&json, expected);
1033    }
1034
1035    #[test]
1036    fn test_header() {
1037        let json = serialize(b"color = rgb { 100 200 150 }");
1038        let expected = r#"{"color":{"rgb":[100,200,150]}}"#;
1039        assert_eq!(&json, expected);
1040    }
1041
1042    #[test]
1043    fn test_large_numbers() {
1044        let json = serialize(b"identity = 18446744073709547616");
1045        let expected = r#"{"identity":"18446744073709547616"}"#;
1046        assert_eq!(&json, expected);
1047    }
1048
1049    #[test]
1050    fn test_large_negative_numbers() {
1051        let json = serialize(b"identity = -90071992547409097");
1052        let expected = r#"{"identity":"-90071992547409097"}"#;
1053        assert_eq!(&json, expected);
1054    }
1055
1056    #[test]
1057    fn test_object_pretty() {
1058        let json = serialize_with(
1059            b"foo={prop=a bar={num=1}}",
1060            JsonOptions {
1061                pretty: true,
1062                ..JsonOptions::default()
1063            },
1064        );
1065        let expected = r#"{
1066  "foo": {
1067    "prop": "a",
1068    "bar": {
1069      "num": 1
1070    }
1071  }
1072}"#;
1073        assert_eq!(&json, expected);
1074    }
1075
1076    #[test]
1077    fn test_array_typed() {
1078        let json = serialize_with(
1079            b"nums={1 2}",
1080            JsonOptions {
1081                duplicate_keys: DuplicateKeyMode::KeyValuePairs,
1082                ..JsonOptions::default()
1083            },
1084        );
1085        let expected = r#"{"type":"obj","val":[["nums",{"type":"array","val":[1,2]}]]}"#;
1086        assert_eq!(&json, expected);
1087    }
1088
1089    #[test]
1090    fn test_mixed_container_1() {
1091        let json = serialize(b"area = { color = { 10 } 1 2 }");
1092        let expected = r#"{"area":{"color":[10],"remainder":[1,2]}}"#;
1093        assert_eq!(&json, expected);
1094    }
1095
1096    #[test]
1097    fn test_mixed_container_2() {
1098        let json = serialize_with(
1099            b"area = { color = { 10 } 1 2 }",
1100            JsonOptions {
1101                duplicate_keys: DuplicateKeyMode::Group,
1102                ..JsonOptions::default()
1103            },
1104        );
1105        let expected = r#"{"area":{"color":[10],"remainder":[1,2]}}"#;
1106        assert_eq!(&json, expected);
1107    }
1108
1109    #[test]
1110    fn test_mixed_container_3() {
1111        let json = serialize_with(
1112            b"area = { color = { 10 } 1 2 }",
1113            JsonOptions {
1114                duplicate_keys: DuplicateKeyMode::KeyValuePairs,
1115                ..JsonOptions::default()
1116            },
1117        );
1118        let expected = r#"{"type":"obj","val":[["area",{"type":"obj","val":[["color",{"type":"array","val":[10]}],[1,2]]}]]}"#;
1119        assert_eq!(&json, expected);
1120    }
1121
1122    #[test]
1123    fn test_mixed_container_4() {
1124        let json = serialize(b"levels={ 10 0=2 1=2 }");
1125        let expected = r#"{"levels":[10,{"0":2},{"1":2}]}"#;
1126        assert_eq!(&json, expected);
1127    }
1128
1129    #[test]
1130    fn test_mixed_container_5() {
1131        let json = serialize(b"mixed={ a=b 10 c=d 20 }");
1132        let expected = r#"{"mixed":{"a":"b","remainder":[10,{"c":"d"},20]}}"#;
1133        assert_eq!(&json, expected);
1134    }
1135
1136    #[test]
1137    fn test_mixed_container_10() {
1138        let json = serialize(
1139            br"on_actions = {
1140            faith_holy_order_land_acquisition_pulse
1141            delay = { days = { 5 10 }}
1142            faith_heresy_events_pulse
1143            delay = { days = { 15 20 }}
1144            faith_fervor_events_pulse
1145          }",
1146        );
1147        let expected = r#"{"on_actions":["faith_holy_order_land_acquisition_pulse",{"delay":{"days":[5,10]}},"faith_heresy_events_pulse",{"delay":{"days":[15,20]}},"faith_fervor_events_pulse"]}"#;
1148        assert_eq!(&json, expected);
1149    }
1150
1151    #[test]
1152    fn test_parameter_definitions_typed() {
1153        let json = serialize_with(
1154            b"generate_advisor = { [[scaled_skill] a=b ] [[!scaled_skill] c=d ]  }",
1155            JsonOptions {
1156                duplicate_keys: DuplicateKeyMode::KeyValuePairs,
1157                ..JsonOptions::default()
1158            },
1159        );
1160        let expected = r#"{"type":"obj","val":[["generate_advisor",{"type":"obj","val":[["[scaled_skill]",{"type":"obj","val":[["a","b"]]}],["[!scaled_skill]",{"type":"obj","val":[["c","d"]]}]]}]]}"#;
1161        assert_eq!(&json, expected);
1162    }
1163
1164    #[test]
1165    fn test_parameter_definition_value_typed() {
1166        let json = serialize_with(
1167            b"foo = { [[add] $add$]}",
1168            JsonOptions {
1169                duplicate_keys: DuplicateKeyMode::KeyValuePairs,
1170                ..JsonOptions::default()
1171            },
1172        );
1173        let expected = r#"{"type":"obj","val":[["foo",{"type":"obj","val":[["[add]","$add$"]]}]]}"#;
1174        assert_eq!(&json, expected);
1175    }
1176
1177    #[test]
1178    fn test_subobject() {
1179        let tape = TextTape::from_slice(b"bar={num=1}").unwrap();
1180        let reader = tape.windows1252_reader();
1181        let mut fields = reader.fields();
1182        let (_key, _op, value) = fields.next().unwrap();
1183        let actual = value.json().to_string();
1184        let expected = r#"{"num":1}"#;
1185        assert_eq!(&actual, expected);
1186    }
1187
1188    #[test]
1189    fn test_array_direct() {
1190        let tape = TextTape::from_slice(b"nums={1 2 3 4}").unwrap();
1191        let reader = tape.windows1252_reader();
1192        let mut fields = reader.fields();
1193        let (_key, _op, value) = fields.next().unwrap();
1194        let array = value.read_array().unwrap();
1195        let actual = array.json().to_string();
1196        let expected = r#"[1,2,3,4]"#;
1197        assert_eq!(&actual, expected);
1198    }
1199
1200    #[test]
1201    fn test_value_direct() {
1202        let tape = TextTape::from_slice(b"core=1").unwrap();
1203        let reader = tape.windows1252_reader();
1204        let mut fields = reader.fields();
1205        let (_key, _op, value) = fields.next().unwrap();
1206        let actual = value.json().to_string();
1207        let expected = r#"1"#;
1208        assert_eq!(&actual, expected);
1209    }
1210
1211    #[test]
1212    fn test_builder_serialization() {
1213        #[derive(Serialize)]
1214        struct MyStruct<'a, 'b> {
1215            bar: JsonValueBuilder<'a, 'b, Windows1252Encoding>,
1216            qux: JsonValueBuilder<'a, 'b, Windows1252Encoding>,
1217        }
1218
1219        let tape = TextTape::from_slice(b"bar={num=1} qux={num=2}").unwrap();
1220        let reader = tape.windows1252_reader();
1221        let mut fields = reader.fields();
1222        let (_key, _op, value) = fields.next().unwrap();
1223        let bar = value.json();
1224
1225        let (_key, _op, value) = fields.next().unwrap();
1226        let qux = value.json();
1227
1228        let actual = serde_json::to_string(&MyStruct { bar, qux }).unwrap();
1229        let expected = r#"{"bar":{"num":1},"qux":{"num":2}}"#;
1230        assert_eq!(&actual, expected);
1231    }
1232
1233    #[test]
1234    fn test_object_template_basic() {
1235        let json = serialize(b"obj={ { a = b }={ 1 2 3 } }");
1236        let expected = r#"{"obj":[{"a":"b"},[1,2,3]]}"#;
1237        assert_eq!(&json, expected);
1238    }
1239
1240    #[test]
1241    fn test_object_template_single_value() {
1242        let json = serialize(b"obj={ { foo }={ 1000 } }");
1243        let expected = r#"{"obj":[["foo"],[1000]]}"#;
1244        assert_eq!(&json, expected);
1245    }
1246
1247    #[test]
1248    fn test_object_template_sequential() {
1249        let json = serialize(b"obj={ { template1 }={ values1 } { template2 }={ values2 } }");
1250        let expected = r#"{"obj":[["template1"],["values1"],["template2"],["values2"]]}"#;
1251        assert_eq!(&json, expected);
1252    }
1253
1254    #[test]
1255    fn test_object_template_with_preserve_duplicates() {
1256        let json = serialize_with(
1257            b"obj={ { a = b }={ 1 2 3 } }",
1258            JsonOptions {
1259                duplicate_keys: DuplicateKeyMode::Preserve,
1260                ..JsonOptions::default()
1261            },
1262        );
1263        let expected = r#"{"obj":[{"a":"b"},[1,2,3]]}"#;
1264        assert_eq!(&json, expected);
1265    }
1266
1267    #[test]
1268    fn test_object_template_with_key_value_pairs() {
1269        let json = serialize_with(
1270            b"obj={ { a = b }={ 1 2 3 } }",
1271            JsonOptions {
1272                duplicate_keys: DuplicateKeyMode::KeyValuePairs,
1273                ..JsonOptions::default()
1274            },
1275        );
1276        let expected = r#"{"type":"obj","val":[["obj",{"type":"array","val":[{"type":"obj","val":[["a","b"]]},{"type":"array","val":[1,2,3]}]}]]}"#;
1277        assert_eq!(&json, expected);
1278    }
1279
1280    #[test]
1281    fn test_object_template_complex() {
1282        let json = serialize(
1283            b"config={ { debug=yes logging=verbose }={ output_file=\"debug.log\" level=trace } }",
1284        );
1285        let expected = r#"{"config":[{"debug":true,"logging":"verbose"},{"output_file":"debug.log","level":"trace"}]}"#;
1286        assert_eq!(&json, expected);
1287    }
1288
1289    #[test]
1290    fn test_object_template_nested() {
1291        let json = serialize(b"obj={ { outer }={ { inner }={ deep } } }");
1292        let expected = r#"{"obj":[["outer"],[["inner"],["deep"]]]}"#;
1293        assert_eq!(&json, expected);
1294    }
1295
1296    #[test]
1297    fn test_object_template_pretty() {
1298        let json = serialize_with(
1299            b"obj={ { a = b }={ 1 2 3 } }",
1300            JsonOptions {
1301                pretty: true,
1302                ..JsonOptions::default()
1303            },
1304        );
1305        let expected = r#"{
1306  "obj": [
1307    {
1308      "a": "b"
1309    },
1310    [
1311      1,
1312      2,
1313      3
1314    ]
1315  ]
1316}"#;
1317        assert_eq!(&json, expected);
1318    }
1319
1320    #[test]
1321    fn test_object_template_scalar_value() {
1322        let json = serialize(b"obj={ { 31=16 }=16 }");
1323        let expected = r#"{"obj":[{"31":16},16]}"#;
1324        assert_eq!(&json, expected);
1325    }
1326
1327    #[test]
1328    fn test_equals_operator_mutation() {
1329        use crate::text::{Operator, TextToken};
1330
1331        // Parse "foo ?= 10" with exists operator
1332        let mut tape = TextTape::from_slice(b"foo ?= 10").unwrap();
1333
1334        // Verify the original JSON output with exists operator
1335        let reader_before = tape.windows1252_reader();
1336        let json_before = reader_before.json().to_string();
1337        let expected_before = r#"{"foo":{"EXISTS":10}}"#;
1338        assert_eq!(&json_before, expected_before);
1339
1340        // Find and replace the exists operator with an equals operator in-place
1341        let tokens = tape.tokens_mut();
1342        for token in tokens.iter_mut() {
1343            if let TextToken::Operator(Operator::Exists) = token {
1344                *token = TextToken::Operator(Operator::Equal);
1345                break;
1346            }
1347        }
1348
1349        // Verify the JSON output after in-place modification
1350        let reader_after = tape.windows1252_reader();
1351        let json_after = reader_after.json().to_string();
1352        let expected_after = r#"{"foo":10}"#;
1353        assert_eq!(&json_after, expected_after);
1354    }
1355
1356    #[test]
1357    fn test_mixed_array_remains_array() {
1358        // Test case: mixed arrays should remain arrays
1359        let json = serialize(b"test={ a=1 standalone b=2 }");
1360        assert_eq!(
1361            &json,
1362            "{\"test\":{\"a\":1,\"remainder\":[\"standalone\",{\"b\":2}]}}"
1363        );
1364    }
1365
1366    #[test]
1367    fn test_empty_containers() {
1368        let json = serialize(b"empty={}");
1369        assert_eq!(&json, "{\"empty\":[]}");
1370    }
1371
1372    #[test]
1373    fn test_single_key_value_pair() {
1374        let json = serialize(b"single={ key=value }");
1375        assert_eq!(&json, "{\"single\":{\"key\":\"value\"}}");
1376    }
1377
1378    #[test]
1379    fn test_operators_other_than_equal() {
1380        // Test various operators
1381        let json = serialize(b"test={ a>1 b<2 c>=3 d<=4 e!=5 }");
1382        assert_eq!(&json, "{\"test\":{\"a\":{\"GREATER_THAN\":1},\"b\":{\"LESS_THAN\":2},\"c\":{\"GREATER_THAN_EQUAL\":3},\"d\":{\"LESS_THAN_EQUAL\":4},\"e\":{\"NOT_EQUAL\":5}}}");
1383    }
1384
1385    #[test]
1386    fn test_complex_nested_structures() {
1387        let json =
1388            serialize(b"complex={ nested={ a=1 b=2 } standalone={ c=3 } mixed={ d=4 bare e=5 } }");
1389        assert_eq!(&json, "{\"complex\":{\"nested\":{\"a\":1,\"b\":2},\"standalone\":{\"c\":3},\"mixed\":{\"d\":4,\"remainder\":[\"bare\",{\"e\":5}]}}}");
1390    }
1391
1392    #[test]
1393    fn test_stress_alternating_patterns() {
1394        let json = serialize(b"test={ a=1 standalone1 b=2 standalone2 c=3 }");
1395        assert_eq!(&json, "{\"test\":{\"a\":1,\"remainder\":[\"standalone1\",{\"b\":2},\"standalone2\",{\"c\":3}]}}");
1396    }
1397
1398    #[test]
1399    fn test_preserve_vs_group_mode_consistency() {
1400        let data = b"test={ scope:treaty != this scope:county = yes }";
1401
1402        let preserve_json = serialize_with(data, JsonOptions::new());
1403        let group_json = serialize_with(
1404            data,
1405            JsonOptions::new().with_duplicate_keys(DuplicateKeyMode::Group),
1406        );
1407        assert_eq!(
1408            preserve_json,
1409            "{\"test\":{\"scope:treaty\":{\"NOT_EQUAL\":\"this\"},\"scope:county\":true}}"
1410        );
1411        assert_eq!(
1412            group_json,
1413            "{\"test\":{\"scope:treaty\":{\"NOT_EQUAL\":\"this\"},\"scope:county\":true}}"
1414        );
1415    }
1416
1417    #[test]
1418    fn test_field_order_encoding_consistency() {
1419        let a_trigger = br#"a_trigger = {
1420    OR = {
1421        obj_a = {
1422                a_bool = no
1423        }
1424        obj_a.field != this
1425    }
1426}"#;
1427
1428        let b_trigger = br#"b_trigger = {
1429    OR = {
1430        obj_a.field != this
1431        obj_a = {
1432                a_bool = no
1433        }
1434    }
1435}"#;
1436
1437        let a_json = serialize(a_trigger);
1438        let b_json = serialize(b_trigger);
1439
1440        assert_eq!(
1441            a_json,
1442            "{\"a_trigger\":{\"OR\":{\"obj_a\":{\"a_bool\":false},\"obj_a.field\":{\"NOT_EQUAL\":\"this\"}}}}"
1443        );
1444        assert_eq!(
1445            b_json,
1446            "{\"b_trigger\":{\"OR\":{\"obj_a.field\":{\"NOT_EQUAL\":\"this\"},\"obj_a\":{\"a_bool\":false}}}}"
1447        );
1448    }
1449}