json_compare/
lib.rs

1/*
2 * code taken from https://github.com/marvindv/jsonlogic_rs/blob/master/src/operators/logic.rs
3 */
4use serde_json::{Number, Value};
5
6/// See http://jsonlogic.com/truthy.html
7pub fn is_truthy(value: &Value) -> bool {
8    match value {
9        Value::Array(arr) => !arr.is_empty(),
10        Value::Bool(b) => *b,
11        Value::Null => false,
12        Value::Number(num) => num.as_f64().unwrap() != 0f64,
13        Value::Object(_) => true,
14        Value::String(s) => s != "",
15    }
16}
17
18/// See https://www.ecma-international.org/ecma-262/#sec-strict-equality-comparison
19pub fn is_strict_equal(a: &Value, b: &Value) -> bool {
20    use Value::*;
21
22    match (a, b) {
23        (Array(_), Array(_)) => false,
24        (Bool(a), Bool(b)) => a == b,
25        (Null, Null) => true,
26        (Number(a), Number(b)) => equal_numbers(a, b),
27        (Object(_), Object(_)) => false,
28        (String(a), String(b)) => a == b,
29        _ => false,
30    }
31}
32
33// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
34// and https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison
35#[allow(clippy::float_cmp)]
36pub fn is_abstract_equal(a: &Value, b: &Value) -> bool {
37    use Value::*;
38
39    match (a, b) {
40        // 1. Strict equal for same types.
41        (Array(_), Array(_))
42        | (Bool(_), Bool(_))
43        | (Null, Null)
44        | (Number(_), Number(_))
45        | (Object(_), Object(_))
46        | (String(_), String(_)) => is_strict_equal(a, b),
47        // short-circuit only one operand being null
48        (Null, _) | (_, Null) => false,
49        // 4. If Type(a) is number and Type(b) is string, return a == ToNumber(b).
50        (Number(a), String(_)) => coerce_to_f64(b)
51            .map(|b| a.as_f64().unwrap() == b)
52            .unwrap_or(false),
53        // 5. If Type(a) is string and Type(b) is number, return ToNumber(a) == b.
54        (String(_), Number(b)) => coerce_to_f64(a)
55            .map(|a| a == b.as_f64().unwrap())
56            .unwrap_or(false),
57        // 6. If Type(a) is bool return ToNumber(a)==b
58        (Bool(_), _) => coerce_to_f64(a)
59            .map(|a| is_abstract_equal(&Value::Number(serde_json::Number::from_f64(a).unwrap()), b))
60            .unwrap_or(false),
61        // 7. If Type(b) is bool return a==ToNumber(b)
62        (_, Bool(_)) => coerce_to_f64(b)
63            .map(|b| is_abstract_equal(a, &Value::Number(serde_json::Number::from_f64(b).unwrap())))
64            .unwrap_or(false),
65        // 8. something with object
66        // if non array object:
67        //   An object is never equal to something else, including another object, since
68        //   ToPrimitive(object) does not work for json.
69        (Object(_), _) | (_, Object(_)) => false,
70        // if array:
71        //   the only possible operand types that are still possible are Number and String
72        (String(a), Array(b)) | (Array(b), String(a)) => a == &arr_to_primitive_str(b),
73        (Number(_), Array(b)) => is_abstract_equal(a, &Value::String(arr_to_primitive_str(b))),
74        (Array(a), Number(_)) => is_abstract_equal(&Value::String(arr_to_primitive_str(a)), b),
75    }
76}
77
78// See https://www.ecma-international.org/ecma-262/#sec-abstract-relational-comparison
79pub fn less_than(a: &Value, b: &Value) -> bool {
80    use Value::*;
81
82    match (a, b) {
83        (Null, Null) => false,
84        (Bool(false), Bool(true)) => true,
85        (Bool(_), Bool(_)) => false,
86        (Object(_), _) | (_, Object(_)) => false,
87        (String(a), String(b)) => a < b,
88        // Combinations where both operands will be coerced to strings:
89        //   Arrays will be converted to a primitive (i.e. a string). (1.)
90        //   Strings will be compared lexically. (3.)
91        (Array(_), Array(_)) | (Array(_), String(_)) | (String(_), Array(_)) => {
92            coerce_to_str(a) < coerce_to_str(b)
93        }
94        // Combinations where both operands will be coerced to numbers:
95        //   In every other combination the operands will be converted to numbers in the end. (4.)
96        (Null, _) | (_, Null) | (Number(_), _) | (_, Number(_)) | (Bool(_), _) | (_, Bool(_)) => {
97            match (coerce_to_f64(a), coerce_to_f64(b)) {
98                (Some(a), Some(b)) => a < b,
99                _ => false,
100            }
101        }
102    }
103}
104
105pub fn less_equal_than(a: &Value, b: &Value) -> bool {
106    less_than(a, b) || is_abstract_equal(a, b)
107}
108
109pub fn greater_than(a: &Value, b: &Value) -> bool {
110    !less_equal_than(a, b)
111}
112
113pub fn greater_equal_than(a: &Value, b: &Value) -> bool {
114    !less_than(a, b)
115}
116
117/// The javascript operation `String(val)`.
118pub fn coerce_to_str(val: &Value) -> String {
119    match val {
120        Value::Array(arr) => arr_to_primitive_str(arr),
121        Value::Bool(b) => b.to_string(),
122        Value::Null => String::from("null"),
123        Value::Number(num) => num.to_string(),
124        Value::Object(_) => String::from("[object Object]"),
125        Value::String(s) => s.to_string(),
126    }
127}
128
129/// `Number(val)` in javascript or as named in the standard `ToNumber(val)`.
130pub fn coerce_to_f64(val: &Value) -> Option<f64> {
131    match val {
132        Value::Array(arr) => match &arr[..] {
133            [] => Some(0f64),
134            // I don't really understand why Number([true]) is NaN but thats the way it is.
135            [el] => match el {
136                Value::Array(_) | Value::Null | Value::Number(_) | Value::String(_) => {
137                    coerce_to_f64(el)
138                }
139                _ => None,
140            },
141            _ => None,
142        },
143        Value::Bool(true) => Some(1f64),
144        Value::Bool(false) => Some(0f64),
145        Value::Null => Some(0f64),
146        Value::Number(num) => num.as_f64(),
147        Value::Object(_) => None,
148        Value::String(s) => {
149            let s = s.trim();
150            if s == "" {
151                Some(0f64)
152            } else {
153                s.parse::<f64>().ok()
154            }
155        }
156    }
157}
158
159/// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
160/// parseFloat is a top-level function and not a method of any object.
161///     - If parseFloat encounters a character other than a plus sign (+), minus sign
162///       (- U+002D HYPHEN-MINUS), numeral (0–9), decimal point (.), or exponent (e or E), it
163///       returns the value up to that character, ignoring the invalid character and characters
164///       following it.
165///     - A second decimal point also stops parsing (characters up to that point will still be
166///       parsed).
167///     - Leading and trailing spaces in the argument are ignored.
168///     - If the argument’s first character can’t be converted to a number (it’s not any of the
169///       above characters), parseFloat returns NaN.
170///     - parseFloat can also parse and return Infinity.
171///     - parseFloat converts BigInt syntax to Numbers, losing precision. This happens because the
172///       trailing n character is discarded.
173///
174/// This function does not support BigInt syntax, since JSON does not support it.
175pub fn parse_float(val: &Value) -> Option<f64> {
176    match val {
177        Value::Number(num) => Some(num.as_f64().unwrap()),
178        Value::String(s) => {
179            let s = s.trim();
180            let mut end = 0;
181            // Keeping track of decimal point presence, since the parsing stops on a second one.
182            let mut has_decimal_point = false;
183            for ch in s.chars() {
184                match ch {
185                    '+' | '-' | '0'..='9' | 'e' | 'E' => end += 1,
186                    '.' => {
187                        if has_decimal_point {
188                            break;
189                        } else {
190                            end += 1;
191                            has_decimal_point = true;
192                        }
193                    }
194                    _ => break,
195                }
196            }
197
198            let parsed = &s[0..end];
199            parsed.parse::<f64>().ok()
200        }
201        _ => None,
202    }
203}
204
205#[allow(clippy::float_cmp)]
206fn equal_numbers(a: &Number, b: &Number) -> bool {
207    // Avoid float compare if possible.
208    if a.is_u64() && b.is_u64() {
209        a.as_u64().unwrap() == b.as_u64().unwrap()
210    } else if a.is_i64() && b.is_i64() {
211        a.as_i64().unwrap() == b.as_i64().unwrap()
212    } else {
213        a.as_f64().unwrap() == b.as_f64().unwrap()
214    }
215}
216
217// In the end this is 7.1.1.1 OrdinaryToPrimitive for array(-objects)
218// from https://www.ecma-international.org/ecma-262/#sec-ordinarytoprimitive with hint=number
219// but in the end it boils down to arr.toString()
220fn arr_to_primitive_str(arr: &[Value]) -> String {
221    arr.iter()
222        .map(|el| coerce_to_str(el))
223        .collect::<Vec<String>>()
224        .join(",")
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230    use serde_json::json;
231
232    mod strict_equal {
233        use super::*;
234
235        macro_rules! test_strict_equal {
236            ($a:expr, $b:expr) => {
237                assert!(is_strict_equal(&json!($a), &json!($b)));
238                assert!(is_strict_equal(&json!($b), &json!($a)));
239            };
240        }
241
242        macro_rules! test_strict_not_equal {
243            ($a:expr, $b:expr) => {
244                assert!(!is_strict_equal(&json!($a), &json!($b)));
245                assert!(!is_strict_equal(&json!($b), &json!($a)));
246            };
247        }
248
249        #[test]
250        fn same_type_string() {
251            test_strict_equal!("", "");
252            test_strict_not_equal!(" ", "");
253            test_strict_equal!("a", "a");
254            test_strict_not_equal!("a", "b");
255        }
256
257        #[test]
258        fn same_type_number() {
259            test_strict_equal!(0, 0);
260            test_strict_equal!(0, 0.0);
261            test_strict_equal!(-0, 0);
262            test_strict_not_equal!(-1, 1);
263            test_strict_not_equal!(1.1, 1);
264        }
265
266        #[test]
267        fn same_type_bool() {
268            test_strict_equal!(true, true);
269            test_strict_equal!(false, false);
270            test_strict_not_equal!(false, true);
271        }
272
273        #[test]
274        fn same_type_object() {
275            assert!(!is_strict_equal(&json!({}), &json!({}),));
276            assert!(!is_strict_equal(
277                &json!({"foo": "bar"}),
278                &json!({"foo": "bar"}),
279            ));
280            assert!(!is_strict_equal(
281                &json!({"foo": "bar"}),
282                &json!({"foo":  1}),
283            ));
284        }
285
286        #[test]
287        fn same_type_array() {
288            assert!(!is_strict_equal(&json!([]), &json!([])));
289            assert!(!is_strict_equal(&json!([1]), &json!([1])));
290            assert!(!is_strict_equal(&json!([1]), &json!([2])));
291        }
292
293        #[test]
294        fn different_type() {
295            test_strict_not_equal!("", 0);
296            test_strict_not_equal!("1", 1);
297            test_strict_not_equal!("true", true);
298            test_strict_not_equal!(1, true);
299            assert!(!is_strict_equal(&json!(null), &json!(false)));
300        }
301    }
302
303    mod abstract_equal {
304        use super::*;
305
306        macro_rules! test_abstract_equal {
307            ($a:expr, $b:expr) => {
308                assert!(is_abstract_equal(&json!($a), &json!($b)));
309                assert!(is_abstract_equal(&json!($b), &json!($a)));
310            };
311        }
312        macro_rules! test_abstract_not_equal {
313            ($a:expr, $b:expr) => {
314                assert!(!is_abstract_equal(&json!($a), &json!($b)),);
315                assert!(!is_abstract_equal(&json!($b), &json!($a)),);
316            };
317        }
318        #[test]
319        fn loose_equal_same_type() {
320            test_abstract_equal!(Value::Null, Value::Null);
321            test_abstract_equal!(true, true);
322            test_abstract_equal!(false, false);
323            test_abstract_equal!("foo", "foo");
324            test_abstract_equal!(0, 0);
325            test_abstract_equal!(0, -0);
326            test_abstract_equal!(0, 0.0);
327            test_abstract_equal!(0.2, 0.2);
328        }
329        #[test]
330        fn loose_equal_diff_type() {
331            test_abstract_equal!([1, 2], "1,2");
332        }
333        #[test]
334        fn loose_not_equal() {
335            test_abstract_not_equal!(0, &Value::Null);
336        }
337        #[test]
338        fn number_boolean() {
339            test_abstract_equal!(-0, false);
340            test_abstract_equal!(0, false);
341            test_abstract_equal!(1, true);
342            test_abstract_equal!(1.0, true);
343            test_abstract_not_equal!(-1, true);
344            test_abstract_not_equal!(0.1 + 0.2, false);
345        }
346        #[test]
347        fn number_string() {
348            test_abstract_equal!("", 0);
349            test_abstract_equal!("0", 0);
350            test_abstract_equal!("-0", 0);
351            test_abstract_equal!("+0", 0);
352            test_abstract_equal!("0.0", 0);
353            test_abstract_equal!("+0.0", 0);
354            test_abstract_equal!("-0.0", 0);
355            test_abstract_equal!("17", 17);
356            test_abstract_equal!("-17", -17);
357            test_abstract_equal!("   1 ", 1);
358            test_abstract_equal!("   1.3 ", 1.3);
359        }
360        #[test]
361        fn array_bool() {
362            test_abstract_equal!([1], true);
363            test_abstract_not_equal!([true], true);
364        }
365        #[test]
366        fn string_bool() {
367            test_abstract_equal!("", false);
368            test_abstract_equal!("  ", false);
369            test_abstract_equal!("0", false);
370            test_abstract_equal!("  0 ", false);
371            test_abstract_equal!("1", true);
372            test_abstract_equal!(" 1  ", true);
373        }
374        #[test]
375        fn number_array() {
376            test_abstract_equal!([1], 1);
377            test_abstract_equal!([1.2], 1.2);
378        }
379    }
380
381    mod test_less_than {
382        use super::*;
383
384        macro_rules! less_than {
385            ($a:expr, $b:expr, $result:expr) => {
386                assert_eq!(less_than(&json!($a), &json!($b)), $result);
387            };
388        }
389
390        #[test]
391        fn same_type() {
392            // number < number
393            assert_eq!(less_than(&json!(1), &json!(2)), true);
394            assert_eq!(less_than(&json!(2), &json!(2)), false);
395            assert_eq!(less_than(&json!(3), &json!(2)), false);
396
397            // string < string
398            assert_eq!(less_than(&json!("a"), &json!("b")), true);
399            assert_eq!(less_than(&json!("b"), &json!("b")), false);
400            assert_eq!(less_than(&json!("c"), &json!("b")), false);
401
402            // null < null
403            assert_eq!(less_than(&json!(null), &json!(null)), false);
404
405            // bool < bool
406            assert_eq!(less_than(&json!(false), &json!(true)), true);
407            assert_eq!(less_than(&json!(true), &json!(false)), false);
408            assert_eq!(less_than(&json!(true), &json!(true)), false);
409            assert_eq!(less_than(&json!(false), &json!(false)), false);
410        }
411
412        #[test]
413        fn number_string() {
414            // number < string, string is casted to number
415            assert_eq!(less_than(&json!(1), &json!("b")), false);
416            assert_eq!(less_than(&json!(1), &json!("1")), false);
417            assert_eq!(less_than(&json!(-1), &json!("")), true);
418            assert_eq!(less_than(&json!(1), &json!("12")), true);
419
420            // string < number, string is casted to number
421            assert_eq!(less_than(&json!("b"), &json!(1)), false);
422            assert_eq!(less_than(&json!("1"), &json!(1)), false);
423            assert_eq!(less_than(&json!(""), &json!(-1)), false);
424            assert_eq!(less_than(&json!("12"), &json!(1)), false);
425        }
426
427        #[test]
428        fn array_number() {
429            // array < number, cast array to number
430            assert_eq!(less_than(&json!([1]), &json!(12)), true);
431            assert_eq!(less_than(&json!([2]), &json!(12)), true);
432            assert_eq!(less_than(&json!([[2]]), &json!(12)), true);
433            assert_eq!(less_than(&json!([[2], 3]), &json!(12)), false);
434
435            // number < array, cast array to number
436            assert_eq!(less_than(&json!(1), &json!([12])), true);
437            assert_eq!(less_than(&json!(2), &json!([12])), true);
438            assert_eq!(less_than(&json!(2), &json!([[12]])), true);
439            assert_eq!(less_than(&json!(2), &json!([10, [12]])), false);
440        }
441
442        #[test]
443        fn multi_elem_arrays() {
444            // Multiple element arrays are converted to string and lexicographically compared.
445            assert_eq!(less_than(&json!([1, 2]), &json!([3, 4])), true);
446            assert_eq!(less_than(&json!([3, 4]), &json!([1, 2])), false);
447            assert_eq!(less_than(&json!([1, 2, 2]), &json!([2, 2])), true);
448        }
449
450        #[test]
451        fn bool_number() {
452            // bool < number, bool is converted to number
453            assert_eq!(less_than(&json!(false), &json!(1)), true);
454            assert_eq!(less_than(&json!(true), &json!(1)), false);
455            assert_eq!(less_than(&json!(true), &json!(2)), true);
456
457            // number < bool, bool is converted to number
458            assert_eq!(less_than(&json!(-1), &json!(false)), true);
459            assert_eq!(less_than(&json!(1), &json!(true)), false);
460            assert_eq!(less_than(&json!(0), &json!(true)), true);
461        }
462
463        #[test]
464        fn bool_string() {
465            // bool < string, bool is converted to number, string is converted to number
466            assert_eq!(less_than(&json!(false), &json!("1")), true);
467            assert_eq!(less_than(&json!(true), &json!("1")), false);
468            assert_eq!(less_than(&json!(true), &json!("2")), true);
469            assert_eq!(less_than(&json!(true), &json!("foo")), false);
470
471            // string < bool, bool is converted to number, string is converted to number
472            assert_eq!(less_than(&json!("-1"), &json!(false)), true);
473            assert_eq!(less_than(&json!("1"), &json!(true)), false);
474            assert_eq!(less_than(&json!("0"), &json!(true)), true);
475            assert_eq!(less_than(&json!("foo"), &json!(true)), false);
476        }
477
478        #[test]
479        fn bool_array() {
480            less_than!(false, [true], false);
481            less_than!(false, [false], false);
482            less_than!(false, [0], false);
483            less_than!(false, [1], true);
484            less_than!(false, [1, 2], false);
485            less_than!(true, [true], false);
486            less_than!(true, [false], false);
487            less_than!(true, [0], false);
488            less_than!(true, [1], false);
489            less_than!(true, [2], true);
490            less_than!(true, [2, 3], false);
491        }
492
493        #[test]
494        fn string_array() {
495            assert_eq!(less_than(&json!([1]), &json!("12")), true);
496            assert_eq!(less_than(&json!([2]), &json!("12")), false);
497        }
498
499        #[test]
500        fn with_null() {
501            // null < *, * is converted to number, null is treated as 0
502            macro_rules! null_less_than {
503                ($a:expr, $b:expr) => {
504                    assert_eq!(less_than(&json!(null), &json!($a)), $b);
505                };
506            }
507
508            macro_rules! is_less_than_null {
509                ($a:expr, $b:expr) => {
510                    assert_eq!(less_than(&json!($a), &json!(null)), $b);
511                };
512            }
513
514            null_less_than!(1, true);
515            null_less_than!("5", true);
516            null_less_than!(true, true);
517
518            null_less_than!({}, false);
519            null_less_than!([-5], false);
520            null_less_than!(["-5"], false);
521            null_less_than!([5], true);
522            null_less_than!(["5"], true);
523
524            is_less_than_null!(-1, true);
525            is_less_than_null!(1, false);
526            is_less_than_null!("-1", true);
527            is_less_than_null!("1", false);
528
529            is_less_than_null!({}, false);
530            is_less_than_null!([-5], true);
531            is_less_than_null!(["-5"], true);
532            is_less_than_null!([5], false);
533            is_less_than_null!(["5"], false);
534        }
535    }
536
537    #[allow(clippy::approx_constant)]
538    mod parse_float {
539        use super::*;
540
541        #[test]
542        fn success() {
543            let result = Some(3.14);
544
545            assert_eq!(parse_float(&json!(3.14)), result);
546            assert_eq!(parse_float(&json!("3.14")), result);
547            assert_eq!(parse_float(&json!("3.14.5")), result);
548            assert_eq!(parse_float(&json!("  3.14  ")), result);
549            assert_eq!(parse_float(&json!("314e-2")), result);
550            assert_eq!(parse_float(&json!("0.0314E+2")), result);
551            assert_eq!(parse_float(&json!("0.0314e+2")), result);
552            assert_eq!(parse_float(&json!("3.14some non-digit characters")), result);
553        }
554
555        #[test]
556        fn nan() {
557            assert_eq!(parse_float(&json!("FF2")), None);
558        }
559
560        #[test]
561        fn sign() {
562            assert_eq!(parse_float(&json!("+3.14")), Some(3.14));
563            assert_eq!(parse_float(&json!("-3.14")), Some(-3.14));
564        }
565    }
566
567    #[test]
568    fn test_less_equal_than() {
569        assert_eq!(less_equal_than(&json!(1), &json!(1)), true);
570        assert_eq!(less_equal_than(&json!([1]), &json!("1")), true);
571        assert_eq!(less_equal_than(&json!([1]), &json!("12")), true);
572
573        assert_eq!(less_equal_than(&json!(2), &json!(1)), false);
574        assert_eq!(less_equal_than(&json!([2]), &json!("12")), false);
575    }
576
577    #[test]
578    fn test_greater_than() {
579        assert_eq!(greater_than(&json!(2), &json!(1)), true);
580        assert_eq!(greater_than(&json!(2), &json!(2)), false);
581        assert_eq!(greater_than(&json!(2), &json!(3)), false);
582    }
583
584    #[test]
585    fn test_greater_equal_than() {
586        assert_eq!(greater_equal_than(&json!(2), &json!(1)), true);
587        assert_eq!(greater_equal_than(&json!(2), &json!(2)), true);
588        assert_eq!(greater_equal_than(&json!(2), &json!(3)), false);
589    }
590
591    #[test]
592    fn truthy_values() {
593        // See http://jsonlogic.com/truthy.html
594        assert_eq!(is_truthy(&json!(0)), false);
595        assert_eq!(is_truthy(&json!(-1)), true);
596        assert_eq!(is_truthy(&json!(1)), true);
597        assert_eq!(is_truthy(&json!([])), false);
598        assert_eq!(is_truthy(&json!([1, 2])), true);
599        assert_eq!(is_truthy(&json!("")), false);
600        assert_eq!(is_truthy(&json!("anything")), true);
601        assert_eq!(is_truthy(&json!("0")), true);
602        assert_eq!(is_truthy(&json!(["0"])), true);
603        assert_eq!(is_truthy(&json!(null)), false);
604
605        assert_eq!(is_truthy(&json!({})), true);
606        assert_eq!(is_truthy(&json!(true)), true);
607        assert_eq!(is_truthy(&json!(false)), false);
608    }
609
610    #[test]
611    fn value_as_string_coercion() {
612        assert_eq!(coerce_to_str(&json!(true)), "true");
613        assert_eq!(coerce_to_str(&json!(false)), "false");
614        assert_eq!(coerce_to_str(&json!([false])), "false");
615        assert_eq!(coerce_to_str(&json!([true])), "true");
616        assert_eq!(coerce_to_str(&json!(null)), "null");
617        assert_eq!(coerce_to_str(&json!({})), "[object Object]");
618
619        assert_eq!(coerce_to_str(&json!([1, 2])), "1,2");
620        // String([[1,2], [3, 4]]) === "1,2,3,4"
621        assert_eq!(coerce_to_str(&json!([[1, 2], [3, 4]])), "1,2,3,4");
622        // String([[1,2], [[true, 4]], 5]) === '1,2,true,4,5'
623        assert_eq!(
624            coerce_to_str(&json!([[1, 2], [[true, 4]], 5])),
625            "1,2,true,4,5"
626        );
627    }
628
629    #[test]
630    fn value_as_f64_coercion() {
631        assert_eq!(coerce_to_f64(&json!([[[5]]])), Some(5f64));
632        assert_eq!(coerce_to_f64(&json!([[[5], 6]])), None);
633        assert_eq!(coerce_to_f64(&json!([[[1, 2]]])), None);
634        assert_eq!(coerce_to_f64(&json!(null)), Some(0f64));
635        assert_eq!(coerce_to_f64(&json!(true)), Some(1f64));
636        assert_eq!(coerce_to_f64(&json!([true])), None);
637        assert_eq!(coerce_to_f64(&json!(false)), Some(0f64));
638        assert_eq!(coerce_to_f64(&json!([false])), None);
639        assert_eq!(coerce_to_f64(&json!("1")), Some(1f64));
640        assert_eq!(coerce_to_f64(&json!("1.1")), Some(1.1));
641        assert_eq!(coerce_to_f64(&json!("1.8")), Some(1.8));
642        assert_eq!(coerce_to_f64(&json!(["1"])), Some(1f64));
643        assert_eq!(coerce_to_f64(&json!(null)), Some(0f64));
644        assert_eq!(coerce_to_f64(&json!([null])), Some(0f64));
645    }
646}