diff --git a/libs/giskard-checks/src/giskard/checks/builtin/json_valid.py b/libs/giskard-checks/src/giskard/checks/builtin/json_valid.py index 26a4c38171..a0ad696732 100644 --- a/libs/giskard-checks/src/giskard/checks/builtin/json_valid.py +++ b/libs/giskard-checks/src/giskard/checks/builtin/json_valid.py @@ -114,7 +114,11 @@ async def run(self, trace: TraceType) -> CheckResult: @staticmethod def _parse_json(value: Any) -> Any: if isinstance(value, str): - return json.loads(value) + if value.strip() == "": + return json.loads(value) + if JsonValid._looks_like_serialized_json(value): + return json.loads(value) + return value try: json.dumps(value) @@ -123,6 +127,27 @@ def _parse_json(value: Any) -> Any: return value + @staticmethod + def _looks_like_serialized_json(value: str) -> bool: + stripped = value.strip() + if not stripped: + return False + + if stripped.startswith(("{", "[", '"')): + return True + + if stripped in ("true", "false", "null"): + return True + + if (stripped[:1].isdigit() or stripped.startswith("-")) and "_" not in stripped: + try: + parsed = json.loads(stripped) + except json.JSONDecodeError: + return False + return isinstance(parsed, int | float) + + return False + @staticmethod def _validate_schema_definition(schema: dict[str, Any]) -> None: validator_for(schema).check_schema(schema) diff --git a/libs/giskard-checks/tests/builtin/test_json_valid.py b/libs/giskard-checks/tests/builtin/test_json_valid.py index 9c3c40a768..1dd1cab98e 100644 --- a/libs/giskard-checks/tests/builtin/test_json_valid.py +++ b/libs/giskard-checks/tests/builtin/test_json_valid.py @@ -18,6 +18,12 @@ ("null", None), ("true", True), ("42", 42), + ("hello", "hello"), + ("truth", "truth"), + ("falsehood", "falsehood"), + ("nullify", "nullify"), + ("123-apple", "123-apple"), + ("-infinity", "-infinity"), ], ) async def test_valid_json_output_passes(outputs: Any, expected_parsed: Any) -> None: @@ -37,6 +43,7 @@ async def test_valid_json_output_passes(outputs: Any, expected_parsed: Any) -> N [ '{"name": "Alice"', "", + " ", ], ) async def test_invalid_json_string_fails(outputs: str) -> None: @@ -83,6 +90,7 @@ async def test_nested_jsonpath_extraction() -> None: ), ({"type": "integer"}, 42, 42), ({"type": "string", "minLength": 3, "maxLength": 7}, '"hello"', "hello"), + ({"type": "string", "minLength": 3, "maxLength": 7}, "hello", "hello"), ], ) async def test_schema_validation_passes(