From 6fec366452da30fa6d149aa7c5f4984495a359c1 Mon Sep 17 00:00:00 2001 From: Xiwei Pan Date: Fri, 18 Sep 2026 01:28:03 +0800 Subject: [PATCH] Validate string model loading and rebuild derived length bounds --- src/models/misc/closest_string.rs | 61 ++++++++++------ src/models/misc/grouping_by_swapping.rs | 71 +++++++++--------- src/models/misc/longest_common_subsequence.rs | 66 +++++++++-------- .../misc/shortest_common_supersequence.rs | 59 +++++++++------ .../misc/shortest_common_superstring.rs | 43 ++++++++--- .../misc/string_to_string_correction.rs | 72 +++++++++++-------- src/unit_tests/models/misc/closest_string.rs | 11 +++ .../models/misc/grouping_by_swapping.rs | 10 +++ .../models/misc/longest_common_subsequence.rs | 20 ++++++ .../misc/shortest_common_supersequence.rs | 20 ++++++ .../misc/shortest_common_superstring.rs | 20 ++++++ .../misc/string_to_string_correction.rs | 10 +++ 12 files changed, 317 insertions(+), 146 deletions(-) diff --git a/src/models/misc/closest_string.rs b/src/models/misc/closest_string.rs index 722d23dfd..6910a10bd 100644 --- a/src/models/misc/closest_string.rs +++ b/src/models/misc/closest_string.rs @@ -46,11 +46,26 @@ inventory::submit! { /// syntactically feasible; the objective is its worst-case Hamming distance /// to the input strings. #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(try_from = "ClosestStringData")] pub struct ClosestString { alphabet_size: usize, strings: Vec>, } +#[derive(Deserialize)] +struct ClosestStringData { + alphabet_size: usize, + strings: Vec>, +} + +impl TryFrom for ClosestString { + type Error = crate::registry::ConstructionError; + + fn try_from(data: ClosestStringData) -> Result { + Self::try_new(data.alphabet_size, data.strings) + } +} + impl ClosestString { /// Create a new `ClosestString` instance. /// @@ -62,30 +77,34 @@ impl ClosestString { /// - `alphabet_size == 0` while any input string is non-empty, /// - any symbol in any input string is `>= alphabet_size`. pub fn new(alphabet_size: usize, strings: Vec>) -> Self { - assert!( - !strings.is_empty(), - "ClosestString requires at least one input string" - ); + Self::try_new(alphabet_size, strings).unwrap_or_else(|error| panic!("{error}")) + } + + fn try_new( + alphabet_size: usize, + strings: Vec>, + ) -> Result { + if strings.is_empty() { + return Err("ClosestString requires at least one input string".into()); + } let string_length = strings[0].len(); - assert!( - strings.iter().all(|s| s.len() == string_length), - "all input strings must have the same length" - ); - assert!( - alphabet_size > 0 || string_length == 0, - "alphabet_size must be > 0 when input strings are non-empty" - ); - assert!( - strings - .iter() - .flat_map(|s| s.iter()) - .all(|&symbol| symbol < alphabet_size), - "input symbols must be less than alphabet_size" - ); - Self { + if !(strings.iter().all(|s| s.len() == string_length)) { + return Err("all input strings must have the same length".into()); + } + if !(alphabet_size > 0 || string_length == 0) { + return Err("alphabet_size must be > 0 when input strings are non-empty".into()); + } + if !(strings + .iter() + .flat_map(|s| s.iter()) + .all(|&symbol| symbol < alphabet_size)) + { + return Err("input symbols must be less than alphabet_size".into()); + } + Ok(Self { alphabet_size, strings, - } + }) } /// Returns the alphabet size `q`. diff --git a/src/models/misc/grouping_by_swapping.rs b/src/models/misc/grouping_by_swapping.rs index 863c0fec6..8b22940c2 100644 --- a/src/models/misc/grouping_by_swapping.rs +++ b/src/models/misc/grouping_by_swapping.rs @@ -27,12 +27,28 @@ inventory::submit! { /// adjacent swap position `i` (swap positions `i` and `i + 1`) or the special /// no-op value `string_len - 1`. #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(try_from = "GroupingBySwappingData")] pub struct GroupingBySwapping { alphabet_size: usize, string: Vec, budget: usize, } +#[derive(Deserialize)] +struct GroupingBySwappingData { + alphabet_size: usize, + string: Vec, + budget: usize, +} + +impl TryFrom for GroupingBySwapping { + type Error = crate::registry::ConstructionError; + + fn try_from(data: GroupingBySwappingData) -> Result { + Self::try_new(data.alphabet_size, data.string, data.budget) + } +} + #[derive(Debug, Deserialize, crate::CreateSpec)] struct GroupingBySwappingCreateSpec { /// Optional alphabet size; omitted values are inferred from the string. @@ -61,27 +77,7 @@ impl TryFrom for GroupingBySwapping { .transpose()? .unwrap_or(0); let alphabet_size = spec.alphabet_size.unwrap_or(inferred_alphabet_size); - if alphabet_size < inferred_alphabet_size { - return Err(format!( - "alphabet size {alphabet_size} is smaller than inferred alphabet size {inferred_alphabet_size}" - ).into()); - } - if alphabet_size == 0 && !spec.string.is_empty() { - return Err("alphabet size must be positive for a non-empty string" - .to_string() - .into()); - } - if spec.string.is_empty() && spec.bound != 0 { - return Err("bound must be zero when the string is empty" - .to_string() - .into()); - } - - Ok(Self { - alphabet_size, - string: spec.string, - budget: spec.bound, - }) + Self::try_new(alphabet_size, spec.string, spec.bound) } } @@ -93,23 +89,28 @@ impl GroupingBySwapping { /// Panics if the string contains a symbol outside the declared alphabet, /// or if the string is empty while the budget is positive. pub fn new(alphabet_size: usize, string: Vec, budget: usize) -> Self { - assert!( - alphabet_size > 0 || string.is_empty(), - "alphabet_size must be > 0 when string is non-empty" - ); - assert!( - string.iter().all(|&symbol| symbol < alphabet_size), - "input symbols must be less than alphabet_size" - ); - assert!( - !string.is_empty() || budget == 0, - "budget must be 0 when string is empty" - ); - Self { + Self::try_new(alphabet_size, string, budget).unwrap_or_else(|error| panic!("{error}")) + } + + fn try_new( + alphabet_size: usize, + string: Vec, + budget: usize, + ) -> Result { + if !(alphabet_size > 0 || string.is_empty()) { + return Err("alphabet_size must be > 0 when string is non-empty".into()); + } + if !(string.iter().all(|&symbol| symbol < alphabet_size)) { + return Err("input symbols must be less than alphabet_size".into()); + } + if !(!string.is_empty() || budget == 0) { + return Err("budget must be 0 when string is empty".into()); + } + Ok(Self { alphabet_size, string, budget, - } + }) } /// Returns the alphabet size. diff --git a/src/models/misc/longest_common_subsequence.rs b/src/models/misc/longest_common_subsequence.rs index 15bb0c0d2..1f28fda78 100644 --- a/src/models/misc/longest_common_subsequence.rs +++ b/src/models/misc/longest_common_subsequence.rs @@ -36,12 +36,27 @@ inventory::submit! { /// subsequence consists of the symbols before padding starts. The objective is /// to maximize the effective length. #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(try_from = "LongestCommonSubsequenceData")] pub struct LongestCommonSubsequence { alphabet_size: usize, strings: Vec>, max_length: usize, } +#[derive(Deserialize)] +struct LongestCommonSubsequenceData { + alphabet_size: usize, + strings: Vec>, +} + +impl TryFrom for LongestCommonSubsequence { + type Error = crate::registry::ConstructionError; + + fn try_from(data: LongestCommonSubsequenceData) -> Result { + Self::try_new(data.alphabet_size, data.strings) + } +} + #[derive(Debug, Deserialize, crate::CreateSpec)] struct LongestCommonSubsequenceCreateSpec { /// Optional alphabet size; omitted values are inferred from the strings. @@ -74,21 +89,7 @@ impl TryFrom for LongestCommonSubsequence { .transpose()? .unwrap_or(0); let alphabet_size = spec.alphabet_size.unwrap_or(inferred_alphabet_size); - if alphabet_size < inferred_alphabet_size { - return Err(format!( - "alphabet size {alphabet_size} is smaller than inferred alphabet size {inferred_alphabet_size}" - ).into()); - } - if alphabet_size == 0 { - return Err("alphabet size must be positive".to_string().into()); - } - let max_length = spec.strings.iter().map(Vec::len).min().unwrap_or(0); - - Ok(Self { - alphabet_size, - strings: spec.strings, - max_length, - }) + Self::try_new(alphabet_size, spec.strings) } } @@ -101,26 +102,31 @@ impl LongestCommonSubsequence { /// # Panics /// /// Panics if `alphabet_size == 0` and any input string is non-empty, or if - /// an input symbol is outside the declared alphabet, or if all strings are - /// empty (max_length would be 0, requiring at least one non-empty string). + /// an input symbol is outside the declared alphabet. pub fn new(alphabet_size: usize, strings: Vec>) -> Self { + Self::try_new(alphabet_size, strings).unwrap_or_else(|error| panic!("{error}")) + } + + fn try_new( + alphabet_size: usize, + strings: Vec>, + ) -> Result { let max_length = strings.iter().map(|s| s.len()).min().unwrap_or(0); - assert!( - alphabet_size > 0 || strings.iter().all(|s| s.is_empty()), - "alphabet_size must be > 0 when any input string is non-empty" - ); - assert!( - strings - .iter() - .flat_map(|s| s.iter()) - .all(|&symbol| symbol < alphabet_size), - "input symbols must be less than alphabet_size" - ); - Self { + if !(alphabet_size > 0 || strings.iter().all(|s| s.is_empty())) { + return Err("alphabet_size must be > 0 when any input string is non-empty".into()); + } + if !(strings + .iter() + .flat_map(|s| s.iter()) + .all(|&symbol| symbol < alphabet_size)) + { + return Err("input symbols must be less than alphabet_size".into()); + } + Ok(Self { alphabet_size, strings, max_length, - } + }) } /// Returns the alphabet size. diff --git a/src/models/misc/shortest_common_supersequence.rs b/src/models/misc/shortest_common_supersequence.rs index b158a5347..5ea1fdea0 100644 --- a/src/models/misc/shortest_common_supersequence.rs +++ b/src/models/misc/shortest_common_supersequence.rs @@ -55,12 +55,27 @@ inventory::submit! { /// assert!(solution.is_some()); /// ``` #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(try_from = "ShortestCommonSupersequenceData")] pub struct ShortestCommonSupersequence { alphabet_size: usize, strings: Vec>, max_length: usize, } +#[derive(Deserialize)] +struct ShortestCommonSupersequenceData { + alphabet_size: usize, + strings: Vec>, +} + +impl TryFrom for ShortestCommonSupersequence { + type Error = crate::registry::ConstructionError; + + fn try_from(data: ShortestCommonSupersequenceData) -> Result { + Self::try_new(data.alphabet_size, data.strings) + } +} + #[derive(Debug, Deserialize, crate::CreateSpec)] struct ShortestCommonSupersequenceCreateSpec { /// Input strings; the alphabet and maximum length are inferred from them. @@ -72,10 +87,6 @@ impl TryFrom for ShortestCommonSuperseque type Error = crate::registry::ConstructionError; fn try_from(spec: ShortestCommonSupersequenceCreateSpec) -> Result { - if spec.strings.is_empty() { - return Err("must have at least one string".to_string().into()); - } - let alphabet_size = spec .strings .iter() @@ -89,17 +100,7 @@ impl TryFrom for ShortestCommonSuperseque }) .transpose()? .unwrap_or(0); - let max_length = spec.strings.iter().try_fold(0_usize, |total, string| { - total - .checked_add(string.len()) - .ok_or_else(|| "maximum supersequence length overflows usize".to_string()) - })?; - - Ok(Self { - alphabet_size, - strings: spec.strings, - max_length, - }) + Self::try_new(alphabet_size, spec.strings) } } @@ -114,17 +115,29 @@ impl ShortestCommonSupersequence { /// Panics if `strings` is empty, or if `alphabet_size` is 0 and any input /// string is non-empty. pub fn new(alphabet_size: usize, strings: Vec>) -> Self { - assert!(!strings.is_empty(), "must have at least one string"); - let max_length: usize = strings.iter().map(|s| s.len()).sum(); - assert!( - alphabet_size > 0 || strings.iter().all(|s| s.is_empty()), - "alphabet_size must be > 0 when any input string is non-empty" - ); - Self { + Self::try_new(alphabet_size, strings).unwrap_or_else(|error| panic!("{error}")) + } + + fn try_new( + alphabet_size: usize, + strings: Vec>, + ) -> Result { + if strings.is_empty() { + return Err("must have at least one string".into()); + } + let max_length = strings.iter().try_fold(0usize, |total, string| { + total + .checked_add(string.len()) + .ok_or("maximum string length overflows usize") + })?; + if !(alphabet_size > 0 || strings.iter().all(|s| s.is_empty())) { + return Err("alphabet_size must be > 0 when any input string is non-empty".into()); + } + Ok(Self { alphabet_size, strings, max_length, - } + }) } /// Returns the alphabet size. diff --git a/src/models/misc/shortest_common_superstring.rs b/src/models/misc/shortest_common_superstring.rs index 81fee2a1c..ed634b785 100644 --- a/src/models/misc/shortest_common_superstring.rs +++ b/src/models/misc/shortest_common_superstring.rs @@ -63,12 +63,27 @@ inventory::submit! { /// assert!(solution.is_some()); /// ``` #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(try_from = "ShortestCommonSuperstringData")] pub struct ShortestCommonSuperstring { alphabet_size: usize, strings: Vec>, max_length: usize, } +#[derive(Deserialize)] +struct ShortestCommonSuperstringData { + alphabet_size: usize, + strings: Vec>, +} + +impl TryFrom for ShortestCommonSuperstring { + type Error = crate::registry::ConstructionError; + + fn try_from(data: ShortestCommonSuperstringData) -> Result { + Self::try_new(data.alphabet_size, data.strings) + } +} + impl ShortestCommonSuperstring { /// Create a new ShortestCommonSuperstring instance. /// @@ -80,17 +95,29 @@ impl ShortestCommonSuperstring { /// Panics if `strings` is empty, or if `alphabet_size` is 0 and any input /// string is non-empty. pub fn new(alphabet_size: usize, strings: Vec>) -> Self { - assert!(!strings.is_empty(), "must have at least one string"); - let max_length: usize = strings.iter().map(|s| s.len()).sum(); - assert!( - alphabet_size > 0 || strings.iter().all(|s| s.is_empty()), - "alphabet_size must be > 0 when any input string is non-empty" - ); - Self { + Self::try_new(alphabet_size, strings).unwrap_or_else(|error| panic!("{error}")) + } + + fn try_new( + alphabet_size: usize, + strings: Vec>, + ) -> Result { + if strings.is_empty() { + return Err("must have at least one string".into()); + } + let max_length = strings.iter().try_fold(0usize, |total, string| { + total + .checked_add(string.len()) + .ok_or("maximum string length overflows usize") + })?; + if !(alphabet_size > 0 || strings.iter().all(|s| s.is_empty())) { + return Err("alphabet_size must be > 0 when any input string is non-empty".into()); + } + Ok(Self { alphabet_size, strings, max_length, - } + }) } /// Returns the alphabet size. diff --git a/src/models/misc/string_to_string_correction.rs b/src/models/misc/string_to_string_correction.rs index 0343806e8..d5912afb0 100644 --- a/src/models/misc/string_to_string_correction.rs +++ b/src/models/misc/string_to_string_correction.rs @@ -66,6 +66,7 @@ inventory::submit! { /// assert!(solution.is_some()); /// ``` #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(try_from = "StringToStringCorrectionData")] pub struct StringToStringCorrection { alphabet_size: usize, source: Vec, @@ -73,6 +74,22 @@ pub struct StringToStringCorrection { bound: usize, } +#[derive(Deserialize)] +struct StringToStringCorrectionData { + alphabet_size: usize, + source: Vec, + target: Vec, + bound: usize, +} + +impl TryFrom for StringToStringCorrection { + type Error = crate::registry::ConstructionError; + + fn try_from(data: StringToStringCorrectionData) -> Result { + Self::try_new(data.alphabet_size, data.source, data.target, data.bound) + } +} + #[derive(Debug, Deserialize, crate::CreateSpec)] struct StringToStringCorrectionCreateSpec { /// Optional alphabet size; omitted values are inferred from both strings. @@ -105,22 +122,12 @@ impl TryFrom for StringToStringCorrection { .transpose()? .unwrap_or(0); let alphabet_size = spec.alphabet_size.unwrap_or(inferred_alphabet_size); - if alphabet_size < inferred_alphabet_size { - return Err(format!( - "alphabet size {alphabet_size} is smaller than inferred alphabet size {inferred_alphabet_size}" - ).into()); - } - if alphabet_size == 0 && (!spec.source_string.is_empty() || !spec.target_string.is_empty()) - { - return Err("alphabet size must be positive when either string is non-empty".into()); - } - - Ok(Self { + Self::try_new( alphabet_size, - source: spec.source_string, - target: spec.target_string, - bound: spec.bound, - }) + spec.source_string, + spec.target_string, + spec.bound, + ) } } @@ -133,24 +140,31 @@ impl StringToStringCorrection { /// non-empty, or if any symbol in `source` or `target` is /// `>= alphabet_size`. pub fn new(alphabet_size: usize, source: Vec, target: Vec, bound: usize) -> Self { - assert!( - alphabet_size > 0 || (source.is_empty() && target.is_empty()), - "alphabet_size must be > 0 when source or target is non-empty" - ); - assert!( - source.iter().all(|&s| s < alphabet_size), - "all source symbols must be < alphabet_size" - ); - assert!( - target.iter().all(|&s| s < alphabet_size), - "all target symbols must be < alphabet_size" - ); - Self { + Self::try_new(alphabet_size, source, target, bound) + .unwrap_or_else(|error| panic!("{error}")) + } + + fn try_new( + alphabet_size: usize, + source: Vec, + target: Vec, + bound: usize, + ) -> Result { + if !(alphabet_size > 0 || (source.is_empty() && target.is_empty())) { + return Err("alphabet_size must be > 0 when source or target is non-empty".into()); + } + if !(source.iter().all(|&s| s < alphabet_size)) { + return Err("all source symbols must be < alphabet_size".into()); + } + if !(target.iter().all(|&s| s < alphabet_size)) { + return Err("all target symbols must be < alphabet_size".into()); + } + Ok(Self { alphabet_size, source, target, bound, - } + }) } /// Returns the alphabet size. diff --git a/src/unit_tests/models/misc/closest_string.rs b/src/unit_tests/models/misc/closest_string.rs index fba543793..df523fb4f 100644 --- a/src/unit_tests/models/misc/closest_string.rs +++ b/src/unit_tests/models/misc/closest_string.rs @@ -126,3 +126,14 @@ fn test_closest_string_serialization() { problem.evaluate(&vec![0, 0, 0]).unwrap() ); } + +#[test] +fn deserialize_rejects_invalid_input() { + for json in [ + serde_json::json!({"alphabet_size": 2, "strings": []}), + serde_json::json!({"alphabet_size": 2, "strings": [[0], [0, 1]]}), + serde_json::json!({"alphabet_size": 2, "strings": [[2]]}), + ] { + assert!(serde_json::from_value::(json).is_err()); + } +} diff --git a/src/unit_tests/models/misc/grouping_by_swapping.rs b/src/unit_tests/models/misc/grouping_by_swapping.rs index 7b56a31d8..e8911fffc 100644 --- a/src/unit_tests/models/misc/grouping_by_swapping.rs +++ b/src/unit_tests/models/misc/grouping_by_swapping.rs @@ -147,3 +147,13 @@ fn test_grouping_by_swapping_create_spec_rejects_nonzero_bound_for_empty_string( assert!(result.is_err()); } + +#[test] +fn deserialize_rejects_invalid_input() { + for json in [ + serde_json::json!({"alphabet_size": 1, "string": [1], "budget": 0}), + serde_json::json!({"alphabet_size": 0, "string": [], "budget": 1}), + ] { + assert!(serde_json::from_value::(json).is_err()); + } +} diff --git a/src/unit_tests/models/misc/longest_common_subsequence.rs b/src/unit_tests/models/misc/longest_common_subsequence.rs index 913a300db..ac7665979 100644 --- a/src/unit_tests/models/misc/longest_common_subsequence.rs +++ b/src/unit_tests/models/misc/longest_common_subsequence.rs @@ -220,3 +220,23 @@ fn test_lcs_create_spec_rejects_all_empty_strings() { assert!(result.is_err()); } + +#[test] +fn deserialize_rejects_invalid_input() { + for json in [ + serde_json::json!({"alphabet_size": 1, "strings": [[1]], "max_length": 1}), + serde_json::json!({"alphabet_size": 0, "strings": [[0]], "max_length": 1}), + ] { + assert!(serde_json::from_value::(json).is_err()); + } +} + +#[test] +fn deserialize_rebuilds_length_bound() { + let problem: LongestCommonSubsequence = serde_json::from_value(serde_json::json!({ + "alphabet_size": 2, "strings": [[0, 1], [1]], "max_length": 99 + })) + .unwrap(); + assert_eq!(problem.max_length(), 1); + assert_eq!(problem.dimensions().len(), 1); +} diff --git a/src/unit_tests/models/misc/shortest_common_supersequence.rs b/src/unit_tests/models/misc/shortest_common_supersequence.rs index 65dfccfcb..c4a233371 100644 --- a/src/unit_tests/models/misc/shortest_common_supersequence.rs +++ b/src/unit_tests/models/misc/shortest_common_supersequence.rs @@ -249,3 +249,23 @@ fn test_shortestcommonsupersequence_paper_example() { // Optimal SCS for "abc" and "bac" is length 4 assert_eq!(val.0.unwrap(), 4); } + +#[test] +fn deserialize_rejects_invalid_input() { + for json in [ + serde_json::json!({"alphabet_size": 2, "strings": [], "max_length": 0}), + serde_json::json!({"alphabet_size": 0, "strings": [[0]], "max_length": 1}), + ] { + assert!(serde_json::from_value::(json).is_err()); + } +} + +#[test] +fn deserialize_rebuilds_length_bound() { + let problem: ShortestCommonSupersequence = serde_json::from_value(serde_json::json!({ + "alphabet_size": 2, "strings": [[0, 1], [1]], "max_length": 99 + })) + .unwrap(); + assert_eq!(problem.max_length(), 3); + assert_eq!(problem.dimensions().len(), 3); +} diff --git a/src/unit_tests/models/misc/shortest_common_superstring.rs b/src/unit_tests/models/misc/shortest_common_superstring.rs index c2462e6c3..71386218f 100644 --- a/src/unit_tests/models/misc/shortest_common_superstring.rs +++ b/src/unit_tests/models/misc/shortest_common_superstring.rs @@ -262,3 +262,23 @@ fn test_shortestcommonsuperstring_paper_example() { Min(Some(3)) ); } + +#[test] +fn deserialize_rejects_invalid_input() { + for json in [ + serde_json::json!({"alphabet_size": 2, "strings": [], "max_length": 0}), + serde_json::json!({"alphabet_size": 0, "strings": [[0]], "max_length": 1}), + ] { + assert!(serde_json::from_value::(json).is_err()); + } +} + +#[test] +fn deserialize_rebuilds_length_bound() { + let problem: ShortestCommonSuperstring = serde_json::from_value(serde_json::json!({ + "alphabet_size": 2, "strings": [[0, 1], [1]], "max_length": 99 + })) + .unwrap(); + assert_eq!(problem.max_length(), 3); + assert_eq!(problem.dimensions().len(), 3); +} diff --git a/src/unit_tests/models/misc/string_to_string_correction.rs b/src/unit_tests/models/misc/string_to_string_correction.rs index 4530eab36..e5c099713 100644 --- a/src/unit_tests/models/misc/string_to_string_correction.rs +++ b/src/unit_tests/models/misc/string_to_string_correction.rs @@ -198,3 +198,13 @@ fn test_string_to_string_correction_create_spec_rejects_small_alphabet() { assert!(result.is_err()); } + +#[test] +fn deserialize_rejects_invalid_input() { + for json in [ + serde_json::json!({"alphabet_size": 1, "source": [1], "target": [], "bound": 1}), + serde_json::json!({"alphabet_size": 1, "source": [], "target": [1], "bound": 1}), + ] { + assert!(serde_json::from_value::(json).is_err()); + } +}