Rust LogoJSON Validator

A JSON Validator is a tool or library used to check if a given JSON (JavaScript Object Notation) document is well-formed and/or adheres to a predefined structure and data types. This process is crucial for ensuring data integrity, preventing errors in data processing, and maintaining consistency in API interactions or data storage.

There are primarily two types of JSON validation:

1. Syntactic Validation: This type of validation checks if the JSON string is correctly formatted according to the official JSON specification. It ensures that all curly braces, square brackets, quotes, colons, and commas are used correctly, and that data types (strings, numbers, booleans, null) are properly represented. Most JSON parsing libraries inherently perform syntactic validation; if a string cannot be parsed into a JSON data structure, it is considered syntactically invalid.

2. Schema Validation: This is a more advanced form of validation where the JSON data is checked against a JSON Schema. A JSON Schema is a declarative format (itself written in JSON) that describes the structure, data types, required properties, minimum/maximum values, string patterns, and other constraints that a JSON document must satisfy. Schema validation is essential for:
* Data Integrity: Ensuring that incoming data conforms to expected formats before processing.
* API Reliability: Validating request bodies and response payloads in web services.
* Documentation: JSON Schemas also serve as excellent documentation for data structures.
* Code Generation: Schemas can be used to generate code for data models.

How it works (in Rust):

* Syntactic Validation: Libraries like `serde_json` provide functions (e.g., `serde_json::from_str`) to parse a string into a Rust data structure or a generic `serde_json::Value`. If the parsing succeeds, the JSON is syntactically valid; otherwise, it's invalid.
* Schema Validation: Dedicated crates like `jsonschema` allow you to load a JSON Schema, compile it, and then validate `serde_json::Value` instances against the compiled schema. This process returns a success or a detailed error report indicating which parts of the JSON failed to conform to the schema.

Example Code

```rust
use serde_json::{self, Value};
use jsonschema::{JSONSchema, ValidationError};

fn main() {
    println!("--- Syntactic Validation ---");

    let valid_json_str = r#"{
        "name": "Alice",
        "age": 30,
        "isStudent": false,
        "courses": ["Math", "Science"]
    }"#;

    let invalid_json_str_syntax = r#"{
        "name": "Bob",
        "age": 25,
        "city": "New York",
    "#; // Missing closing brace

    // Example 1: Syntactically Valid JSON
    match serde_json::from_str::<Value>(valid_json_str) {
        Ok(parsed_json) => {
            println!("Valid JSON string parsed successfully:\n{}", serde_json::to_string_pretty(&parsed_json).unwrap());
        }
        Err(e) => {
            println!("Error parsing valid JSON string (this should not happen): {}", e);
        }
    }

    // Example 2: Syntactically Invalid JSON
    match serde_json::from_str::<Value>(invalid_json_str_syntax) {
        Ok(_) => {
            println!("Parsed invalid JSON string (this should not happen).");
        }
        Err(e) => {
            println!("Error parsing invalid JSON string (expected): {}", e);
        }
    }

    println!("\n--- Schema Validation ---");

    // Define a JSON Schema
    let schema_str = r#"{
        "type": "object",
        "properties": {
            "name": { "type": "string", "minLength": 1 },
            "age": { "type": "integer", "minimum": 0, "maximum": 150 },
            "email": { "type": "string", "format": "email" }
        },
        "required": ["name", "age"]
    }"#;

    let schema_value: Value = serde_json::from_str(schema_str).expect("Invalid schema JSON");
    let compiled_schema = JSONSchema::compile(&schema_value).expect("Failed to compile schema");

    // Data to validate
    let valid_data_str = r#"{
        "name": "Charlie",
        "age": 45,
        "email": "charlie@example.com"
    }"#;

    let invalid_data_missing_field_str = r#"{
        "age": 20,
        "email": "test@example.com"
    }"#; // Missing 'name' which is required

    let invalid_data_wrong_type_str = r#"{
        "name": "David",
        "age": "fifty"
    }"#; // 'age' should be integer, not string

    let invalid_data_out_of_range_str = r#"{
        "name": "Eve",
        "age": 200
    }"#; // 'age' is above maximum of 150

    // Example 3: Valid data against schema
    let valid_data: Value = serde_json::from_str(valid_data_str).expect("Invalid data JSON");
    match compiled_schema.validate(&valid_data) {
        Ok(_) => {
            println!("Valid data against schema (expected):\n{}", serde_json::to_string_pretty(&valid_data).unwrap());
            println!("Validation result: OK");
        }
        Err(errors) => {
            println!("Validation failed for valid data (this should not happen):");
            for error in errors {
                println!("- Error: {} at instance path {}", error.instance_path, error.to_string());
            }
        }
    }

    // Example 4: Invalid data (missing required field) against schema
    let invalid_data_missing_field: Value = serde_json::from_str(invalid_data_missing_field_str).expect("Invalid data JSON");
    match compiled_schema.validate(&invalid_data_missing_field) {
        Ok(_) => {
            println!("Validation succeeded for invalid data (this should not happen):\n{}", serde_json::to_string_pretty(&invalid_data_missing_field).unwrap());
        }
        Err(errors) => {
            println!("Validation failed for invalid data (missing required field, expected):\n{}", serde_json::to_string_pretty(&invalid_data_missing_field).unwrap());
            for error in errors {
                println!("- Error: {} at instance path {}", error.instance_path, error.to_string());
            }
        }
    }

    // Example 5: Invalid data (wrong type) against schema
    let invalid_data_wrong_type: Value = serde_json::from_str(invalid_data_wrong_type_str).expect("Invalid data JSON");
    match compiled_schema.validate(&invalid_data_wrong_type) {
        Ok(_) => {
            println!("Validation succeeded for invalid data (wrong type, this should not happen):\n{}", serde_json::to_string_pretty(&invalid_data_wrong_type).unwrap());
        }
        Err(errors) => {
            println!("Validation failed for invalid data (wrong type, expected):\n{}", serde_json::to_string_pretty(&invalid_data_wrong_type).unwrap());
            for error in errors {
                println!("- Error: {} at instance path {}", error.instance_path, error.to_string());
            }
        }
    }

    // Example 6: Invalid data (out of range) against schema
    let invalid_data_out_of_range: Value = serde_json::from_str(invalid_data_out_of_range_str).expect("Invalid data JSON");
    match compiled_schema.validate(&invalid_data_out_of_range) {
        Ok(_) => {
            println!("Validation succeeded for invalid data (out of range, this should not happen):\n{}", serde_json::to_string_pretty(&invalid_data_out_of_range).unwrap());
        }
        Err(errors) => {
            println!("Validation failed for invalid data (out of range, expected):\n{}", serde_json::to_string_pretty(&invalid_data_out_of_range).unwrap());
            for error in errors {
                println!("- Error: {} at instance path {}", error.instance_path, error.to_string());
            }
        }
    }
}
```

To run this Rust example, you need to add the following dependencies to your `Cargo.toml` file:

```toml
[dependencies]
serde_json = "1.0"
jsonschema = "0.16"
```