json_schema_utils 1.0.0-dev.13 copy "json_schema_utils: ^1.0.0-dev.13" to clipboard
json_schema_utils: ^1.0.0-dev.13 copied to clipboard

Utilities for building Json Schema Documents to ensure they are valid and assist with reducing the complexity of creating a schema manually

example/json_schema_utils_example.dart

import 'package:json_schema_utils/json_schema_utils.dart';

void main() {
  // Part 1: General JsonSchema example
  print('=== General JsonSchema Example ===');
  generalJsonSchemaExample();

  // Part 2: StringJsonSchema example
  print('\n\n=== StringJsonSchema Example ===');
  stringJsonSchemaExample();

  // Part 3: BooleanJsonSchema example
  print('\n\n=== BooleanJsonSchema Example ===');
  booleanJsonSchemaExample();

  // Part 4: NumberJsonSchema example
  print('\n\n=== NumberJsonSchema Example ===');
  numberJsonSchemaExample();

  // Part 5: NullJsonSchema example
  print('\n\n=== NullJsonSchema Example ===');
  nullJsonSchemaExample();

  // Part 6: ObjectJsonSchema example
  print('\n\n=== ObjectJsonSchema Example ===');
  objectJsonSchemaExample();

  // Part 7: ArrayJsonSchema example
  print('\n\n=== ArrayJsonSchema Example ===');
  arrayJsonSchemaExample();

  // Part 8: Validation Error Reporting example
  print('\n\n=== Validation Error Reporting Example ===');
  validationErrorReportingExample();
}

void generalJsonSchemaExample() {
  // Create a basic schema document
  var schema = JsonSchemaDocument(
    "https://indiealexh.dev/schema/vehicle",
    "Vehicle Schema",
    "A Schema to describe a vehicle",
  );

  try {
    // Test basic properties
    print('Testing basic properties...');
    schema
      ..id = "https://example.com/schemas/vehicle"
      ..comment = "This is a comment"
      ..defaultValue = {"type": "sedan"}
      ..examples = [
        {"type": "sedan"},
        {"type": "suv"},
      ]
      ..readOnly = true
      ..writeOnly = false;

    // Test type property
    print('Testing type property...');
    schema.type = JsonType.object;

    // Test numeric validation
    print('Testing numeric validation...');
    schema.multipleOf = 2;
    schema.maximum = 100;
    schema.exclusiveMaximum = 99;
    schema.minimum = 1;
    schema.exclusiveMinimum = 2;

    // Test string validation
    print('Testing string validation...');
    schema.maxLength = 50;
    schema.minLength = 1;
    schema.pattern = r"^[a-zA-Z0-9]+$";

    // Test array validation
    print('Testing array validation...');
    schema.maxItems = 10;
    schema.minItems = 1;
    schema.uniqueItems = true;

    // Test object validation
    print('Testing object validation...');
    schema.maxProperties = 20;
    schema.minProperties = 1;
    schema.required = ["type", "make", "model"];

    // Test format
    print('Testing format...');
    schema.format = "email";
    schema.contentMediaType = "application/json";
    schema.contentEncoding = "utf-8";

    // Print the final schema
    print('Final schema: ${schema.toJson()}');
    print('All tests passed successfully!');
  } catch (e) {
    print('Error: $e');
  }

  // Test validation errors
  print('\nTesting validation errors...');

  // Test multipleOf validation
  try {
    schema.multipleOf = 0;
    print('Failed: multipleOf should not accept 0');
  } catch (e) {
    print('Success: multipleOf validation works - $e');
  }

  // Test maxLength validation
  try {
    schema.maxLength = -1;
    print('Failed: maxLength should not accept negative values');
  } catch (e) {
    print('Success: maxLength validation works - $e');
  }

  // Test minLength validation
  try {
    schema.minLength = -1;
    print('Failed: minLength should not accept negative values');
  } catch (e) {
    print('Success: minLength validation works - $e');
  }

  // Test pattern validation
  try {
    schema.pattern = "["; // Invalid regex
    print('Failed: pattern should not accept invalid regex');
  } catch (e) {
    print('Success: pattern validation works - $e');
  }

  // Test required validation
  try {
    schema.required = []; // Empty array
    print('Failed: required should not accept empty array');
  } catch (e) {
    print('Success: required validation works - $e');
  }

  // Test allOf validation
  try {
    schema.allOf = []; // Empty array
    print('Failed: allOf should not accept empty array');
  } catch (e) {
    print('Success: allOf validation works - $e');
  }
}

void stringJsonSchemaExample() {
  print('Creating a StringJsonSchema for email validation...');

  // Create a StringJsonSchema for email validation
  var emailSchema = StringJsonSchema()
    ..title = "Email Schema"
    ..description = "A schema for validating email addresses"
    ..format = "email"
    ..minLength = 5
    ..maxLength = 100
    ..pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";

  print('Schema created: ${emailSchema.toJson()}');

  // Test valid emails
  print('\nValidating valid email addresses:');
  var validEmails = [
    'user@example.com',
    'john.doe@company.co.uk',
    'info@test-site.org',
  ];

  for (var email in validEmails) {
    bool isValid = emailSchema.validateString(email);
    print('  $email: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Test invalid emails
  print('\nValidating invalid email addresses:');
  var invalidEmails = [
    'not-an-email',
    'missing@domain',
    '@missing-username.com',
    'too.short@a.b', // Too short
    'a' * 101 + '@example.com', // Too long
  ];

  for (var email in invalidEmails) {
    bool isValid = emailSchema.validateString(email);
    print('  $email: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate type restriction
  print('\nDemonstrating type restriction:');
  try {
    print('  Attempting to set type to number...');
    emailSchema.type = JsonType.number;
    print('  Failed: Should not allow setting type to number');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Demonstrate defaultValue restriction
  print('\nDemonstrating defaultValue restriction:');
  try {
    print('  Setting defaultValue to a valid string...');
    emailSchema.defaultValue = 'default@example.com';
    print('  Success: defaultValue set to ${emailSchema.defaultValue}');

    print('  Attempting to set defaultValue to a number...');
    emailSchema.defaultValue = 123;
    print('  Failed: Should not allow setting defaultValue to a number');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Create a password schema
  print('\nCreating a StringJsonSchema for password validation...');
  var passwordSchema = StringJsonSchema()
    ..title = "Password Schema"
    ..description = "A schema for validating passwords"
    ..minLength = 8
    ..maxLength = 64
    ..pattern =
        r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$";

  print('Schema created: ${passwordSchema.toJson()}');

  // Test valid and invalid passwords
  print('\nValidating passwords:');
  var passwords = [
    'Abcd1234!', // Valid
    'weakpassword', // Invalid - no uppercase, digits or special chars
    'Short1!', // Invalid - too short
  ];

  for (var password in passwords) {
    bool isValid = passwordSchema.validateString(password);
    print('  "$password": ${isValid ? 'Valid' : 'Invalid'}');
  }
}

void booleanJsonSchemaExample() {
  print('Creating a BooleanJsonSchema for feature flag validation...');

  // Create a BooleanJsonSchema for feature flag validation
  var featureFlagSchema = BooleanJsonSchema()
    ..title = "Feature Flag Schema"
    ..description = "A schema for validating feature flag values"
    ..defaultValue = false;

  print('Schema created: ${featureFlagSchema.toJson()}');

  // Test valid boolean values
  print('\nValidating valid boolean values:');
  var validBooleans = [true, false];

  for (var value in validBooleans) {
    bool isValid = featureFlagSchema.validateBoolean(value);
    print('  $value: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Test invalid boolean values
  print('\nValidating invalid boolean values:');
  var invalidValues = [
    'true', // String, not boolean
    1, // Number, not boolean
    0, // Number, not boolean
    {}, // Object, not boolean
    [], // Array, not boolean
  ];

  for (var value in invalidValues) {
    bool isValid = featureFlagSchema.validateBoolean(value);
    print('  $value: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate type restriction
  print('\nDemonstrating type restriction:');
  try {
    print('  Attempting to set type to string...');
    featureFlagSchema.type = JsonType.string;
    print('  Failed: Should not allow setting type to string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Demonstrate defaultValue restriction
  print('\nDemonstrating defaultValue restriction:');
  try {
    print('  Setting defaultValue to a valid boolean...');
    featureFlagSchema.defaultValue = true;
    print('  Success: defaultValue set to ${featureFlagSchema.defaultValue}');

    print('  Attempting to set defaultValue to a string...');
    featureFlagSchema.defaultValue = 'true';
    print('  Failed: Should not allow setting defaultValue to a string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Create a schema with enum constraint
  print('\nCreating a BooleanJsonSchema with enum constraint...');
  var adminFlagSchema = BooleanJsonSchema()
    ..title = "Admin Flag Schema"
    ..description = "A schema for validating admin flag values"
    ..enumValues =
        [true] // Only true is allowed
    ..defaultValue = true;

  print('Schema created: ${adminFlagSchema.toJson()}');

  // Test enum constraint
  print('\nTesting enum constraint:');
  print(
    '  true: ${adminFlagSchema.validateBoolean(true) ? 'Valid' : 'Invalid'}',
  );
  print(
    '  false: ${adminFlagSchema.validateBoolean(false) ? 'Valid' : 'Invalid'}',
  );

  // Demonstrate const constraint
  print('\nDemonstrating const constraint:');
  var readOnlySchema = BooleanJsonSchema()
    ..title = "Read-Only Flag Schema"
    ..description = "A schema for a read-only flag that is always true"
    ..constValue = true;

  print('Schema created: ${readOnlySchema.toJson()}');
  print(
    '  true: ${readOnlySchema.validateBoolean(true) ? 'Valid' : 'Invalid'}',
  );
  print(
    '  false: ${readOnlySchema.validateBoolean(false) ? 'Valid' : 'Invalid'}',
  );
}

void numberJsonSchemaExample() {
  print('Creating a NumberJsonSchema for price validation...');

  // Create a NumberJsonSchema for price validation
  var priceSchema = NumberJsonSchema()
    ..title = "Price Schema"
    ..description = "A schema for validating price values"
    ..minimum = 0
    ..multipleOf =
        0.01 // Ensure prices have at most 2 decimal places
    ..defaultValue = 9.99;

  print('Schema created: ${priceSchema.toJson()}');

  // Test valid numeric values
  print('\nValidating valid numeric values:');
  var validNumbers = [0, 9.99, 10.50, 100, 123.45];

  for (var value in validNumbers) {
    bool isValid = priceSchema.validateNumber(value);
    print('  $value: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Test invalid numeric values
  print('\nValidating invalid numeric values:');
  var invalidValues = [
    -1, // Below minimum
    9.999, // Not a multiple of 0.01
    'price', // String, not number
    true, // Boolean, not number
    [10, 20], // Array, not number
    {'price': 10}, // Object, not number
  ];

  for (var value in invalidValues) {
    bool isValid = priceSchema.validateNumber(value);
    print('  $value: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate type restriction
  print('\nDemonstrating type restriction:');
  try {
    print('  Attempting to set type to string...');
    priceSchema.type = JsonType.string;
    print('  Failed: Should not allow setting type to string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Demonstrate defaultValue restriction
  print('\nDemonstrating defaultValue restriction:');
  try {
    print('  Setting defaultValue to a valid number...');
    priceSchema.defaultValue = 19.99;
    print('  Success: defaultValue set to ${priceSchema.defaultValue}');

    print('  Attempting to set defaultValue to a string...');
    priceSchema.defaultValue = 'price';
    print('  Failed: Should not allow setting defaultValue to a string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Create a schema with range constraints
  print('\nCreating a NumberJsonSchema for age validation...');
  var ageSchema = NumberJsonSchema()
    ..title = "Age Schema"
    ..description = "A schema for validating age values"
    ..minimum = 0
    ..maximum = 120
    ..multipleOf =
        1 // Integer values only
    ..defaultValue = 18;

  print('Schema created: ${ageSchema.toJson()}');

  // Test range constraints
  print('\nTesting range constraints:');
  var ageValues = [0, 18, 65, 120, -1, 121, 18.5];
  for (var age in ageValues) {
    bool isValid = ageSchema.validateNumber(age);
    print('  $age: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Create a schema with enum constraint
  print('\nCreating a NumberJsonSchema with enum constraint...');
  var ratingSchema = NumberJsonSchema()
    ..title = "Rating Schema"
    ..description = "A schema for validating rating values (1-5 stars)"
    ..enumValues = [1, 2, 3, 4, 5]
    ..defaultValue = 5;

  print('Schema created: ${ratingSchema.toJson()}');

  // Test enum constraint
  print('\nTesting enum constraint:');
  var ratingValues = [1, 3, 5, 0, 2.5, 6];
  for (var rating in ratingValues) {
    bool isValid = ratingSchema.validateNumber(rating);
    print('  $rating: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate const constraint
  print('\nDemonstrating const constraint:');
  var piSchema = NumberJsonSchema()
    ..title = "Pi Schema"
    ..description = "A schema for the mathematical constant pi"
    ..constValue = 3.14159;

  print('Schema created: ${piSchema.toJson()}');

  var piValues = [3.14159, 3.14, 3];
  for (var value in piValues) {
    bool isValid = piSchema.validateNumber(value);
    print('  $value: ${isValid ? 'Valid' : 'Invalid'}');
  }
}

void nullJsonSchemaExample() {
  print('Creating a NullJsonSchema for optional field validation...');

  // Create a NullJsonSchema for optional field validation
  var optionalFieldSchema = NullJsonSchema()
    ..title = "Optional Field Schema"
    ..description =
        "A schema for validating optional fields that can only be null"
    ..defaultValue = null;

  print('Schema created: ${optionalFieldSchema.toJson()}');

  // Test valid null value
  print('\nValidating valid null value:');
  bool isValid = optionalFieldSchema.validateNull(null);
  print('  null: ${isValid ? 'Valid' : 'Invalid'}');

  // Test invalid non-null values
  print('\nValidating invalid non-null values:');
  var invalidValues = [
    'string', // String, not null
    123, // Number, not null
    true, // Boolean, not null
    [1, 2, 3], // Array, not null
    {'key': 'value'}, // Object, not null
  ];

  for (var value in invalidValues) {
    bool isValid = optionalFieldSchema.validateNull(value);
    print('  $value: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate type restriction
  print('\nDemonstrating type restriction:');
  try {
    print('  Attempting to set type to string...');
    optionalFieldSchema.type = JsonType.string;
    print('  Failed: Should not allow setting type to string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Demonstrate defaultValue restriction
  print('\nDemonstrating defaultValue restriction:');
  try {
    print('  Setting defaultValue to null...');
    optionalFieldSchema.defaultValue = null;
    print('  Success: defaultValue set to ${optionalFieldSchema.defaultValue}');

    print('  Attempting to set defaultValue to a string...');
    optionalFieldSchema.defaultValue = 'value';
    print('  Failed: Should not allow setting defaultValue to a string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Create a schema with enum constraint
  print('\nCreating a NullJsonSchema with enum constraint...');
  var nullEnumSchema = NullJsonSchema()
    ..title = "Null Enum Schema"
    ..description = "A schema for validating null values with enum constraint"
    ..enumValues = [null];

  print('Schema created: ${nullEnumSchema.toJson()}');

  // Test enum constraint
  print('\nTesting enum constraint:');
  print('  null: ${nullEnumSchema.validateNull(null) ? 'Valid' : 'Invalid'}');
  print(
    '  "value": ${nullEnumSchema.validateNull("value") ? 'Valid' : 'Invalid'}',
  );

  // Demonstrate const constraint
  print('\nDemonstrating const constraint:');
  var nullConstSchema = NullJsonSchema()
    ..title = "Null Const Schema"
    ..description = "A schema for validating null values with const constraint"
    ..constValue = null;

  print('Schema created: ${nullConstSchema.toJson()}');
  print('  null: ${nullConstSchema.validateNull(null) ? 'Valid' : 'Invalid'}');
  print('  123: ${nullConstSchema.validateNull(123) ? 'Valid' : 'Invalid'}');
}

void objectJsonSchemaExample() {
  print('Creating an ObjectJsonSchema for user profile validation...');

  // Create an ObjectJsonSchema for user profile validation
  var userProfileSchema = ObjectJsonSchema()
    ..title = "User Profile Schema"
    ..description = "A schema for validating user profile objects"
    ..required = ['id', 'name', 'email']
    ..minProperties = 3
    ..maxProperties = 10;

  print('Schema created: ${userProfileSchema.toJson()}');

  // Create schemas for individual properties
  var idSchema = JsonSchema()
    ..type = JsonType.string
    ..pattern = r'^[a-zA-Z0-9-]+$';

  var nameSchema = JsonSchema()
    ..type = JsonType.string
    ..minLength = 2
    ..maxLength = 50;

  var emailSchema = JsonSchema()
    ..type = JsonType.string
    ..format = 'email';

  var ageSchema = JsonSchema()
    ..type = JsonType.integer
    ..minimum = 0
    ..maximum = 120;

  // Add property schemas to the user profile schema
  userProfileSchema.properties = {
    'id': idSchema,
    'name': nameSchema,
    'email': emailSchema,
    'age': ageSchema,
  };

  // Add pattern properties for custom fields
  var stringSchema = JsonSchema()..type = JsonType.string;

  userProfileSchema.patternProperties = {'^custom_': stringSchema};

  // Restrict additional properties
  userProfileSchema.additionalProperties = false;

  print('\nSchema with properties defined: ${userProfileSchema.toJson()}');

  // Test valid user profiles
  print('\nValidating valid user profiles:');
  var validProfiles = [
    {
      'id': 'user-123',
      'name': 'John Doe',
      'email': 'john@example.com',
      'age': 30,
      'custom_field': 'Custom value',
    },
    {'id': 'user-456', 'name': 'Jane Smith', 'email': 'jane@example.com'},
  ];

  for (var profile in validProfiles) {
    bool isValid = userProfileSchema.validateObject(profile);
    print('  $profile: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Test invalid user profiles
  print('\nValidating invalid user profiles:');
  var invalidProfiles = [
    {
      'id': 'user-123',
      'name': 'John Doe',
      // Missing required email field
    },
    {
      'id': 'user-456',
      'name': 'J', // Name too short
      'email': 'not-an-email',
    },
    {
      'id': 'user-789',
      'name': 'Bob Smith',
      'email': 'bob@example.com',
      'age': 150, // Age above maximum
    },
    {
      'id': 'user-101',
      'name': 'Alice Johnson',
      'email': 'alice@example.com',
      'invalid_field':
          'This field is not allowed', // Additional property not allowed
    },
  ];

  for (var profile in invalidProfiles) {
    bool isValid = userProfileSchema.validateObject(profile);
    print('  $profile: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate property dependencies
  print('\nDemonstrating property dependencies:');
  var addressSchema = ObjectJsonSchema()
    ..title = "Address Schema"
    ..description = "A schema for validating address objects with dependencies"
    ..dependencies = {
      'shipping_address': ['billing_address'],
    };

  print('Schema created: ${addressSchema.toJson()}');

  var validAddresses = [
    {}, // Empty object is valid
    {'billing_address': '123 Main St'}, // Only billing address is valid
    {
      'shipping_address': '456 Elm St',
      'billing_address': '123 Main St',
    }, // Both addresses are valid
  ];

  var invalidAddresses = [
    {'shipping_address': '456 Elm St'}, // Missing dependent property
  ];

  print('\nValidating addresses with dependencies:');
  for (var address in validAddresses) {
    bool isValid = addressSchema.validateObject(address);
    print('  $address: ${isValid ? 'Valid' : 'Invalid'}');
  }

  for (var address in invalidAddresses) {
    bool isValid = addressSchema.validateObject(address);
    print('  $address: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate property names validation
  print('\nDemonstrating property names validation:');
  var configSchema = ObjectJsonSchema()
    ..title = "Config Schema"
    ..description =
        "A schema for validating configuration objects with property name constraints";

  var propNameSchema = JsonSchema()
    ..type = JsonType.string
    ..pattern = r'^[a-z][a-z0-9_]*$';

  configSchema.propertyNames = propNameSchema;

  print('Schema created: ${configSchema.toJson()}');

  var validConfigs = [
    {}, // Empty object is valid
    {'setting1': 'value1', 'setting2': 'value2'},
    {'api_key': '12345', 'timeout_ms': 5000},
  ];

  var invalidConfigs = [
    {'Setting1': 'value1'}, // Starts with uppercase
    {'1setting': 'value1'}, // Starts with number
    {'api-key': '12345'}, // Contains hyphen
  ];

  print('\nValidating configs with property name constraints:');
  for (var config in validConfigs) {
    bool isValid = configSchema.validateObject(config);
    print('  $config: ${isValid ? 'Valid' : 'Invalid'}');
  }

  for (var config in invalidConfigs) {
    bool isValid = configSchema.validateObject(config);
    print('  $config: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate type restriction
  print('\nDemonstrating type restriction:');
  try {
    print('  Attempting to set type to string...');
    userProfileSchema.type = JsonType.string;
    print('  Failed: Should not allow setting type to string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Demonstrate defaultValue restriction
  print('\nDemonstrating defaultValue restriction:');
  try {
    print('  Setting defaultValue to a valid object...');
    userProfileSchema.defaultValue = {
      'id': 'default-user',
      'name': 'Default User',
      'email': 'default@example.com',
    };
    print('  Success: defaultValue set to ${userProfileSchema.defaultValue}');

    print('  Attempting to set defaultValue to a string...');
    userProfileSchema.defaultValue = 'not-an-object';
    print('  Failed: Should not allow setting defaultValue to a string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }
}

void arrayJsonSchemaExample() {
  print('Creating an ArrayJsonSchema for list validation...');

  // Create an ArrayJsonSchema for list validation
  var listSchema = ArrayJsonSchema()
    ..title = "List Schema"
    ..description = "A schema for validating lists of items"
    ..minItems = 1
    ..maxItems = 10
    ..uniqueItems = true;

  print('Schema created: ${listSchema.toJson()}');

  // Test valid arrays
  print('\nValidating valid arrays:');
  var validArrays = [
    [1, 2, 3],
    ['a', 'b', 'c'],
    [true, false],
    [1.5, 2.5, 3.5],
  ];

  for (var value in validArrays) {
    bool isValid = listSchema.validateArray(value);
    print('  $value: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Test invalid arrays
  print('\nValidating invalid arrays:');
  var invalidArrays = [
    [], // Too few items
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], // Too many items
    [1, 2, 2], // Duplicate items
    'not an array', // Not an array
    123, // Not an array
    true, // Not an array
    {'key': 'value'}, // Not an array
  ];

  for (var value in invalidArrays) {
    bool isValid = listSchema.validateArray(value);
    print('  $value: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate type restriction
  print('\nDemonstrating type restriction:');
  try {
    print('  Attempting to set type to string...');
    listSchema.type = JsonType.string;
    print('  Failed: Should not allow setting type to string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Demonstrate defaultValue restriction
  print('\nDemonstrating defaultValue restriction:');
  try {
    print('  Setting defaultValue to a valid array...');
    listSchema.defaultValue = [1, 2, 3];
    print('  Success: defaultValue set to ${listSchema.defaultValue}');

    print('  Attempting to set defaultValue to a string...');
    listSchema.defaultValue = 'not an array';
    print('  Failed: Should not allow setting defaultValue to a string');
  } catch (e) {
    print('  Success: ${e.toString()}');
  }

  // Create a schema with items constraint
  print('\nCreating an ArrayJsonSchema with items constraint...');
  var numberSchema = JsonSchema()
    ..type = JsonType.number
    ..minimum = 0
    ..maximum = 100;

  var numbersArraySchema = ArrayJsonSchema()
    ..title = "Numbers Array Schema"
    ..description = "A schema for validating arrays of numbers"
    ..items = numberSchema;

  print('Schema created: ${numbersArraySchema.toJson()}');

  // Test items constraint
  print('\nTesting items constraint:');
  var numberArrays = [
    [10, 20, 30],
    [0, 50, 100],
    [-10, 20, 30], // Contains negative number
    [10, 20, 'string'], // Contains non-number
    [10, 20, 110], // Contains number > 100
  ];

  for (var array in numberArrays) {
    bool isValid = numbersArraySchema.validateArray(array);
    print('  $array: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Create a schema with tuple validation
  print('\nCreating an ArrayJsonSchema for tuple validation...');
  var stringSchema = JsonSchema()..type = JsonType.string;

  var booleanSchema = JsonSchema()..type = JsonType.boolean;

  var tupleSchema = ArrayJsonSchema()
    ..title = "Tuple Schema"
    ..description = "A schema for validating tuples [string, number, boolean]"
    ..items = [stringSchema, numberSchema, booleanSchema]
    ..additionalItems = false; // No additional items allowed

  print('Schema created: ${tupleSchema.toJson()}');

  // Test tuple validation
  print('\nTesting tuple validation:');
  var tuples = [
    ['name', 25, true], // Valid tuple
    ['name', 25], // Valid partial tuple
    ['name', 25, true, 'extra'], // Invalid - extra item
    [25, 'name', true], // Invalid - wrong order
  ];

  for (var tuple in tuples) {
    bool isValid = tupleSchema.validateArray(tuple);
    print('  $tuple: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Create a schema with contains constraint
  print('\nCreating an ArrayJsonSchema with contains constraint...');
  var containsSchema = ArrayJsonSchema()
    ..title = "Contains Schema"
    ..description =
        "A schema for validating arrays that contain at least one even number"
    ..contains = NumberJsonSchema()
    ..multipleOf = 2;

  print('Schema created: ${containsSchema.toJson()}');

  // Test contains constraint
  print('\nTesting contains constraint:');
  var containsArrays = [
    [1, 2, 3], // Contains even number
    [2, 4, 6], // All even numbers
    [1, 3, 5], // No even numbers
    [], // Empty array
  ];

  for (var array in containsArrays) {
    bool isValid = containsSchema.validateArray(array);
    print('  $array: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate enum constraint
  print('\nDemonstrating enum constraint:');
  var enumSchema = ArrayJsonSchema()
    ..title = "Enum Schema"
    ..description = "A schema for validating arrays with enum constraint"
    ..enumValues = [
      [1, 2, 3],
      ['a', 'b', 'c'],
    ];

  print('Schema created: ${enumSchema.toJson()}');

  var enumArrays = [
    [1, 2, 3], // In enum
    ['a', 'b', 'c'], // In enum
    [4, 5, 6], // Not in enum
    [], // Not in enum
  ];

  for (var array in enumArrays) {
    bool isValid = enumSchema.validateArray(array);
    print('  $array: ${isValid ? 'Valid' : 'Invalid'}');
  }

  // Demonstrate const constraint
  print('\nDemonstrating const constraint:');
  var constSchema = ArrayJsonSchema()
    ..title = "Const Schema"
    ..description = "A schema for validating arrays with const constraint"
    ..constValue = [1, 2, 3];

  print('Schema created: ${constSchema.toJson()}');

  var constArrays = [
    [1, 2, 3], // Equal to const
    [3, 2, 1], // Not equal to const
    [1, 2], // Not equal to const
  ];

  for (var array in constArrays) {
    bool isValid = constSchema.validateArray(array);
    print('  $array: ${isValid ? 'Valid' : 'Invalid'}');
  }
}

void validationErrorReportingExample() {
  print('Demonstrating the new structured error reporting system...');

  // Example 1: String validation with detailed error reporting
  print('\n1. String Validation with Detailed Error Reporting:');
  var emailSchema = StringJsonSchema()
    ..title = "Email Schema"
    ..description = "A schema for validating email addresses"
    ..format = "email"
    ..minLength = 5
    ..maxLength = 100;

  print('Validating an invalid email address:');
  var invalidEmail = 'not-an-email';

  // Using the new validate method that returns a ValidationResult
  var result = emailSchema.validate(invalidEmail, '/user/email');

  if (!result.isValid) {
    print('  Validation failed with ${result.errors.length} errors:');
    for (var error in result.errors) {
      print('  - Error at ${error.path}:');
      print('    Keyword: ${error.keyword}');
      print('    Message: ${error.message}');
      print('    Expected: ${error.expected}, Actual: ${error.actual}');
    }
  }

  // Example 2: Number validation with multiple errors
  print('\n2. Number Validation with Multiple Errors:');
  var ageSchema = NumberJsonSchema()
    ..title = "Age Schema"
    ..description = "A schema for validating age values"
    ..minimum = 18
    ..maximum = 120
    ..multipleOf = 1; // Integer values only

  print('Validating an invalid age value:');
  var invalidAge = 16.5; // Too young and not an integer

  result = ageSchema.validate(invalidAge, '/user/age');

  if (!result.isValid) {
    print('  Validation failed with ${result.errors.length} errors:');
    for (var error in result.errors) {
      print('  - Error at ${error.path}:');
      print('    Keyword: ${error.keyword}');
      print('    Message: ${error.message}');
    }
  }

  // Example 3: Filtering errors by keyword
  print('\n3. Filtering Errors by Keyword:');

  // Get only multipleOf errors
  var multipleOfErrors = result.forKeyword('multipleOf');

  print('  multipleOf errors: ${multipleOfErrors.errors.length}');
  for (var error in multipleOfErrors.errors) {
    print('  - ${error.message}');
  }

  // Example 4: Complex object validation with nested errors
  print('\n4. Complex Object Validation with Nested Errors:');

  // Create a schema for a user profile
  var nameSchema = StringJsonSchema()..minLength = 3;

  var emailSchema2 = StringJsonSchema()..format = 'email';

  var ageSchema2 = NumberJsonSchema()
    ..minimum = 18
    ..maximum = 120;

  // Create a user profile with invalid data
  var invalidUser = {
    'name': 'Jo', // Too short
    'email': 'not-an-email', // Invalid format
    'age': 16, // Too young
  };

  print('Validating an invalid user profile:');
  print('  $invalidUser');

  // Validate each field and collect errors
  var nameResult = nameSchema.validate(
    invalidUser['name'] as String?,
    '/user/name',
  );
  var emailResult = emailSchema2.validate(
    invalidUser['email'] as String?,
    '/user/email',
  );
  var ageResult = ageSchema2.validate(invalidUser['age'], '/user/age');

  // Combine all validation results
  var combinedResult = ValidationResult.combine([
    nameResult,
    emailResult,
    ageResult,
  ]);

  if (!combinedResult.isValid) {
    print('  Validation failed with ${combinedResult.errors.length} errors:');
    for (var error in combinedResult.errors) {
      print('  - Error at ${error.path}:');
      print('    Keyword: ${error.keyword}');
      print('    Message: ${error.message}');
    }
  }

  // Example 5: Backward compatibility
  print('\n5. Backward Compatibility:');

  // Using the old boolean validation method
  bool isValid = nameSchema.validateString(invalidUser['name'] as String?);
  print('  Using validateString(): ${isValid ? 'Valid' : 'Invalid'}');

  // Using the old exception-throwing validation method
  try {
    nameSchema.validateStringWithExceptions(invalidUser['name'] as String?);
    print('  Using validateStringWithExceptions(): Valid');
  } catch (e) {
    print('  Using validateStringWithExceptions(): Invalid - ${e.toString()}');
  }

  print('\nThe new validation error reporting system provides:');
  print(
    '  1. Detailed error information (path, keyword, expected vs. actual values)',
  );
  print('  2. Multiple error reporting in a single validation');
  print('  3. Error filtering capabilities');
  print('  4. Ability to combine validation results from multiple validations');
  print('  5. Backward compatibility with existing code');
}
1
likes
0
points
52
downloads

Publisher

verified publisherindiealexh.com

Weekly Downloads

Utilities for building Json Schema Documents to ensure they are valid and assist with reducing the complexity of creating a schema manually

Homepage
Repository (GitHub)
View/report issues

Topics

#json #json-schema #jsonschema #schema

License

unknown (license)

More

Packages that depend on json_schema_utils