From 00000a430681e5f7b4b572ffd228703f2e012e69 Mon Sep 17 00:00:00 2001 From: Clemens Vasters Date: Tue, 25 Nov 2025 12:31:09 +0100 Subject: [PATCH 1/4] Reorganize samples: move schemas into directories, restructure imports Core samples: - Move all schema files into their respective numbered directories - Each directory now contains schema.struct.json + example files - Update validate-all.ps1 with new paths - Update README.md with new directory structure Import samples: - Renumber examples from 03-06 to 01-04 - Move library schemas (person-library, financial-library) into 02-namespace-import/ - Update all import references to use relative paths - Update validate-imports.ps1 with new structure - Update README.md to document reorganized layout All validations pass successfully. --- samples/core/01-basic-person/example1.json | 16 ++ samples/core/01-basic-person/example2.json | 9 + samples/core/01-basic-person/example3.json | 8 + .../core/01-basic-person/schema.struct.json | 42 ++++ samples/core/02-address/example1.json | 8 + samples/core/02-address/example2.json | 8 + samples/core/02-address/example3.json | 6 + samples/core/02-address/schema.struct.json | 37 ++++ samples/core/03-financial-types/example1.json | 45 ++++ samples/core/03-financial-types/example2.json | 28 +++ samples/core/03-financial-types/example3.json | 45 ++++ .../03-financial-types/schema.struct.json | 114 ++++++++++ .../core/04-datetime-examples/example1.json | 26 +++ .../core/04-datetime-examples/example2.json | 20 ++ .../core/04-datetime-examples/example3.json | 18 ++ .../04-datetime-examples/schema.struct.json | 108 ++++++++++ samples/core/05-collections/example1.json | 65 ++++++ samples/core/05-collections/example2.json | 34 +++ samples/core/05-collections/example3.json | 15 ++ .../core/05-collections/schema.struct.json | 110 ++++++++++ samples/core/06-tuples/example1.json | 30 +++ samples/core/06-tuples/example2.json | 21 ++ samples/core/06-tuples/example3.json | 25 +++ samples/core/06-tuples/schema.struct.json | 138 ++++++++++++ samples/core/07-unions/example1.json | 20 ++ samples/core/07-unions/example2.json | 21 ++ samples/core/07-unions/example3.json | 20 ++ samples/core/07-unions/schema.struct.json | 134 ++++++++++++ samples/core/08-namespaces/example1.json | 108 ++++++++++ samples/core/08-namespaces/example2.json | 35 ++++ samples/core/08-namespaces/example3.json | 35 ++++ samples/core/08-namespaces/schema.struct.json | 127 ++++++++++++ samples/core/09-extensions/example1.json | 102 +++++++++ samples/core/09-extensions/example2.json | 42 ++++ samples/core/09-extensions/example3.json | 31 +++ samples/core/09-extensions/schema.struct.json | 185 +++++++++++++++++ .../10-discriminated-unions/example1.json | 33 +++ .../10-discriminated-unions/example2.json | 31 +++ .../10-discriminated-unions/example3.json | 34 +++ .../schema.struct.json | 196 ++++++++++++++++++ samples/core/11-sets-and-maps/example1.json | 128 ++++++++++++ samples/core/11-sets-and-maps/example2.json | 91 ++++++++ samples/core/11-sets-and-maps/example3.json | 94 +++++++++ .../core/11-sets-and-maps/schema.struct.json | 134 ++++++++++++ samples/core/README.md | 187 +++++++++++++++++ samples/core/validate-all.ps1 | 78 +++++++ samples/import/.editorconfig | 8 + samples/import/.github/CODEOWNERS | 3 + samples/import/.github/workflows/LICENSE.md | 1 + samples/import/.github/workflows/archive.yml | 44 ++++ samples/import/.github/workflows/ghpages.yml | 60 ++++++ samples/import/.github/workflows/publish.yml | 57 +++++ samples/import/.github/workflows/update.yml | 36 ++++ samples/import/.gitignore | 24 +++ samples/import/01-root-import/example.json | 26 +++ .../import/01-root-import/schema.struct.json | 40 ++++ .../import/02-namespace-import/example.json | 82 ++++++++ .../financial-library/schema.struct.json | 91 ++++++++ .../person-library/schema.struct.json | 93 +++++++++ .../02-namespace-import/schema.struct.json | 85 ++++++++ .../import/03-importdefs-only/example.json | 24 +++ .../03-importdefs-only/schema.struct.json | 60 ++++++ samples/import/04-shadowing/example.json | 41 ++++ .../import/04-shadowing/schema.struct.json | 126 +++++++++++ samples/import/CONTRIBUTING.md | 33 +++ samples/import/LICENSE.md | 4 + samples/import/Makefile | 15 ++ samples/import/README.md | 51 +++++ samples/import/validate-imports.ps1 | 60 ++++++ 69 files changed, 3906 insertions(+) create mode 100644 samples/core/01-basic-person/example1.json create mode 100644 samples/core/01-basic-person/example2.json create mode 100644 samples/core/01-basic-person/example3.json create mode 100644 samples/core/01-basic-person/schema.struct.json create mode 100644 samples/core/02-address/example1.json create mode 100644 samples/core/02-address/example2.json create mode 100644 samples/core/02-address/example3.json create mode 100644 samples/core/02-address/schema.struct.json create mode 100644 samples/core/03-financial-types/example1.json create mode 100644 samples/core/03-financial-types/example2.json create mode 100644 samples/core/03-financial-types/example3.json create mode 100644 samples/core/03-financial-types/schema.struct.json create mode 100644 samples/core/04-datetime-examples/example1.json create mode 100644 samples/core/04-datetime-examples/example2.json create mode 100644 samples/core/04-datetime-examples/example3.json create mode 100644 samples/core/04-datetime-examples/schema.struct.json create mode 100644 samples/core/05-collections/example1.json create mode 100644 samples/core/05-collections/example2.json create mode 100644 samples/core/05-collections/example3.json create mode 100644 samples/core/05-collections/schema.struct.json create mode 100644 samples/core/06-tuples/example1.json create mode 100644 samples/core/06-tuples/example2.json create mode 100644 samples/core/06-tuples/example3.json create mode 100644 samples/core/06-tuples/schema.struct.json create mode 100644 samples/core/07-unions/example1.json create mode 100644 samples/core/07-unions/example2.json create mode 100644 samples/core/07-unions/example3.json create mode 100644 samples/core/07-unions/schema.struct.json create mode 100644 samples/core/08-namespaces/example1.json create mode 100644 samples/core/08-namespaces/example2.json create mode 100644 samples/core/08-namespaces/example3.json create mode 100644 samples/core/08-namespaces/schema.struct.json create mode 100644 samples/core/09-extensions/example1.json create mode 100644 samples/core/09-extensions/example2.json create mode 100644 samples/core/09-extensions/example3.json create mode 100644 samples/core/09-extensions/schema.struct.json create mode 100644 samples/core/10-discriminated-unions/example1.json create mode 100644 samples/core/10-discriminated-unions/example2.json create mode 100644 samples/core/10-discriminated-unions/example3.json create mode 100644 samples/core/10-discriminated-unions/schema.struct.json create mode 100644 samples/core/11-sets-and-maps/example1.json create mode 100644 samples/core/11-sets-and-maps/example2.json create mode 100644 samples/core/11-sets-and-maps/example3.json create mode 100644 samples/core/11-sets-and-maps/schema.struct.json create mode 100644 samples/core/README.md create mode 100644 samples/core/validate-all.ps1 create mode 100644 samples/import/.editorconfig create mode 100644 samples/import/.github/CODEOWNERS create mode 100644 samples/import/.github/workflows/LICENSE.md create mode 100644 samples/import/.github/workflows/archive.yml create mode 100644 samples/import/.github/workflows/ghpages.yml create mode 100644 samples/import/.github/workflows/publish.yml create mode 100644 samples/import/.github/workflows/update.yml create mode 100644 samples/import/.gitignore create mode 100644 samples/import/01-root-import/example.json create mode 100644 samples/import/01-root-import/schema.struct.json create mode 100644 samples/import/02-namespace-import/example.json create mode 100644 samples/import/02-namespace-import/financial-library/schema.struct.json create mode 100644 samples/import/02-namespace-import/person-library/schema.struct.json create mode 100644 samples/import/02-namespace-import/schema.struct.json create mode 100644 samples/import/03-importdefs-only/example.json create mode 100644 samples/import/03-importdefs-only/schema.struct.json create mode 100644 samples/import/04-shadowing/example.json create mode 100644 samples/import/04-shadowing/schema.struct.json create mode 100644 samples/import/CONTRIBUTING.md create mode 100644 samples/import/LICENSE.md create mode 100644 samples/import/Makefile create mode 100644 samples/import/README.md create mode 100644 samples/import/validate-imports.ps1 diff --git a/samples/core/01-basic-person/example1.json b/samples/core/01-basic-person/example1.json new file mode 100644 index 0000000..0393f7b --- /dev/null +++ b/samples/core/01-basic-person/example1.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://schemas.example.com/basic-person", + "firstName": "John", + "lastName": "Doe", + "dateOfBirth": "1990-05-15", + "email": "john.doe@example.com", + "age": 33, + "profileData": { + "preferences": { + "theme": "dark", + "notifications": true + }, + "badges": ["verified", "premium"] + }, + "isActive": true +} \ No newline at end of file diff --git a/samples/core/01-basic-person/example2.json b/samples/core/01-basic-person/example2.json new file mode 100644 index 0000000..9050c08 --- /dev/null +++ b/samples/core/01-basic-person/example2.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://schemas.example.com/basic-person", + "firstName": "Jane", + "lastName": "Smith", + "dateOfBirth": "1985-12-03", + "email": "jane.smith@company.org", + "age": 28, + "profileData": "Simple string profile" +} \ No newline at end of file diff --git a/samples/core/01-basic-person/example3.json b/samples/core/01-basic-person/example3.json new file mode 100644 index 0000000..2d83775 --- /dev/null +++ b/samples/core/01-basic-person/example3.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://schemas.example.com/basic-person", + "firstName": "Michael", + "lastName": "Johnson", + "dateOfBirth": "1978-09-22", + "email": "mike.j@example.net", + "isActive": false +} \ No newline at end of file diff --git a/samples/core/01-basic-person/schema.struct.json b/samples/core/01-basic-person/schema.struct.json new file mode 100644 index 0000000..6058273 --- /dev/null +++ b/samples/core/01-basic-person/schema.struct.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/basic-person", + "name": "Person", + "type": "object", + "description": "A basic person schema demonstrating primitive types", + "properties": { + "firstName": { + "type": "string", + "description": "The person's first name", + "maxLength": 50 + }, + "lastName": { + "type": "string", + "description": "The person's last name", + "maxLength": 50 + }, + "dateOfBirth": { + "type": "date", + "description": "The person's date of birth" + }, + "email": { + "type": "string", + "description": "The person's email address", + "examples": ["john.doe@example.com"] + }, + "age": { + "type": "int8", + "description": "The person's age in years (0-127)" + }, + "profileData": { + "type": "any", + "description": "Flexible profile data - can be any JSON value" + }, + "isActive": { + "type": "boolean", + "description": "Whether the person is currently active", + "default": true + } + }, + "required": ["firstName", "lastName", "email"] +} \ No newline at end of file diff --git a/samples/core/02-address/example1.json b/samples/core/02-address/example1.json new file mode 100644 index 0000000..8dd32fd --- /dev/null +++ b/samples/core/02-address/example1.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://schemas.example.com/address", + "street": "123 Main Street", + "city": "New York", + "state": "New York", + "zipCode": "10001", + "country": "US" +} \ No newline at end of file diff --git a/samples/core/02-address/example2.json b/samples/core/02-address/example2.json new file mode 100644 index 0000000..1d29b22 --- /dev/null +++ b/samples/core/02-address/example2.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://schemas.example.com/address", + "street": "456 Oak Avenue", + "city": "Toronto", + "state": "Ontario", + "zipCode": "M5V 3A8", + "country": "CA" +} \ No newline at end of file diff --git a/samples/core/02-address/example3.json b/samples/core/02-address/example3.json new file mode 100644 index 0000000..a404025 --- /dev/null +++ b/samples/core/02-address/example3.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://schemas.example.com/address", + "street": "789 Queen Street", + "city": "London", + "country": "UK" +} \ No newline at end of file diff --git a/samples/core/02-address/schema.struct.json b/samples/core/02-address/schema.struct.json new file mode 100644 index 0000000..88976d7 --- /dev/null +++ b/samples/core/02-address/schema.struct.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/address", + "name": "Address", + "type": "object", + "description": "An address schema demonstrating required fields and additional properties", + "properties": { + "street": { + "type": "string", + "description": "Street address line", + "maxLength": 100 + }, + "city": { + "type": "string", + "description": "City name", + "maxLength": 50 + }, + "state": { + "type": "string", + "description": "State or province", + "maxLength": 50 + }, + "zipCode": { + "type": "string", + "description": "Postal/ZIP code", + "maxLength": 20 + }, + "country": { + "type": "string", + "description": "Country code", + "enum": ["US", "CA", "MX", "UK", "DE", "FR", "JP", "AU"], + "examples": ["US", "CA"] + } + }, + "required": ["street", "city", "country"], + "additionalProperties": false +} \ No newline at end of file diff --git a/samples/core/03-financial-types/example1.json b/samples/core/03-financial-types/example1.json new file mode 100644 index 0000000..431df47 --- /dev/null +++ b/samples/core/03-financial-types/example1.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://schemas.example.com/financial-types", + "invoiceNumber": "INV-2023-001", + "issueDate": "2023-11-13", + "dueDate": "2023-12-13", + "lineItems": [ + { + "description": "Professional Services", + "quantity": "10.000", + "unitPrice": { + "amount": "150.00", + "currency": "USD" + }, + "totalPrice": { + "amount": "1500.00", + "currency": "USD" + } + }, + { + "description": "Software License", + "quantity": "1.000", + "unitPrice": { + "amount": "299.99", + "currency": "USD" + }, + "totalPrice": { + "amount": "299.99", + "currency": "USD" + } + } + ], + "subtotal": { + "amount": "1799.99", + "currency": "USD" + }, + "taxRate": "0.0875", + "taxAmount": { + "amount": "157.50", + "currency": "USD" + }, + "totalAmount": { + "amount": "1957.49", + "currency": "USD" + } +} \ No newline at end of file diff --git a/samples/core/03-financial-types/example2.json b/samples/core/03-financial-types/example2.json new file mode 100644 index 0000000..5167d71 --- /dev/null +++ b/samples/core/03-financial-types/example2.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schemas.example.com/financial-types", + "invoiceNumber": "INV-2023-002", + "issueDate": "2023-11-14", + "dueDate": "2023-11-28", + "lineItems": [ + { + "description": "Monthly Subscription", + "quantity": "1.000", + "unitPrice": { + "amount": "49.99", + "currency": "EUR" + }, + "totalPrice": { + "amount": "49.99", + "currency": "EUR" + } + } + ], + "subtotal": { + "amount": "49.99", + "currency": "EUR" + }, + "totalAmount": { + "amount": "49.99", + "currency": "EUR" + } +} \ No newline at end of file diff --git a/samples/core/03-financial-types/example3.json b/samples/core/03-financial-types/example3.json new file mode 100644 index 0000000..407a076 --- /dev/null +++ b/samples/core/03-financial-types/example3.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://schemas.example.com/financial-types", + "invoiceNumber": "INV-2023-003", + "issueDate": "2023-11-15", + "dueDate": "2023-12-15", + "lineItems": [ + { + "description": "Consulting Hours", + "quantity": "25.500", + "unitPrice": { + "amount": "200.00", + "currency": "GBP" + }, + "totalPrice": { + "amount": "5100.00", + "currency": "GBP" + } + }, + { + "description": "Travel Expenses", + "quantity": "1.000", + "unitPrice": { + "amount": "450.75", + "currency": "GBP" + }, + "totalPrice": { + "amount": "450.75", + "currency": "GBP" + } + } + ], + "subtotal": { + "amount": "5550.75", + "currency": "GBP" + }, + "taxRate": "0.2000", + "taxAmount": { + "amount": "1110.15", + "currency": "GBP" + }, + "totalAmount": { + "amount": "6660.90", + "currency": "GBP" + } +} \ No newline at end of file diff --git a/samples/core/03-financial-types/schema.struct.json b/samples/core/03-financial-types/schema.struct.json new file mode 100644 index 0000000..d3ec955 --- /dev/null +++ b/samples/core/03-financial-types/schema.struct.json @@ -0,0 +1,114 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/financial-types", + "name": "FinancialDocument", + "description": "Demonstrates extended numeric types for financial calculations", + "$root": "#/definitions/Invoice", + "definitions": { + "Money": { + "type": "object", + "description": "Monetary amount with currency", + "properties": { + "amount": { + "type": "decimal", + "description": "The monetary amount", + "precision": 15, + "scale": 2 + }, + "currency": { + "type": "string", + "description": "ISO 4217 currency code", + "enum": ["USD", "EUR", "GBP", "JPY", "CAD", "AUD"], + "examples": ["USD", "EUR"] + } + }, + "required": ["amount", "currency"] + }, + "LineItem": { + "type": "object", + "description": "A single line item in an invoice", + "properties": { + "description": { + "type": "string", + "description": "Description of the item", + "maxLength": 200 + }, + "quantity": { + "type": "decimal", + "description": "Quantity of items", + "precision": 10, + "scale": 3 + }, + "unitPrice": { + "type": { "$ref": "#/definitions/Money" }, + "description": "Price per unit" + }, + "totalPrice": { + "type": { "$ref": "#/definitions/Money" }, + "description": "Total price for this line item" + } + }, + "required": ["description", "quantity", "unitPrice", "totalPrice"] + }, + "Invoice": { + "type": "object", + "description": "A business invoice", + "properties": { + "invoiceNumber": { + "type": "string", + "description": "Unique invoice identifier", + "maxLength": 50 + }, + "sequenceNumber": { + "type": "int64", + "description": "Sequential invoice number for audit trail" + }, + "issueDate": { + "type": "date", + "description": "Date the invoice was issued" + }, + "dueDate": { + "type": "date", + "description": "Payment due date" + }, + "paymentTermsDays": { + "type": "int16", + "description": "Number of days for payment terms", + "default": 30 + }, + "lineItems": { + "type": "array", + "description": "List of invoice line items", + "items": { "type": { "$ref": "#/definitions/LineItem" }} + }, + "subtotal": { + "type": { "$ref": "#/definitions/Money" }, + "description": "Subtotal before taxes" + }, + "taxRate": { + "type": "decimal", + "description": "Tax rate as a decimal (e.g., 0.08 for 8%)", + "precision": 5, + "scale": 4 + }, + "taxAmount": { + "type": { "$ref": "#/definitions/Money" }, + "description": "Total tax amount" + }, + "totalAmount": { + "type": { "$ref": "#/definitions/Money" }, + "description": "Total amount due" + }, + "auditTrail": { + "type": "int128", + "description": "128-bit audit trail hash for financial integrity verification" + }, + "blockchainHash": { + "type": "uint128", + "description": "Unsigned 128-bit blockchain transaction hash for immutable record" + } + }, + "required": ["invoiceNumber", "issueDate", "dueDate", "lineItems", "subtotal", "totalAmount"] + } + } +} \ No newline at end of file diff --git a/samples/core/04-datetime-examples/example1.json b/samples/core/04-datetime-examples/example1.json new file mode 100644 index 0000000..10a30a6 --- /dev/null +++ b/samples/core/04-datetime-examples/example1.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://schemas.example.com/datetime-examples", + "id": "550e8400-e29b-41d4-a716-446655440001", + "title": "Weekly Team Meeting", + "description": "Regular team standup meeting to discuss progress and blockers", + "timeSlot": { + "startTime": "2023-11-20T09:00:00-08:00", + "endTime": "2023-11-20T10:00:00-08:00", + "duration": "PT1H" + }, + "location": "Conference Room A", + "organizerEmail": "team.lead@company.com", + "attendeeEmails": [ + "developer1@company.com", + "developer2@company.com", + "designer@company.com" + ], + "recurrence": { + "frequency": "WEEKLY", + "interval": 1, + "daysOfWeek": ["MON"], + "endDate": "2024-01-15" + }, + "createdAt": "2023-11-13T14:30:00Z", + "modifiedAt": "2023-11-13T14:30:00Z" +} \ No newline at end of file diff --git a/samples/core/04-datetime-examples/example2.json b/samples/core/04-datetime-examples/example2.json new file mode 100644 index 0000000..43642b6 --- /dev/null +++ b/samples/core/04-datetime-examples/example2.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schemas.example.com/datetime-examples", + "id": "550e8400-e29b-41d4-a716-446655440002", + "title": "Product Launch Event", + "description": "Annual product launch presentation with keynote speakers", + "timeSlot": { + "startTime": "2023-12-05T10:00:00+00:00", + "endTime": "2023-12-05T16:00:00+00:00", + "duration": "PT6H" + }, + "location": "Main Auditorium", + "organizerEmail": "events@company.com", + "attendeeEmails": [ + "ceo@company.com", + "cto@company.com", + "marketing@company.com", + "press@company.com" + ], + "createdAt": "2023-11-01T09:00:00Z" +} \ No newline at end of file diff --git a/samples/core/04-datetime-examples/example3.json b/samples/core/04-datetime-examples/example3.json new file mode 100644 index 0000000..6efb2a0 --- /dev/null +++ b/samples/core/04-datetime-examples/example3.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schemas.example.com/datetime-examples", + "id": "550e8400-e29b-41d4-a716-446655440003", + "title": "Daily Backup Process", + "timeSlot": { + "startTime": "2023-11-13T02:00:00Z", + "endTime": "2023-11-13T02:30:00Z", + "duration": "PT30M" + }, + "location": "Data Center", + "organizerEmail": "admin@company.com", + "recurrence": { + "frequency": "DAILY", + "interval": 1 + }, + "createdAt": "2023-01-01T00:00:00Z", + "modifiedAt": "2023-06-15T10:30:00Z" +} \ No newline at end of file diff --git a/samples/core/04-datetime-examples/schema.struct.json b/samples/core/04-datetime-examples/schema.struct.json new file mode 100644 index 0000000..0421849 --- /dev/null +++ b/samples/core/04-datetime-examples/schema.struct.json @@ -0,0 +1,108 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/datetime-examples", + "name": "EventSchedule", + "description": "Demonstrates date, time, and duration types for event management", + "$root": "#/definitions/Event", + "definitions": { + "TimeSlot": { + "type": "object", + "description": "A time slot with start and end times", + "properties": { + "startTime": { + "type": "datetime", + "description": "Event start time with timezone" + }, + "endTime": { + "type": "datetime", + "description": "Event end time with timezone" + }, + "duration": { + "type": "duration", + "description": "Duration of the event (ISO 8601 format)" + } + }, + "required": ["startTime", "endTime"] + }, + "RecurrenceRule": { + "type": "object", + "description": "Defines how an event repeats", + "properties": { + "frequency": { + "type": "string", + "description": "How often the event repeats", + "enum": ["DAILY", "WEEKLY", "MONTHLY", "YEARLY"] + }, + "interval": { + "type": "uint16", + "description": "Interval between occurrences", + "default": 1 + }, + "endDate": { + "type": "date", + "description": "When the recurrence ends" + }, + "daysOfWeek": { + "type": "array", + "description": "Days of week for weekly recurrence", + "items": { + "type": "string", + "enum": ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"] + } + } + }, + "required": ["frequency"] + }, + "Event": { + "type": "object", + "description": "A calendar event with scheduling information", + "properties": { + "id": { + "type": "uuid", + "description": "Unique event identifier" + }, + "title": { + "type": "string", + "description": "Event title", + "maxLength": 100 + }, + "description": { + "type": "string", + "description": "Event description", + "maxLength": 1000 + }, + "timeSlot": { + "type": { "$ref": "#/definitions/TimeSlot" }, + "description": "When the event occurs" + }, + "location": { + "type": "string", + "description": "Event location", + "maxLength": 200 + }, + "organizerEmail": { + "type": "string", + "description": "Email of the event organizer" + }, + "attendeeEmails": { + "type": "array", + "description": "List of attendee email addresses", + "items": { "type": "string" } + }, + "recurrence": { + "type": { "$ref": "#/definitions/RecurrenceRule" }, + "description": "Recurrence pattern for repeating events" + }, + "createdAt": { + "type": "datetime", + "description": "When the event was created" + }, + "modifiedAt": { + "type": "datetime", + "description": "When the event was last modified" + } + }, + "required": ["id", "title", "timeSlot", "organizerEmail", "createdAt"] + } + } +} \ No newline at end of file diff --git a/samples/core/05-collections/example1.json b/samples/core/05-collections/example1.json new file mode 100644 index 0000000..3033a49 --- /dev/null +++ b/samples/core/05-collections/example1.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://schemas.example.com/collections", + "version": "1.0.0", + "lastUpdated": "2023-11-13T15:30:00Z", + "products": [ + { + "sku": "LAPTOP-001", + "name": "Professional Laptop", + "price": "1299.99", + "tags": ["laptop", "professional", "portable"], + "specifications": { + "processor": "Intel Core i7", + "memory": "16GB RAM", + "storage": "512GB SSD", + "display": "15.6 inch" + }, + "images": [ + "https://images.example.com/laptop-001-front.jpg", + "https://images.example.com/laptop-001-side.jpg" + ] + }, + { + "sku": "MOUSE-001", + "name": "Wireless Mouse", + "price": "29.99", + "tags": ["mouse", "wireless", "ergonomic"], + "specifications": { + "connectivity": "Bluetooth 5.0", + "battery": "AAA x 2", + "weight": "85g" + }, + "images": [ + "https://images.example.com/mouse-001.jpg" + ] + } + ], + "categories": [ + { + "id": "ELECTRONICS", + "name": "Electronics", + "productSkus": ["LAPTOP-001", "MOUSE-001"] + }, + { + "id": "COMPUTERS", + "name": "Computers", + "parentId": "ELECTRONICS", + "productSkus": ["LAPTOP-001"] + } + ], + "productBySku": { + "LAPTOP-001": { + "sku": "LAPTOP-001", + "name": "Professional Laptop", + "price": "1299.99", + "tags": ["laptop", "professional", "portable"], + "specifications": { + "processor": "Intel Core i7", + "memory": "16GB RAM", + "storage": "512GB SSD" + }, + "images": ["https://images.example.com/laptop-001-front.jpg"] + } + }, + "featuredSkus": ["LAPTOP-001"] +} \ No newline at end of file diff --git a/samples/core/05-collections/example2.json b/samples/core/05-collections/example2.json new file mode 100644 index 0000000..7c1fb71 --- /dev/null +++ b/samples/core/05-collections/example2.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schemas.example.com/collections", + "version": "2.1.0", + "lastUpdated": "2023-11-14T08:00:00Z", + "products": [ + { + "sku": "BOOK-001", + "name": "Programming Guide", + "price": "45.00", + "tags": ["book", "programming", "education"], + "specifications": { + "pages": "350", + "language": "English", + "format": "Paperback" + }, + "images": ["https://images.example.com/book-001-cover.jpg"] + } + ], + "categories": [ + { + "id": "BOOKS", + "name": "Books", + "productSkus": ["BOOK-001"] + }, + { + "id": "TECHNICAL", + "name": "Technical Books", + "parentId": "BOOKS", + "productSkus": ["BOOK-001"] + } + ], + "productBySku": {}, + "featuredSkus": [] +} \ No newline at end of file diff --git a/samples/core/05-collections/example3.json b/samples/core/05-collections/example3.json new file mode 100644 index 0000000..d432d2c --- /dev/null +++ b/samples/core/05-collections/example3.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schemas.example.com/collections", + "version": "1.5.2", + "lastUpdated": "2023-11-15T12:45:00Z", + "products": [], + "categories": [ + { + "id": "CLOTHING", + "name": "Clothing", + "productSkus": [] + } + ], + "productBySku": {}, + "featuredSkus": [] +} \ No newline at end of file diff --git a/samples/core/05-collections/schema.struct.json b/samples/core/05-collections/schema.struct.json new file mode 100644 index 0000000..7bf1967 --- /dev/null +++ b/samples/core/05-collections/schema.struct.json @@ -0,0 +1,110 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/collections", + "name": "ProductCatalog", + "description": "Demonstrates array, set, and map collection types", + "$root": "#/definitions/Catalog", + "definitions": { + "Product": { + "type": "object", + "description": "A product in the catalog", + "properties": { + "sku": { + "type": "string", + "description": "Stock Keeping Unit identifier", + "maxLength": 20 + }, + "name": { + "type": "string", + "description": "Product name", + "maxLength": 100 + }, + "price": { + "type": "decimal", + "description": "Product price", + "precision": 10, + "scale": 2 + }, + "tags": { + "type": "set", + "description": "Unique product tags", + "items": { "type": "string" } + }, + "specifications": { + "type": "map", + "description": "Product specifications as key-value pairs", + "values": { "type": "string" } + }, + "images": { + "type": "array", + "description": "Ordered list of product image URLs", + "items": { "type": "uri" } + } + }, + "required": ["sku", "name", "price"] + }, + "Category": { + "type": "object", + "description": "Product category with nested subcategories", + "properties": { + "id": { + "type": "string", + "description": "Category identifier", + "maxLength": 20 + }, + "name": { + "type": "string", + "description": "Category name", + "maxLength": 50 + }, + "parentId": { + "type": "string", + "description": "Parent category ID for hierarchical structure", + "maxLength": 20 + }, + "productSkus": { + "type": "set", + "description": "Set of product SKUs in this category", + "items": { "type": "string" } + } + }, + "required": ["id", "name"] + }, + "Catalog": { + "type": "object", + "description": "Complete product catalog", + "properties": { + "version": { + "type": "string", + "description": "Catalog version", + "maxLength": 20 + }, + "lastUpdated": { + "type": "datetime", + "description": "When the catalog was last updated" + }, + "products": { + "type": "array", + "description": "List of all products in order", + "items": { "type": { "$ref": "#/definitions/Product" }} + }, + "categories": { + "type": "array", + "description": "List of product categories", + "items": { "type": { "$ref": "#/definitions/Category" }} + }, + "productBySku": { + "type": "map", + "description": "Quick lookup map from SKU to product", + "values": { "type": { "$ref": "#/definitions/Product" }} + }, + "featuredSkus": { + "type": "set", + "description": "Set of featured product SKUs", + "items": { "type": "string" } + } + }, + "required": ["version", "lastUpdated", "products", "categories"] + } + } +} \ No newline at end of file diff --git a/samples/core/06-tuples/example1.json b/samples/core/06-tuples/example1.json new file mode 100644 index 0000000..8fd6f8f --- /dev/null +++ b/samples/core/06-tuples/example1.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://schemas.example.com/tuples", + "name": "Weather Monitoring Network", + "description": "Network of weather monitoring stations across the region", + "dataPoints": [ + { + "id": "STATION_001", + "location": ["47.6062100", "-122.3320700"], + "measurements": [ + ["2023-11-13T12:00:00Z", "22.500000", 95], + ["2023-11-13T13:00:00Z", "23.200000", 98], + ["2023-11-13T14:00:00Z", "24.100000", 100] + ], + "visualColor": [0, 128, 255, 1.0] + }, + { + "id": "STATION_002", + "location": ["47.7210800", "-122.3197100"], + "measurements": [ + ["2023-11-13T12:00:00Z", "21.800000", 92], + ["2023-11-13T13:00:00Z", "22.400000", 94] + ], + "visualColor": [255, 128, 0, 0.8] + } + ], + "bounds": [ + ["47.6000000", "-122.4000000"], + ["47.8000000", "-122.2000000"] + ] +} \ No newline at end of file diff --git a/samples/core/06-tuples/example2.json b/samples/core/06-tuples/example2.json new file mode 100644 index 0000000..465b205 --- /dev/null +++ b/samples/core/06-tuples/example2.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schemas.example.com/tuples", + "name": "Traffic Analysis Dataset", + "description": "Vehicle traffic flow data from intersection cameras", + "dataPoints": [ + { + "id": "INTERSECTION_A1", + "location": ["40.7589000", "-73.9851000"], + "measurements": [ + ["2023-11-13T08:00:00Z", "125.000000", 85], + ["2023-11-13T09:00:00Z", "178.000000", 90], + ["2023-11-13T10:00:00Z", "145.000000", 88] + ], + "visualColor": [255, 0, 0] + } + ], + "bounds": [ + ["40.7500000", "-73.9900000"], + ["40.7600000", "-73.9800000"] + ] +} \ No newline at end of file diff --git a/samples/core/06-tuples/example3.json b/samples/core/06-tuples/example3.json new file mode 100644 index 0000000..1c53a5e --- /dev/null +++ b/samples/core/06-tuples/example3.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schemas.example.com/tuples", + "name": "Color Palette Testing", + "description": "Color palette data for UI design validation", + "dataPoints": [ + { + "id": "COLOR_SAMPLE_1", + "location": ["0.0000000", "0.0000000"], + "measurements": [], + "visualColor": [255, 255, 255] + }, + { + "id": "COLOR_SAMPLE_2", + "location": ["1.0000000", "1.0000000"], + "measurements": [], + "visualColor": [0, 0, 0] + }, + { + "id": "COLOR_SAMPLE_3", + "location": ["0.5000000", "0.5000000"], + "measurements": [], + "visualColor": [128, 128, 128] + } + ] +} \ No newline at end of file diff --git a/samples/core/06-tuples/schema.struct.json b/samples/core/06-tuples/schema.struct.json new file mode 100644 index 0000000..6c58823 --- /dev/null +++ b/samples/core/06-tuples/schema.struct.json @@ -0,0 +1,138 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/tuples", + "name": "DataAnalytics", + "description": "Demonstrates tuple types for structured data with fixed positions", + "$root": "#/definitions/DataSet", + "definitions": { + "Coordinate": { + "type": "tuple", + "name": "Coordinate", + "description": "Geographic coordinate as [latitude, longitude]", + "properties": { + "latitude": { + "type": "decimal", + "description": "Latitude in degrees", + "precision": 10, + "scale": 7 + }, + "longitude": { + "type": "decimal", + "description": "Longitude in degrees", + "precision": 10, + "scale": 7 + } + }, + "tuple": ["latitude", "longitude"] + }, + "TimeSeriesPoint": { + "type": "tuple", + "name": "TimeSeriesPoint", + "description": "Time series data point as [timestamp, value, quality]", + "properties": { + "timestamp": { + "type": "datetime", + "description": "Data point timestamp" + }, + "value": { + "type": "decimal", + "description": "Measured value", + "precision": 15, + "scale": 6 + }, + "quality": { + "type": "uint8", + "description": "Data quality score (0-100)" + } + }, + "tuple": ["timestamp", "value", "quality"] + }, + "ColorRGB": { + "type": "tuple", + "name": "ColorRGB", + "description": "RGB color as [red, green, blue]", + "properties": { + "red": { + "type": "uint8", + "description": "Red component (0-255)" + }, + "green": { + "type": "uint8", + "description": "Green component (0-255)" + }, + "blue": { + "type": "uint8", + "description": "Blue component (0-255)" + }, + "alpha": { + "type": "float8", + "description": "Alpha transparency (0.0-1.0) with 8-bit precision" + } + }, + "tuple": ["red", "green", "blue", "alpha"] + }, + "DataPoint": { + "type": "object", + "description": "A single data point with location and measurements", + "properties": { + "id": { + "type": "string", + "description": "Data point identifier", + "maxLength": 50 + }, + "location": { + "type": { "$ref": "#/definitions/Coordinate" }, + "description": "Geographic location" + }, + "measurements": { + "type": "array", + "description": "Time series measurements", + "items": { "type": { "$ref": "#/definitions/TimeSeriesPoint" }} + }, + "visualColor": { + "type": { "$ref": "#/definitions/ColorRGB" }, + "description": "Display color for this data point" + } + }, + "required": ["id", "location", "measurements"] + }, + "DataSet": { + "type": "object", + "description": "Collection of data points with metadata", + "properties": { + "name": { + "type": "string", + "description": "Dataset name", + "maxLength": 100 + }, + "description": { + "type": "string", + "description": "Dataset description", + "maxLength": 500 + }, + "dataPoints": { + "type": "array", + "description": "Array of data points", + "items": { "type": { "$ref": "#/definitions/DataPoint" }} + }, + "bounds": { + "type": "tuple", + "name": "BoundingBox", + "description": "Dataset bounding box as [southwest, northeast] coordinates", + "properties": { + "southwest": { + "type": { "$ref": "#/definitions/Coordinate" }, + "description": "Southwest corner" + }, + "northeast": { + "type": { "$ref": "#/definitions/Coordinate" }, + "description": "Northeast corner" + } + }, + "tuple": ["southwest", "northeast"] + } + }, + "required": ["name", "dataPoints"] + } + } +} \ No newline at end of file diff --git a/samples/core/07-unions/example1.json b/samples/core/07-unions/example1.json new file mode 100644 index 0000000..14c4da3 --- /dev/null +++ b/samples/core/07-unions/example1.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schemas.example.com/unions", + "requestId": "550e8400-e29b-41d4-a716-446655440001", + "status": "SUCCESS", + "input": { + "text": "This is a sample document for processing.", + "encoding": "UTF-8" + }, + "output": { + "text": "This is a sample document for processing. [PROCESSED]", + "encoding": "UTF-8" + }, + "metadata": { + "processingEngine": "TextProcessor v2.1", + "wordCount": 8, + "confidence": 0.95, + "enhanced": true + }, + "processingTime": "PT2.5S" +} \ No newline at end of file diff --git a/samples/core/07-unions/example2.json b/samples/core/07-unions/example2.json new file mode 100644 index 0000000..2c8e582 --- /dev/null +++ b/samples/core/07-unions/example2.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schemas.example.com/unions", + "requestId": "550e8400-e29b-41d4-a716-446655440002", + "status": "SUCCESS", + "input": { + "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==", + "mimeType": "image/png", + "size": "95" + }, + "output": { + "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==", + "mimeType": "image/png", + "size": "95" + }, + "metadata": { + "imageProcessor": "ImageOptimizer v1.5", + "originalFormat": "PNG", + "compressionRatio": 1.0 + }, + "processingTime": "PT0.8S" +} \ No newline at end of file diff --git a/samples/core/07-unions/example3.json b/samples/core/07-unions/example3.json new file mode 100644 index 0000000..3f7881f --- /dev/null +++ b/samples/core/07-unions/example3.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://schemas.example.com/unions", + "requestId": "550e8400-e29b-41d4-a716-446655440003", + "status": "FAILED", + "input": { + "id": "550e8400-e29b-41d4-a716-446655440999", + "url": "https://documents.example.com/large-file.pdf" + }, + "output": { + "code": "SIZE_LIMIT_EXCEEDED", + "message": "Document size exceeds the maximum allowed limit of 10MB", + "timestamp": "2023-11-13T15:45:30Z" + }, + "metadata": { + "attemptedEngine": "PDFProcessor v3.0", + "fileSize": "15728640", + "maxAllowedSize": "10485760" + }, + "processingTime": "PT0.1S" +} \ No newline at end of file diff --git a/samples/core/07-unions/schema.struct.json b/samples/core/07-unions/schema.struct.json new file mode 100644 index 0000000..25eb0fb --- /dev/null +++ b/samples/core/07-unions/schema.struct.json @@ -0,0 +1,134 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/unions", + "name": "DocumentProcessing", + "description": "Demonstrates type unions for flexible data handling", + "$root": "#/definitions/ProcessingResult", + "definitions": { + "TextContent": { + "type": "object", + "description": "Plain text content", + "properties": { + "text": { + "type": "string", + "description": "The text content", + "maxLength": 10000 + }, + "encoding": { + "type": "string", + "description": "Text encoding", + "const": "UTF-8" + } + }, + "required": ["text", "encoding"] + }, + "BinaryContent": { + "type": "object", + "description": "Binary file content", + "properties": { + "data": { + "type": "binary", + "description": "Binary data encoded as base64", + "contentEncoding": "base64" + }, + "mimeType": { + "type": "string", + "description": "MIME type of the binary content", + "examples": ["application/pdf", "image/png", "application/msword"] + }, + "size": { + "type": "uint64", + "description": "Size of the binary content in bytes" + } + }, + "required": ["data", "mimeType", "size"] + }, + "DocumentId": { + "type": "object", + "description": "Document identified by ID reference", + "properties": { + "id": { + "type": "uuid", + "description": "Document identifier" + }, + "url": { + "type": "uri", + "description": "URL to retrieve the document" + }, + "schemaRef": { + "type": "jsonpointer", + "description": "JSON Pointer to the document schema definition" + }, + "confidence": { + "type": "float", + "description": "Document processing confidence score (0.0 to 1.0)" + } + }, + "required": ["id"] + }, + "ProcessingError": { + "type": "object", + "description": "Error that occurred during processing", + "properties": { + "code": { + "type": "string", + "description": "Error code", + "enum": ["INVALID_FORMAT", "SIZE_LIMIT_EXCEEDED", "PROCESSING_TIMEOUT", "PERMISSION_DENIED"] + }, + "message": { + "type": "string", + "description": "Human-readable error message", + "maxLength": 500 + }, + "timestamp": { + "type": "datetime", + "description": "When the error occurred" + } + }, + "required": ["code", "message", "timestamp"] + }, + "ProcessingResult": { + "type": "object", + "description": "Result of document processing operation", + "properties": { + "requestId": { + "type": "uuid", + "description": "Unique request identifier" + }, + "status": { + "type": "string", + "description": "Processing status", + "enum": ["SUCCESS", "PARTIAL_SUCCESS", "FAILED"] + }, + "input": { + "type": [ + { "$ref": "#/definitions/TextContent" }, + { "$ref": "#/definitions/BinaryContent" }, + { "$ref": "#/definitions/DocumentId" } + ], + "description": "Input document content - can be text, binary, or reference" + }, + "output": { + "type": [ + { "$ref": "#/definitions/TextContent" }, + { "$ref": "#/definitions/BinaryContent" }, + { "$ref": "#/definitions/ProcessingError" } + ], + "description": "Processing output - success result or error" + }, + "metadata": { + "type": "map", + "description": "Additional processing metadata", + "values": { + "type": ["string", "number", "boolean"] + } + }, + "processingTime": { + "type": "duration", + "description": "Time taken to process the document" + } + }, + "required": ["requestId", "status", "input"] + } + } +} \ No newline at end of file diff --git a/samples/core/08-namespaces/example1.json b/samples/core/08-namespaces/example1.json new file mode 100644 index 0000000..10b0c09 --- /dev/null +++ b/samples/core/08-namespaces/example1.json @@ -0,0 +1,108 @@ +{ + "$schema": "https://schemas.example.com/namespaces", + "companyName": "TechCorp Industries", + "headquarters": { + "street": "1000 Innovation Drive", + "city": "Silicon Valley", + "state": "California", + "zipCode": "94025", + "country": "US" + }, + "employees": [ + { + "employeeId": "EMP001", + "firstName": "Alice", + "lastName": "Johnson", + "department": { + "code": "ENG", + "name": "Engineering", + "managerId": "EMP003", + "budget": "2500000.00" + }, + "position": "Senior Software Engineer", + "salary": "145000.00", + "hireDate": "2020-03-15", + "address": { + "street": "123 Oak Street", + "city": "Palo Alto", + "state": "California", + "zipCode": "94301", + "country": "US" + }, + "contact": { + "email": "alice.johnson@techcorp.com", + "phone": "650-555-1234", + "mobile": "650-555-5678" + } + }, + { + "employeeId": "EMP002", + "firstName": "Bob", + "lastName": "Smith", + "department": { + "code": "MKT", + "name": "Marketing", + "managerId": "EMP004", + "budget": "1200000.00" + }, + "position": "Marketing Manager", + "salary": "95000.00", + "hireDate": "2019-07-20", + "address": { + "street": "456 Pine Avenue", + "city": "San Jose", + "state": "California", + "zipCode": "95110", + "country": "US" + }, + "contact": { + "email": "bob.smith@techcorp.com", + "phone": "408-555-9876" + } + } + ], + "departments": [ + { + "code": "ENG", + "name": "Engineering", + "managerId": "EMP003", + "budget": "2500000.00" + }, + { + "code": "MKT", + "name": "Marketing", + "managerId": "EMP004", + "budget": "1200000.00" + } + ], + "buildings": [ + { + "buildingId": "BLDG01", + "name": "Main Campus", + "address": { + "street": "1000 Innovation Drive", + "city": "Silicon Valley", + "state": "California", + "zipCode": "94025", + "country": "US" + }, + "floors": 5, + "capacity": 500, + "offices": [ + { + "officeNumber": "101", + "floor": 1, + "capacity": 2, + "equipment": ["desk", "computer", "phone", "whiteboard"] + }, + { + "officeNumber": "205", + "floor": 2, + "capacity": 4, + "equipment": ["conference_table", "projector", "video_conference"] + } + ] + } + ], + "lastUpdated": "2023-11-13T15:30:00Z" +} \ No newline at end of file diff --git a/samples/core/08-namespaces/example2.json b/samples/core/08-namespaces/example2.json new file mode 100644 index 0000000..a5ad231 --- /dev/null +++ b/samples/core/08-namespaces/example2.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://schemas.example.com/namespaces", + "employee": { + "personalInfo": { + "firstName": "Sarah", + "lastName": "Johnson", + "dateOfBirth": "1985-08-22" + }, + "contactInfo": { + "email": "sarah.johnson@company.com", + "phone": "+1-555-987-6543" + }, + "workInfo": { + "employeeId": "EMP002", + "department": "Engineering", + "position": "Senior Software Developer", + "manager": "John Smith", + "startDate": "2019-03-15", + "salary": "125000.00" + } + }, + "workspaceAssignment": { + "assignmentId": "WS002", + "employeeId": "EMP002", + "assignedDate": "2019-03-20", + "workspace": { + "workspaceId": "WS-3B-15", + "building": "Building B", + "floor": 3, + "room": "3B-15", + "capacity": 1, + "equipment": ["desk", "chair", "monitor", "laptop dock"] + } + } +} \ No newline at end of file diff --git a/samples/core/08-namespaces/example3.json b/samples/core/08-namespaces/example3.json new file mode 100644 index 0000000..f49340b --- /dev/null +++ b/samples/core/08-namespaces/example3.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://schemas.example.com/namespaces", + "employee": { + "personalInfo": { + "firstName": "Michael", + "lastName": "Chen", + "dateOfBirth": "1992-11-03" + }, + "contactInfo": { + "email": "michael.chen@company.com", + "phone": "+1-555-234-8901" + }, + "workInfo": { + "employeeId": "EMP003", + "department": "Marketing", + "position": "Marketing Manager", + "manager": "Lisa Brown", + "startDate": "2021-01-10", + "salary": "95000.00" + } + }, + "workspaceAssignment": { + "assignmentId": "WS003", + "employeeId": "EMP003", + "assignedDate": "2021-01-15", + "workspace": { + "workspaceId": "WS-2A-08", + "building": "Building A", + "floor": 2, + "room": "2A-08", + "capacity": 2, + "equipment": ["desk", "chair", "monitor", "laptop dock", "whiteboard"] + } + } +} \ No newline at end of file diff --git a/samples/core/08-namespaces/schema.struct.json b/samples/core/08-namespaces/schema.struct.json new file mode 100644 index 0000000..cb810c1 --- /dev/null +++ b/samples/core/08-namespaces/schema.struct.json @@ -0,0 +1,127 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/namespaces", + "name": "CorporateDirectory", + "description": "Demonstrates hierarchical namespaces and type organization", + "$root": "#/definitions/Company/Directory", + "definitions": { + "Common": { + "Address": { + "type": "object", + "description": "Standard address format used across all namespaces", + "properties": { + "street": { "type": "string", "maxLength": 100 }, + "city": { "type": "string", "maxLength": 50 }, + "state": { "type": "string", "maxLength": 50 }, + "zipCode": { "type": "string", "maxLength": 20 }, + "country": { "type": "string", "maxLength": 3 } + }, + "required": ["street", "city", "country"] + }, + "ContactInfo": { + "type": "object", + "description": "Standard contact information", + "properties": { + "email": { "type": "string" }, + "phone": { "type": "string", "maxLength": 20 }, + "mobile": { "type": "string", "maxLength": 20 } + }, + "required": ["email"] + } + }, + "HR": { + "Employee": { + "type": "object", + "description": "Employee record in HR namespace", + "properties": { + "employeeId": { "type": "string", "maxLength": 20 }, + "firstName": { "type": "string", "maxLength": 50 }, + "lastName": { "type": "string", "maxLength": 50 }, + "department": { "type": { "$ref": "#/definitions/HR/Department" }}, + "position": { "type": "string", "maxLength": 100 }, + "salary": { + "type": "decimal", + "precision": 10, + "scale": 2 + }, + "hireDate": { "type": "date" }, + "address": { "type": { "$ref": "#/definitions/Common/Address" }}, + "contact": { "type": { "$ref": "#/definitions/Common/ContactInfo" }} + }, + "required": ["employeeId", "firstName", "lastName", "department", "hireDate"] + }, + "Department": { + "type": "object", + "description": "Department information", + "properties": { + "code": { "type": "string", "maxLength": 10 }, + "name": { "type": "string", "maxLength": 100 }, + "managerId": { "type": "string", "maxLength": 20 }, + "budget": { + "type": "decimal", + "precision": 15, + "scale": 2 + } + }, + "required": ["code", "name"] + } + }, + "Facilities": { + "Building": { + "type": "object", + "description": "Building information in facilities namespace", + "properties": { + "buildingId": { "type": "string", "maxLength": 10 }, + "name": { "type": "string", "maxLength": 100 }, + "address": { "type": { "$ref": "#/definitions/Common/Address" }}, + "floors": { "type": "uint8" }, + "capacity": { "type": "uint16" }, + "offices": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/Facilities/Office" }} + } + }, + "required": ["buildingId", "name", "address"] + }, + "Office": { + "type": "object", + "description": "Individual office within a building", + "properties": { + "officeNumber": { "type": "string", "maxLength": 10 }, + "floor": { "type": "uint8" }, + "capacity": { "type": "uint8" }, + "equipment": { + "type": "set", + "items": { "type": "string" } + }, + "occupant": { "type": { "$ref": "#/definitions/HR/Employee" }} + }, + "required": ["officeNumber", "floor"] + } + }, + "Company": { + "Directory": { + "type": "object", + "description": "Complete company directory combining all namespaces", + "properties": { + "companyName": { "type": "string", "maxLength": 100 }, + "headquarters": { "type": { "$ref": "#/definitions/Common/Address" }}, + "employees": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/HR/Employee" }} + }, + "departments": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/HR/Department" }} + }, + "buildings": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/Facilities/Building" }} + }, + "lastUpdated": { "type": "datetime" } + }, + "required": ["companyName", "headquarters", "employees", "departments", "lastUpdated"] + } + } + } +} \ No newline at end of file diff --git a/samples/core/09-extensions/example1.json b/samples/core/09-extensions/example1.json new file mode 100644 index 0000000..0263ab0 --- /dev/null +++ b/samples/core/09-extensions/example1.json @@ -0,0 +1,102 @@ +{ + "$schema": "https://schemas.example.com/extensions", + "fleetName": "Metro City Fleet", + "cars": [ + { + "vin": "1HGBH41JXMN109186", + "make": "Honda", + "model": "Civic", + "year": 2023, + "color": "Silver", + "mileage": 15420, + "registrationDate": "2023-01-15", + "engineType": "hybrid", + "fuelCapacity": "12.40", + "transmissionType": "cvt", + "doors": 4, + "seatingCapacity": 5, + "trunkCapacity": "15.10", + "features": ["air_conditioning", "navigation", "bluetooth", "backup_camera"] + }, + { + "vin": "5NPE34AF4KH012345", + "make": "Hyundai", + "model": "Elantra", + "year": 2022, + "color": "Blue", + "mileage": 28750, + "registrationDate": "2022-05-10", + "engineType": "gasoline", + "fuelCapacity": "14.00", + "transmissionType": "automatic", + "doors": 4, + "seatingCapacity": 5, + "trunkCapacity": "14.20" + } + ], + "trucks": [ + { + "vin": "1FTFW1ET5DKE12345", + "make": "Ford", + "model": "F-150", + "year": 2021, + "color": "Red", + "mileage": 45680, + "registrationDate": "2021-03-22", + "engineType": "gasoline", + "fuelCapacity": "36.00", + "transmissionType": "automatic", + "payloadCapacity": 3270, + "towingCapacity": 13200, + "axles": 2, + "commercialLicense": false, + "cargoType": "general" + } + ], + "motorcycles": [ + { + "vin": "JH2RC5014LM100123", + "make": "Honda", + "model": "CBR600RR", + "year": 2020, + "color": "Black", + "mileage": 8450, + "registrationDate": "2020-04-18", + "engineType": "gasoline", + "fuelCapacity": "4.50", + "transmissionType": "manual", + "engineSize": 599, + "style": "sport", + "helmetsRequired": true + } + ], + "bicycles": [ + { + "vin": "BK1234567890", + "make": "Trek", + "model": "FX 3", + "year": 2023, + "color": "Green", + "mileage": 0, + "registrationDate": "2023-06-01", + "gears": 21, + "frameSize": "Medium", + "bikeType": "hybrid", + "wheelSize": "27.5" + } + ], + "maintenanceRecords": [ + { + "recordId": "550e8400-e29b-41d4-a716-446655440001", + "vehicleVin": "1HGBH41JXMN109186", + "serviceDate": "2023-11-01", + "mileageAtService": 15000, + "serviceType": "oil_change", + "description": "Regular oil change and filter replacement", + "cost": "65.99", + "serviceProvider": "QuickLube Express" + } + ], + "totalVehicles": 5, + "lastInventoryDate": "2023-11-13T10:00:00Z" +} \ No newline at end of file diff --git a/samples/core/09-extensions/example2.json b/samples/core/09-extensions/example2.json new file mode 100644 index 0000000..016c02f --- /dev/null +++ b/samples/core/09-extensions/example2.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://schemas.example.com/extensions", + "fleet": [ + { + "vehicleId": "TRUCK001", + "make": "Ford", + "model": "F-150", + "year": 2023, + "vin": "1FTFW1E50NFB12345", + "licensePlate": "ABC-1234", + "registrationExpiry": "2024-12-31", + "engineType": "V6", + "fuelType": "gasoline", + "payloadCapacity": "1500" + }, + { + "vehicleId": "CAR002", + "make": "Tesla", + "model": "Model 3", + "year": 2024, + "vin": "5YJ3E1EA0KF123456", + "licensePlate": "EV-5678", + "registrationExpiry": "2025-06-30", + "engineType": "electric", + "fuelType": "electric", + "seatingCapacity": 5, + "batteryCapacity": "75" + }, + { + "vehicleId": "BIKE001", + "make": "Harley-Davidson", + "model": "Street 750", + "year": 2022, + "vin": "1HD1KB4XXNB123456", + "licensePlate": "BIKE-99", + "registrationExpiry": "2024-08-15", + "engineType": "V-Twin", + "fuelType": "gasoline", + "engineDisplacement": "749" + } + ] +} \ No newline at end of file diff --git a/samples/core/09-extensions/example3.json b/samples/core/09-extensions/example3.json new file mode 100644 index 0000000..9baaaad --- /dev/null +++ b/samples/core/09-extensions/example3.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://schemas.example.com/extensions", + "fleet": [ + { + "vehicleId": "CAR003", + "make": "BMW", + "model": "X5", + "year": 2023, + "vin": "5UXCR6C0XN9123456", + "licensePlate": "LUX-2024", + "registrationExpiry": "2025-03-31", + "engineType": "turbo-inline-6", + "fuelType": "gasoline", + "seatingCapacity": 7, + "drivetrain": "AWD" + }, + { + "vehicleId": "TRUCK002", + "make": "Chevrolet", + "model": "Silverado 2500HD", + "year": 2024, + "vin": "1GC4YPEY6NF123456", + "licensePlate": "WORK-789", + "registrationExpiry": "2025-09-30", + "engineType": "V8", + "fuelType": "diesel", + "payloadCapacity": "3500", + "towingCapacity": "18500" + } + ] +} \ No newline at end of file diff --git a/samples/core/09-extensions/schema.struct.json b/samples/core/09-extensions/schema.struct.json new file mode 100644 index 0000000..3ffb676 --- /dev/null +++ b/samples/core/09-extensions/schema.struct.json @@ -0,0 +1,185 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/extensions", + "name": "VehicleManagement", + "description": "Demonstrates abstract types and $extends for inheritance", + "$root": "#/definitions/Fleet", + "definitions": { + "Vehicle": { + "abstract": true, + "type": "object", + "description": "Abstract base vehicle type - cannot be instantiated directly", + "properties": { + "vin": { + "type": "string", + "description": "Vehicle Identification Number", + "maxLength": 17 + }, + "make": { "type": "string", "maxLength": 50 }, + "model": { "type": "string", "maxLength": 50 }, + "year": { "type": "uint16" }, + "color": { "type": "string", "maxLength": 30 }, + "mileage": { "type": "uint32" }, + "registrationDate": { "type": "date" } + }, + "required": ["vin", "make", "model", "year"] + }, + "MotorVehicle": { + "abstract": true, + "type": "object", + "description": "Abstract motor vehicle extending base Vehicle", + "$extends": "#/definitions/Vehicle", + "properties": { + "engineType": { + "type": "string", + "enum": ["gasoline", "diesel", "hybrid", "electric"] + }, + "fuelCapacity": { + "type": "decimal", + "precision": 5, + "scale": 2 + }, + "transmissionType": { + "type": "string", + "enum": ["manual", "automatic", "cvt"] + } + }, + "required": ["engineType"] + }, + "Car": { + "type": "object", + "description": "Passenger car extending MotorVehicle", + "$extends": "#/definitions/MotorVehicle", + "properties": { + "doors": { + "type": "uint8", + "enum": [2, 4, 5] + }, + "seatingCapacity": { "type": "uint8" }, + "trunkCapacity": { + "type": "decimal", + "precision": 6, + "scale": 2 + }, + "features": { + "type": "set", + "items": { + "type": "string", + "enum": ["air_conditioning", "navigation", "bluetooth", "backup_camera", "heated_seats", "sunroof"] + } + } + }, + "required": ["doors", "seatingCapacity"] + }, + "Truck": { + "type": "object", + "description": "Commercial truck extending MotorVehicle", + "$extends": "#/definitions/MotorVehicle", + "properties": { + "payloadCapacity": { + "type": "uint32", + "description": "Payload capacity in pounds" + }, + "towingCapacity": { + "type": "uint32", + "description": "Towing capacity in pounds" + }, + "axles": { "type": "uint8" }, + "commercialLicense": { "type": "boolean" }, + "cargoType": { + "type": "string", + "enum": ["general", "refrigerated", "hazmat", "livestock", "construction"] + } + }, + "required": ["payloadCapacity", "commercialLicense"] + }, + "Motorcycle": { + "type": "object", + "description": "Motorcycle extending MotorVehicle", + "$extends": "#/definitions/MotorVehicle", + "properties": { + "engineSize": { + "type": "uint16", + "description": "Engine displacement in cubic centimeters" + }, + "style": { + "type": "string", + "enum": ["sport", "cruiser", "touring", "dirt", "scooter"] + }, + "helmetsRequired": { "type": "boolean" } + }, + "required": ["engineSize", "style"] + }, + "Bicycle": { + "type": "object", + "description": "Non-motor bicycle extending base Vehicle", + "$extends": "#/definitions/Vehicle", + "properties": { + "gears": { "type": "uint8" }, + "frameSize": { "type": "string", "maxLength": 10 }, + "bikeType": { + "type": "string", + "enum": ["road", "mountain", "hybrid", "bmx", "electric"] + }, + "wheelSize": { + "type": "decimal", + "precision": 3, + "scale": 1 + } + }, + "required": ["bikeType"] + }, + "MaintenanceRecord": { + "type": "object", + "description": "Vehicle maintenance record", + "properties": { + "recordId": { "type": "uuid" }, + "vehicleVin": { "type": "string", "maxLength": 17 }, + "serviceDate": { "type": "date" }, + "mileageAtService": { "type": "uint32" }, + "serviceType": { + "type": "string", + "enum": ["oil_change", "tire_rotation", "brake_service", "transmission", "major_service", "repair"] + }, + "description": { "type": "string", "maxLength": 500 }, + "cost": { + "type": "decimal", + "precision": 10, + "scale": 2 + }, + "serviceProvider": { "type": "string", "maxLength": 100 } + }, + "required": ["recordId", "vehicleVin", "serviceDate", "serviceType", "cost"] + }, + "Fleet": { + "type": "object", + "description": "Complete vehicle fleet management system", + "properties": { + "fleetName": { "type": "string", "maxLength": 100 }, + "cars": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/Car" }} + }, + "trucks": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/Truck" }} + }, + "motorcycles": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/Motorcycle" }} + }, + "bicycles": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/Bicycle" }} + }, + "maintenanceRecords": { + "type": "array", + "items": { "type": { "$ref": "#/definitions/MaintenanceRecord" }} + }, + "totalVehicles": { "type": "uint32" }, + "lastInventoryDate": { "type": "datetime" } + }, + "required": ["fleetName", "totalVehicles", "lastInventoryDate"] + } + } +} \ No newline at end of file diff --git a/samples/core/10-discriminated-unions/example1.json b/samples/core/10-discriminated-unions/example1.json new file mode 100644 index 0000000..7bd8370 --- /dev/null +++ b/samples/core/10-discriminated-unions/example1.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://schemas.example.com/discriminated-unions", + "transactionId": "550e8400-e29b-41d4-a716-446655440001", + "merchantId": "MERCH_12345", + "amount": "149.99", + "currency": "USD", + "description": "Online purchase - electronics", + "paymentMethod": { + "creditCard": { + "cardNumber": "**** **** **** 1234", + "cardholderName": "John Smith", + "expiryMonth": 12, + "expiryYear": 2026, + "cardType": "visa", + "authorizationCode": "ABC123" + } + }, + "status": "captured", + "notification": { + "notificationId": "550e8400-e29b-41d4-a716-446655440002", + "timestamp": "2023-11-13T15:45:30Z", + "priority": "normal", + "recipient": "customer@example.com", + "notificationType": "email", + "subject": "Payment Confirmation - Order #12345", + "body": "Thank you for your purchase! Your payment of $149.99 has been successfully processed." + }, + "processedAt": "2023-11-13T15:45:30Z", + "fees": { + "processing": "4.35", + "interchange": "1.85" + } +} \ No newline at end of file diff --git a/samples/core/10-discriminated-unions/example2.json b/samples/core/10-discriminated-unions/example2.json new file mode 100644 index 0000000..fbbbed6 --- /dev/null +++ b/samples/core/10-discriminated-unions/example2.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://schemas.example.com/discriminated-unions", + "transactionId": "550e8400-e29b-41d4-a716-446655440003", + "merchantId": "MERCH_67890", + "amount": "89.50", + "currency": "USD", + "description": "Subscription payment - premium service", + "paymentMethod": { + "bankTransfer": { + "bankName": "First National Bank", + "accountNumber": "*****6789", + "routingNumber": "021000021", + "accountHolderName": "Jane Doe", + "transferReference": "REF123456789" + } + }, + "status": "pending", + "notification": { + "notificationId": "550e8400-e29b-41d4-a716-446655440004", + "timestamp": "2023-11-13T16:20:15Z", + "priority": "high", + "recipient": "+1-555-123-4567", + "notificationType": "sms", + "message": "Payment of $89.50 is being processed. You'll receive confirmation soon." + }, + "processedAt": "2023-11-13T16:20:15Z", + "fees": { + "processing": "2.50", + "wire": "15.00" + } +} \ No newline at end of file diff --git a/samples/core/10-discriminated-unions/example3.json b/samples/core/10-discriminated-unions/example3.json new file mode 100644 index 0000000..8ccabc5 --- /dev/null +++ b/samples/core/10-discriminated-unions/example3.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://schemas.example.com/discriminated-unions", + "transactionId": "550e8400-e29b-41d4-a716-446655440005", + "merchantId": "MERCH_11111", + "amount": "25.00", + "currency": "USD", + "description": "Digital gift card purchase", + "paymentMethod": { + "digitalWallet": { + "walletProvider": "PayPal", + "walletId": "user@example.com", + "transactionId": "PP123456789", + "walletBalance": "150.75" + } + }, + "status": "completed", + "notification": { + "notificationId": "550e8400-e29b-41d4-a716-446655440006", + "timestamp": "2023-11-13T17:10:45Z", + "priority": "normal", + "recipient": "admin@merchantstore.com", + "notificationType": "webhook", + "webhookUrl": "https://api.merchantstore.com/webhooks/payment", + "webhookPayload": { + "event": "payment.completed", + "transactionId": "550e8400-e29b-41d4-a716-446655440005", + "amount": "25.00" + } + }, + "processedAt": "2023-11-13T17:10:45Z", + "fees": { + "processing": "0.75" + } +} \ No newline at end of file diff --git a/samples/core/10-discriminated-unions/schema.struct.json b/samples/core/10-discriminated-unions/schema.struct.json new file mode 100644 index 0000000..566a842 --- /dev/null +++ b/samples/core/10-discriminated-unions/schema.struct.json @@ -0,0 +1,196 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/discriminated-unions", + "name": "PaymentSystem", + "description": "Demonstrates discriminated unions (choice types) - both tagged and inline variants", + "$root": "#/definitions/PaymentTransaction", + "definitions": { + "TaggedPaymentMethod": { + "type": "choice", + "name": "TaggedPaymentMethod", + "description": "Tagged union - payment method is wrapped with type selector", + "choices": { + "creditCard": { "type": { "$ref": "#/definitions/CreditCardPayment" }}, + "bankTransfer": { "type": { "$ref": "#/definitions/BankTransferPayment" }}, + "digitalWallet": { "type": { "$ref": "#/definitions/DigitalWalletPayment" }}, + "cash": { "type": { "$ref": "#/definitions/CashPayment" }} + } + }, + "CreditCardPayment": { + "type": "object", + "description": "Credit card payment details", + "properties": { + "cardNumber": { + "type": "string", + "description": "Masked card number", + "maxLength": 19 + }, + "cardholderName": { "type": "string", "maxLength": 100 }, + "expiryMonth": { "type": "uint8" }, + "expiryYear": { "type": "uint16" }, + "cardType": { + "type": "string", + "enum": ["visa", "mastercard", "amex", "discover"] + }, + "authorizationCode": { "type": "string", "maxLength": 20 } + }, + "required": ["cardNumber", "cardholderName", "expiryMonth", "expiryYear", "cardType"] + }, + "BankTransferPayment": { + "type": "object", + "description": "Bank transfer payment details", + "properties": { + "bankName": { "type": "string", "maxLength": 100 }, + "routingNumber": { "type": "string", "maxLength": 15 }, + "accountNumber": { + "type": "string", + "description": "Masked account number", + "maxLength": 20 + }, + "transferType": { + "type": "string", + "enum": ["ach", "wire", "instant"] + }, + "referenceNumber": { "type": "string", "maxLength": 50 } + }, + "required": ["bankName", "accountNumber", "transferType"] + }, + "DigitalWalletPayment": { + "type": "object", + "description": "Digital wallet payment details", + "properties": { + "walletProvider": { + "type": "string", + "enum": ["paypal", "apple_pay", "google_pay", "venmo", "zelle"] + }, + "walletAccountId": { "type": "string", "maxLength": 100 }, + "transactionId": { "type": "string", "maxLength": 100 } + }, + "required": ["walletProvider", "walletAccountId"] + }, + "CashPayment": { + "type": "object", + "description": "Cash payment details", + "properties": { + "receivedAmount": { + "type": "decimal", + "precision": 10, + "scale": 2 + }, + "changeGiven": { + "type": "decimal", + "precision": 10, + "scale": 2 + }, + "registerId": { "type": "string", "maxLength": 20 }, + "cashierId": { "type": "string", "maxLength": 20 } + }, + "required": ["receivedAmount", "registerId"] + }, + "BaseNotification": { + "abstract": true, + "type": "object", + "description": "Abstract base type for all notifications", + "properties": { + "notificationId": { "type": "uuid" }, + "timestamp": { "type": "datetime" }, + "priority": { + "type": "string", + "enum": ["low", "normal", "high", "urgent"] + }, + "recipient": { "type": "string", "maxLength": 100 } + }, + "required": ["notificationId", "timestamp", "priority", "recipient"] + }, + "EmailNotification": { + "type": "object", + "$extends": "#/definitions/BaseNotification", + "description": "Email notification extending base notification", + "properties": { + "subject": { "type": "string", "maxLength": 200 }, + "body": { "type": "string", "maxLength": 5000 }, + "attachments": { + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["subject", "body"] + }, + "SmsNotification": { + "type": "object", + "$extends": "#/definitions/BaseNotification", + "description": "SMS notification extending base notification", + "properties": { + "message": { "type": "string", "maxLength": 160 }, + "phoneNumber": { "type": "string", "maxLength": 20 } + }, + "required": ["message", "phoneNumber"] + }, + "PushNotification": { + "type": "object", + "$extends": "#/definitions/BaseNotification", + "description": "Push notification extending base notification", + "properties": { + "title": { "type": "string", "maxLength": 100 }, + "body": { "type": "string", "maxLength": 500 }, + "badge": { "type": "uint16" }, + "sound": { "type": "string", "maxLength": 50 }, + "deviceToken": { "type": "string", "maxLength": 200 } + }, + "required": ["title", "body", "deviceToken"] + }, + "InlineNotificationChoice": { + "type": "choice", + "name": "InlineNotificationChoice", + "description": "Inline union - notification type is embedded as a property", + "$extends": "#/definitions/BaseNotification", + "selector": "notificationType", + "choices": { + "email": { "type": { "$ref": "#/definitions/EmailNotification" }}, + "sms": { "type": { "$ref": "#/definitions/SmsNotification" }}, + "push": { "type": { "$ref": "#/definitions/PushNotification" }} + } + }, + "PaymentTransaction": { + "type": "object", + "description": "Complete payment transaction with both types of discriminated unions", + "properties": { + "transactionId": { "type": "uuid" }, + "merchantId": { "type": "string", "maxLength": 50 }, + "amount": { + "type": "decimal", + "precision": 12, + "scale": 2 + }, + "currency": { + "type": "string", + "enum": ["USD", "EUR", "GBP", "CAD", "AUD"] + }, + "description": { "type": "string", "maxLength": 200 }, + "paymentMethod": { + "type": { "$ref": "#/definitions/TaggedPaymentMethod" }, + "description": "Tagged union example - payment details wrapped with type" + }, + "status": { + "type": "string", + "enum": ["pending", "authorized", "captured", "failed", "refunded"] + }, + "notification": { + "type": { "$ref": "#/definitions/InlineNotificationChoice" }, + "description": "Inline union example - notification details with embedded type selector" + }, + "processedAt": { "type": "datetime" }, + "fees": { + "type": "map", + "description": "Processing fees by type", + "values": { + "type": "decimal", + "precision": 10, + "scale": 4 + } + } + }, + "required": ["transactionId", "merchantId", "amount", "currency", "paymentMethod", "status", "processedAt"] + } + } +} \ No newline at end of file diff --git a/samples/core/11-sets-and-maps/example1.json b/samples/core/11-sets-and-maps/example1.json new file mode 100644 index 0000000..760876a --- /dev/null +++ b/samples/core/11-sets-and-maps/example1.json @@ -0,0 +1,128 @@ +{ + "$schema": "https://schemas.example.com/sets-and-maps", + "name": "Central Public Library", + "location": "123 Main Street, Downtown", + "books": { + "978-0-13-110362-7": { + "isbn": "978-0-13-110362-7", + "title": "The C Programming Language", + "authors": ["550e8400-e29b-41d4-a716-446655440001", "550e8400-e29b-41d4-a716-446655440002"], + "genres": ["technical", "non-fiction"], + "publishYear": 1988, + "pageCount": 272, + "ratings": { + "550e8400-e29b-41d4-a716-446655440010": { + "score": 9, + "review": "Essential reading for any programmer. Clear and concise.", + "reviewDate": "2023-08-15" + }, + "550e8400-e29b-41d4-a716-446655440011": { + "score": 10, + "reviewDate": "2023-09-22" + } + }, + "metadata": { + "edition": "2nd", + "language": "English", + "dewey_decimal": "005.133", + "difficulty_level": 3 + } + }, + "978-0-7475-3269-9": { + "isbn": "978-0-7475-3269-9", + "title": "Harry Potter and the Philosopher's Stone", + "authors": ["550e8400-e29b-41d4-a716-446655440003"], + "genres": ["fiction", "fantasy"], + "publishYear": 1997, + "pageCount": 223, + "ratings": { + "550e8400-e29b-41d4-a716-446655440012": { + "score": 8, + "review": "Wonderful start to an amazing series!", + "reviewDate": "2023-07-10" + } + }, + "metadata": { + "series": "Harry Potter", + "series_number": 1, + "target_age": "young_adult" + } + } + }, + "authors": { + "550e8400-e29b-41d4-a716-446655440001": { + "id": "550e8400-e29b-41d4-a716-446655440001", + "name": "Brian W. Kernighan", + "nationality": "Canadian", + "birthYear": 1942 + }, + "550e8400-e29b-41d4-a716-446655440002": { + "id": "550e8400-e29b-41d4-a716-446655440002", + "name": "Dennis M. Ritchie", + "nationality": "American", + "birthYear": 1941 + }, + "550e8400-e29b-41d4-a716-446655440003": { + "id": "550e8400-e29b-41d4-a716-446655440003", + "name": "J.K. Rowling", + "nationality": "British", + "birthYear": 1965 + } + }, + "categories": ["fiction", "non-fiction", "technical", "fantasy", "biography"], + "memberIds": [ + "550e8400-e29b-41d4-a716-446655440010", + "550e8400-e29b-41d4-a716-446655440011", + "550e8400-e29b-41d4-a716-446655440012", + "550e8400-e29b-41d4-a716-446655440013" + ], + "openingHours": { + "monday": { + "open": "09:00:00", + "close": "18:00:00", + "isClosed": false + }, + "tuesday": { + "open": "09:00:00", + "close": "18:00:00", + "isClosed": false + }, + "wednesday": { + "open": "09:00:00", + "close": "20:00:00", + "isClosed": false + }, + "thursday": { + "open": "09:00:00", + "close": "18:00:00", + "isClosed": false + }, + "friday": { + "open": "09:00:00", + "close": "17:00:00", + "isClosed": false + }, + "saturday": { + "open": "10:00:00", + "close": "16:00:00", + "isClosed": false + }, + "sunday": { + "isClosed": true + } + }, + "specialCollections": { + "rare_books": { + "description": "Rare and antique books collection with climate-controlled environment", + "bookIsbns": ["978-0-13-110362-7"], + "curator": "Dr. Emily Richardson", + "accessLevel": "restricted" + }, + "local_authors": { + "description": "Works by authors from our local community", + "bookIsbns": ["978-0-7475-3269-9"], + "curator": "Maria Santos", + "accessLevel": "public" + } + } +} \ No newline at end of file diff --git a/samples/core/11-sets-and-maps/example2.json b/samples/core/11-sets-and-maps/example2.json new file mode 100644 index 0000000..73de4c1 --- /dev/null +++ b/samples/core/11-sets-and-maps/example2.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://schemas.example.com/sets-and-maps", + "name": "University Research Library", + "location": "456 Academic Avenue, Campus", + "books": { + "978-0-262-03384-8": { + "isbn": "978-0-262-03384-8", + "title": "Introduction to Algorithms", + "authors": ["550e8400-e29b-41d4-a716-446655440004", "550e8400-e29b-41d4-a716-446655440005"], + "genres": ["technical", "non-fiction"], + "publishYear": 2009, + "pageCount": 1312, + "ratings": { + "550e8400-e29b-41d4-a716-446655440014": { + "score": 9, + "review": "Comprehensive and well-structured. The go-to algorithms textbook.", + "reviewDate": "2023-09-01" + } + }, + "metadata": { + "edition": "3rd", + "isbn_13": "978-0262033848", + "subject_area": "computer_science" + } + } + }, + "authors": { + "550e8400-e29b-41d4-a716-446655440004": { + "id": "550e8400-e29b-41d4-a716-446655440004", + "name": "Thomas H. Cormen", + "nationality": "American", + "birthYear": 1956 + }, + "550e8400-e29b-41d4-a716-446655440005": { + "id": "550e8400-e29b-41d4-a716-446655440005", + "name": "Charles E. Leiserson", + "nationality": "American", + "birthYear": 1953 + } + }, + "categories": ["technical", "non-fiction", "computer_science", "mathematics"], + "memberIds": [ + "550e8400-e29b-41d4-a716-446655440014", + "550e8400-e29b-41d4-a716-446655440015" + ], + "openingHours": { + "monday": { + "open": "08:00:00", + "close": "22:00:00", + "isClosed": false + }, + "tuesday": { + "open": "08:00:00", + "close": "22:00:00", + "isClosed": false + }, + "wednesday": { + "open": "08:00:00", + "close": "22:00:00", + "isClosed": false + }, + "thursday": { + "open": "08:00:00", + "close": "22:00:00", + "isClosed": false + }, + "friday": { + "open": "08:00:00", + "close": "20:00:00", + "isClosed": false + }, + "saturday": { + "open": "10:00:00", + "close": "18:00:00", + "isClosed": false + }, + "sunday": { + "open": "12:00:00", + "close": "20:00:00", + "isClosed": false + } + }, + "specialCollections": { + "research_papers": { + "description": "Academic research papers and journals", + "bookIsbns": ["978-0-262-03384-8"], + "curator": "Prof. Alan Turing", + "accessLevel": "members" + } + } +} \ No newline at end of file diff --git a/samples/core/11-sets-and-maps/example3.json b/samples/core/11-sets-and-maps/example3.json new file mode 100644 index 0000000..382fa51 --- /dev/null +++ b/samples/core/11-sets-and-maps/example3.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://schemas.example.com/sets-and-maps", + "name": "Community Branch Library", + "location": "789 Neighborhood Street, Suburb", + "books": { + "978-1-4028-9462-6": { + "isbn": "978-1-4028-9462-6", + "title": "A Brief History of Time", + "authors": ["550e8400-e29b-41d4-a716-446655440006"], + "genres": ["non-fiction", "science"], + "publishYear": 1988, + "pageCount": 256, + "ratings": { + "550e8400-e29b-41d4-a716-446655440016": { + "score": 8, + "review": "Accessible introduction to cosmology and physics.", + "reviewDate": "2023-10-05" + }, + "550e8400-e29b-41d4-a716-446655440017": { + "score": 7, + "reviewDate": "2023-10-12" + } + }, + "metadata": { + "subject": "cosmology", + "reading_level": "general_audience", + "awards": ["Royal Society Science Book Prize shortlist"] + } + } + }, + "authors": { + "550e8400-e29b-41d4-a716-446655440006": { + "id": "550e8400-e29b-41d4-a716-446655440006", + "name": "Stephen Hawking", + "nationality": "British", + "birthYear": 1942 + } + }, + "categories": ["non-fiction", "science", "biography", "children"], + "memberIds": [ + "550e8400-e29b-41d4-a716-446655440016", + "550e8400-e29b-41d4-a716-446655440017", + "550e8400-e29b-41d4-a716-446655440018" + ], + "openingHours": { + "monday": { + "open": "10:00:00", + "close": "17:00:00", + "isClosed": false + }, + "tuesday": { + "open": "10:00:00", + "close": "17:00:00", + "isClosed": false + }, + "wednesday": { + "open": "10:00:00", + "close": "19:00:00", + "isClosed": false + }, + "thursday": { + "open": "10:00:00", + "close": "17:00:00", + "isClosed": false + }, + "friday": { + "open": "10:00:00", + "close": "17:00:00", + "isClosed": false + }, + "saturday": { + "open": "09:00:00", + "close": "15:00:00", + "isClosed": false + }, + "sunday": { + "isClosed": true + } + }, + "specialCollections": { + "childrens_corner": { + "description": "Books and materials specifically curated for young readers", + "bookIsbns": [], + "curator": "Ms. Sarah Johnson", + "accessLevel": "public" + }, + "local_history": { + "description": "Historical documents and books about our local community", + "bookIsbns": ["978-1-4028-9462-6"], + "curator": "Mr. Robert Chen", + "accessLevel": "public" + } + } +} \ No newline at end of file diff --git a/samples/core/11-sets-and-maps/schema.struct.json b/samples/core/11-sets-and-maps/schema.struct.json new file mode 100644 index 0000000..12d2550 --- /dev/null +++ b/samples/core/11-sets-and-maps/schema.struct.json @@ -0,0 +1,134 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/sets-and-maps", + "name": "DataCollections", + "description": "Demonstrates set and map types with various data structures", + "$root": "#/definitions/Library", + "definitions": { + "Author": { + "type": "object", + "description": "Author information", + "properties": { + "id": { "type": "uuid" }, + "name": { "type": "string", "maxLength": 100 }, + "nationality": { "type": "string", "maxLength": 50 }, + "birthYear": { "type": "uint16" } + }, + "required": ["id", "name"] + }, + "Book": { + "type": "object", + "description": "Book information", + "properties": { + "isbn": { "type": "string", "maxLength": 17 }, + "title": { "type": "string", "maxLength": 200 }, + "authors": { + "type": "set", + "description": "Unique set of author IDs for this book", + "items": { "type": "uuid" } + }, + "genres": { + "type": "set", + "description": "Unique set of genre categories", + "items": { + "type": "string", + "enum": ["fiction", "non-fiction", "mystery", "romance", "sci-fi", "biography", "history", "technical"] + } + }, + "publishYear": { "type": "uint16" }, + "pageCount": { "type": "uint16" }, + "ratings": { + "type": "map", + "description": "User ratings mapped by user ID", + "keys": { "type": "uuid" }, + "values": { + "type": "object", + "properties": { + "score": { "type": "uint8", "description": "Rating from 1-10" }, + "review": { "type": "string", "maxLength": 1000 }, + "reviewDate": { "type": "date" } + }, + "required": ["score", "reviewDate"] + } + }, + "metadata": { + "type": "map", + "description": "Flexible metadata key-value pairs", + "keys": { "type": "string" }, + "values": { "type": "any" } + } + }, + "required": ["isbn", "title", "authors", "publishYear"] + }, + "Library": { + "type": "object", + "description": "Library management system", + "properties": { + "name": { "type": "string", "maxLength": 100 }, + "location": { "type": "string", "maxLength": 200 }, + "books": { + "type": "map", + "description": "Books indexed by ISBN", + "keys": { "type": "string" }, + "values": { "$ref": "#/definitions/Book" } + }, + "authors": { + "type": "map", + "description": "Authors indexed by author ID", + "keys": { "type": "uuid" }, + "values": { "$ref": "#/definitions/Author" } + }, + "categories": { + "type": "set", + "description": "All available book categories", + "items": { "type": "string" } + }, + "memberIds": { + "type": "set", + "description": "Set of active library member IDs", + "items": { "type": "uuid" } + }, + "openingHours": { + "type": "map", + "description": "Opening hours by day of week", + "keys": { + "type": "string", + "enum": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] + }, + "values": { + "type": "object", + "properties": { + "open": { "type": "time", "description": "Opening time" }, + "close": { "type": "time", "description": "Closing time" }, + "isClosed": { "type": "boolean", "default": false } + }, + "required": ["isClosed"] + } + }, + "specialCollections": { + "type": "map", + "description": "Special collections with flexible organization", + "keys": { "type": "string" }, + "values": { + "type": "object", + "properties": { + "description": { "type": "string", "maxLength": 500 }, + "bookIsbns": { + "type": "set", + "items": { "type": "string" } + }, + "curator": { "type": "string", "maxLength": 100 }, + "accessLevel": { + "type": "string", + "enum": ["public", "members", "restricted", "staff"], + "default": "public" + } + }, + "required": ["description", "bookIsbns"] + } + } + }, + "required": ["name", "books", "authors", "categories", "memberIds"] + } + } +} \ No newline at end of file diff --git a/samples/core/README.md b/samples/core/README.md new file mode 100644 index 0000000..5845856 --- /dev/null +++ b/samples/core/README.md @@ -0,0 +1,187 @@ +# JSON Structure Core Examples + +This directory contains comprehensive examples of JSON Structure schemas and instances that demonstrate the features of the JSON Structure Core specification. + +## Directory Structure + +``` +core/ +├── 01-basic-person/ # Simple object schema with primitive types +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 02-address/ # Object with required/optional fields and enums +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 03-financial-types/ # Complex schema with decimal types and nested objects +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 04-datetime-examples/ # DateTime types with UUID and duration +│ ├── schema.struct.json +│ ├── example1.json +│ └── example2.json +├── 05-collections/ # Arrays, sets, maps, and product catalogs +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 06-tuples/ # Tuple types with fixed-order elements +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 07-unions/ # Type unions for flexible data handling +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 08-namespaces/ # Namespace organization and type definitions +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 09-extensions/ # Schema inheritance and extension patterns +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 10-discriminated-unions/ # Tagged unions with discriminators +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +├── 11-sets-and-maps/ # Collection types with complex elements +│ ├── schema.struct.json +│ ├── example1.json +│ ├── example2.json +│ └── example3.json +└── validate-all.ps1 # PowerShell script to validate all schemas and instances +``` + +## Schema Examples Overview + +### 1. Basic Person (`01-basic-person/`) +- **Demonstrates**: Primitive types (`string`, `date`, `boolean`) +- **Features**: Required fields, optional fields, type annotations +- **Real-world use**: User profiles, contact information + +### 2. Address (`02-address/`) +- **Demonstrates**: Enumerations, required vs optional properties +- **Features**: Country code validation, additional properties control +- **Real-world use**: Shipping addresses, location data + +### 3. Financial Types (`03-financial-types/`) +- **Demonstrates**: High-precision `decimal` type, nested object references +- **Features**: Currency handling, precision/scale annotations, complex business documents +- **Real-world use**: Invoicing systems, financial calculations + +### 4. DateTime Examples (`04-datetime-examples/`) +- **Demonstrates**: Date/time types (`date`, `datetime`, `duration`), UUID types +- **Features**: Time zone handling, recurrence patterns, event scheduling +- **Real-world use**: Calendar applications, scheduling systems + +### 5. Collections (`05-collections/`) +- **Demonstrates**: Collection types (`array`, `set`, `map`) +- **Features**: Product catalogs, lookup maps, unique sets +- **Real-world use**: E-commerce catalogs, inventory systems + +### 6. Tuples (`06-tuples/`) +- **Demonstrates**: Fixed-position tuple types +- **Features**: Geographic coordinates, scientific data, color values +- **Real-world use**: Data analytics, mapping applications, scientific datasets + +### 7. Unions (`07-unions/`) +- **Demonstrates**: Type unions for handling multiple data formats +- **Features**: Document processing, error handling, flexible input/output +- **Real-world use**: API responses, document processing pipelines + +### 8. Namespaces (`08-namespaces/`) +- **Demonstrates**: Namespace organization for modular schemas +- **Features**: Type organization, namespace prefixes, clean architecture +- **Real-world use**: Large-scale schema organization, enterprise data models + +### 9. Extensions (`09-extensions/`) +- **Demonstrates**: Schema inheritance using `$extends` keyword +- **Features**: Abstract base types, concrete implementations, code reuse +- **Real-world use**: Object-oriented data modeling, type hierarchies + +### 10. Discriminated Unions (`10-discriminated-unions/`) +- **Demonstrates**: Tagged unions with discriminator properties +- **Features**: Type discrimination, polymorphic data, variant types +- **Real-world use**: Event systems, message processing, API responses + +### 11. Sets and Maps (`11-sets-and-maps/`) +- **Demonstrates**: Advanced collection types with complex elements +- **Features**: Unique sets, key-value mappings, structured collections +- **Real-world use**: Configuration management, lookup tables, unique constraints + +## JSON Structure Features Demonstrated + +### Type System +- **Primitive types**: `string`, `number`, `boolean`, `null` +- **Extended primitives**: `int32`, `uint32`, `int64`, `uint64`, `decimal`, `date`, `datetime`, `time`, `duration`, `uuid`, `uri`, `binary`, `jsonpointer` +- **Compound types**: `object`, `array`, `set`, `map`, `tuple`, `choice`, `any` + +### Schema Features +- **Type references**: `$ref` for reusable types +- **Namespaces**: Organized type definitions +- **Annotations**: `description`, `examples`, `maxLength`, `precision`, `scale` +- **Constraints**: `required`, `enum`, `const`, `additionalProperties` + +### Advanced Features +- **Type unions**: Multiple type choices +- **Reusable definitions**: Modular schema design +- **Rich metadata**: Comprehensive documentation + +## Validation + +All schemas and instances have been validated using the official JSON Structure validators: + +```powershell +# Validate all schemas and instances +.\validate-all.ps1 +``` + +### Manual Validation Examples + +```bash +# Validate a schema +python ..\py\json_structure_schema_validator.py 01-basic-person\schema.struct.json + +# Validate an instance against its schema +python ..\py\json_structure_instance_validator.py 01-basic-person\example1.json 01-basic-person\schema.struct.json +``` + +## Key Design Principles Demonstrated + +1. **Strict Typing**: Every field has a precise type definition +2. **Modularity**: Reusable type definitions in namespaces +3. **Determinism**: Clear, unambiguous schema specifications +4. **Rich Type System**: Beyond basic JSON types for real-world data modeling +5. **Self-Documentation**: Comprehensive descriptions and examples +6. **Validation-Ready**: All examples validate against their schemas + +## Usage in Applications + +These examples can serve as: +- **Templates** for creating new JSON Structure schemas +- **Learning materials** for understanding JSON Structure concepts +- **Test cases** for JSON Structure implementations +- **Reference implementations** for common business scenarios + +## Extension Points + +The schemas demonstrate extensibility through: +- Custom annotations (following the `$` prefix convention) +- Namespace organization for modular design +- Type composition and references +- Rich metadata for tooling integration + +--- + +*These examples conform to the JSON Structure Core specification by C. Vasters (Microsoft, 2025)* \ No newline at end of file diff --git a/samples/core/validate-all.ps1 b/samples/core/validate-all.ps1 new file mode 100644 index 0000000..1251c56 --- /dev/null +++ b/samples/core/validate-all.ps1 @@ -0,0 +1,78 @@ +# Test all schema and instance files +Write-Host "Testing JSON Structure Schemas and Instances" -ForegroundColor Yellow +Write-Host "=" * 50 + +# Test all schemas first +Write-Host "`nTesting Schemas:" -ForegroundColor Cyan +$schemas = @( + "01-basic-person\schema.struct.json", + "02-address\schema.struct.json", + "03-financial-types\schema.struct.json", + "04-datetime-examples\schema.struct.json", + "05-collections\schema.struct.json", + "06-tuples\schema.struct.json", + "07-unions\schema.struct.json", + "08-namespaces\schema.struct.json", + "09-extensions\schema.struct.json", + "10-discriminated-unions\schema.struct.json", + "11-sets-and-maps\schema.struct.json" +) + +foreach ($schema in $schemas) { + $result = python "..\py\json_structure_schema_validator.py" $schema 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ $schema" -ForegroundColor Green + } else { + Write-Host "✗ $schema`: $result" -ForegroundColor Red + } +} + +# Test all instances +Write-Host "`nTesting Instance Files:" -ForegroundColor Cyan +$tests = @( + @("01-basic-person\example1.json", "01-basic-person\schema.struct.json"), + @("01-basic-person\example2.json", "01-basic-person\schema.struct.json"), + @("01-basic-person\example3.json", "01-basic-person\schema.struct.json"), + @("02-address\example1.json", "02-address\schema.struct.json"), + @("02-address\example2.json", "02-address\schema.struct.json"), + @("02-address\example3.json", "02-address\schema.struct.json"), + @("03-financial-types\example1.json", "03-financial-types\schema.struct.json"), + @("03-financial-types\example2.json", "03-financial-types\schema.struct.json"), + @("03-financial-types\example3.json", "03-financial-types\schema.struct.json"), + @("04-datetime-examples\example1.json", "04-datetime-examples\schema.struct.json"), + @("04-datetime-examples\example2.json", "04-datetime-examples\schema.struct.json"), + @("05-collections\example1.json", "05-collections\schema.struct.json"), + @("05-collections\example2.json", "05-collections\schema.struct.json"), + @("05-collections\example3.json", "05-collections\schema.struct.json"), + @("06-tuples\example1.json", "06-tuples\schema.struct.json"), + @("06-tuples\example2.json", "06-tuples\schema.struct.json"), + @("06-tuples\example3.json", "06-tuples\schema.struct.json"), + @("07-unions\example1.json", "07-unions\schema.struct.json"), + @("07-unions\example2.json", "07-unions\schema.struct.json"), + @("07-unions\example3.json", "07-unions\schema.struct.json"), + @("08-namespaces\example1.json", "08-namespaces\schema.struct.json"), + @("08-namespaces\example2.json", "08-namespaces\schema.struct.json"), + @("08-namespaces\example3.json", "08-namespaces\schema.struct.json"), + @("09-extensions\example1.json", "09-extensions\schema.struct.json"), + @("09-extensions\example2.json", "09-extensions\schema.struct.json"), + @("09-extensions\example3.json", "09-extensions\schema.struct.json"), + @("10-discriminated-unions\example1.json", "10-discriminated-unions\schema.struct.json"), + @("10-discriminated-unions\example2.json", "10-discriminated-unions\schema.struct.json"), + @("10-discriminated-unions\example3.json", "10-discriminated-unions\schema.struct.json"), + @("11-sets-and-maps\example1.json", "11-sets-and-maps\schema.struct.json"), + @("11-sets-and-maps\example2.json", "11-sets-and-maps\schema.struct.json"), + @("11-sets-and-maps\example3.json", "11-sets-and-maps\schema.struct.json") +) + +foreach ($test in $tests) { + $instance = $test[0] + $schema = $test[1] + $result = python "..\py\json_structure_instance_validator.py" $instance $schema 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ $instance" -ForegroundColor Green + } else { + Write-Host "✗ $instance`: $result" -ForegroundColor Red + } +} + +Write-Host "`nValidation Complete!" -ForegroundColor Yellow \ No newline at end of file diff --git a/samples/import/.editorconfig b/samples/import/.editorconfig new file mode 100644 index 0000000..8682023 --- /dev/null +++ b/samples/import/.editorconfig @@ -0,0 +1,8 @@ +# See http://editorconfig.org + +root = true + +[*.{md,xml,org}] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/samples/import/.github/CODEOWNERS b/samples/import/.github/CODEOWNERS new file mode 100644 index 0000000..d3962d4 --- /dev/null +++ b/samples/import/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Automatically generated CODEOWNERS +# Regenerate with `make update-codeowners` +draft-vasters-json-structure-import.md clemensv@microsoft.com diff --git a/samples/import/.github/workflows/LICENSE.md b/samples/import/.github/workflows/LICENSE.md new file mode 100644 index 0000000..f002b82 --- /dev/null +++ b/samples/import/.github/workflows/LICENSE.md @@ -0,0 +1 @@ +This project is in the public domain. diff --git a/samples/import/.github/workflows/archive.yml b/samples/import/.github/workflows/archive.yml new file mode 100644 index 0000000..5f5d47d --- /dev/null +++ b/samples/import/.github/workflows/archive.yml @@ -0,0 +1,44 @@ +name: "Archive Issues and Pull Requests" + +on: + schedule: + - cron: '0 0 * * 0,2,4' + repository_dispatch: + types: [archive] + workflow_dispatch: + inputs: + archive_full: + description: 'Recreate the archive from scratch' + default: false + type: boolean + +jobs: + build: + name: "Archive Issues and Pull Requests" + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: "Checkout" + uses: actions/checkout@v4 + + # Note: No caching for this build! + + - name: "Update Archive" + uses: martinthomson/i-d-template@v1 + env: + ARCHIVE_FULL: ${{ inputs.archive_full }} + with: + make: archive + token: ${{ github.token }} + + - name: "Update GitHub Pages" + uses: martinthomson/i-d-template@v1 + with: + make: gh-archive + token: ${{ github.token }} + + - name: "Save Archive" + uses: actions/upload-artifact@v4 + with: + path: archive.json diff --git a/samples/import/.github/workflows/ghpages.yml b/samples/import/.github/workflows/ghpages.yml new file mode 100644 index 0000000..f6cb64d --- /dev/null +++ b/samples/import/.github/workflows/ghpages.yml @@ -0,0 +1,60 @@ +name: "Update Editor's Copy" + +on: + push: + paths-ignore: + - README.md + - CONTRIBUTING.md + - LICENSE.md + - .gitignore + pull_request: + paths-ignore: + - README.md + - CONTRIBUTING.md + - LICENSE.md + - .gitignore + +jobs: + build: + name: "Update Editor's Copy" + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: "Checkout" + uses: actions/checkout@v4 + + - name: "Setup" + id: setup + run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" + + - name: "Caching" + uses: actions/cache@v4 + with: + path: | + .refcache + .venv + .gems + node_modules + .targets.mk + key: i-d-${{ steps.setup.outputs.date }} + restore-keys: i-d- + + - name: "Build Drafts" + uses: martinthomson/i-d-template@v1 + with: + token: ${{ github.token }} + + - name: "Update GitHub Pages" + uses: martinthomson/i-d-template@v1 + if: ${{ github.event_name == 'push' }} + with: + make: gh-pages + token: ${{ github.token }} + + - name: "Archive Built Drafts" + uses: actions/upload-artifact@v4 + with: + path: | + draft-*.html + draft-*.txt diff --git a/samples/import/.github/workflows/publish.yml b/samples/import/.github/workflows/publish.yml new file mode 100644 index 0000000..94d885f --- /dev/null +++ b/samples/import/.github/workflows/publish.yml @@ -0,0 +1,57 @@ +name: "Publish New Draft Version" + +on: + push: + tags: + - "draft-*" + workflow_dispatch: + inputs: + email: + description: "Submitter email" + default: "" + type: string + +jobs: + build: + name: "Publish New Draft Version" + runs-on: ubuntu-latest + steps: + - name: "Checkout" + uses: actions/checkout@v4 + + # See https://github.com/actions/checkout/issues/290 + - name: "Get Tag Annotations" + run: git fetch -f origin ${{ github.ref }}:${{ github.ref }} + + - name: "Setup" + id: setup + run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" + + - name: "Caching" + uses: actions/cache@v4 + with: + path: | + .refcache + .venv + .gems + node_modules + .targets.mk + key: i-d-${{ steps.setup.outputs.date }} + restore-keys: i-d- + + - name: "Build Drafts" + uses: martinthomson/i-d-template@v1 + with: + token: ${{ github.token }} + + - name: "Upload to Datatracker" + uses: martinthomson/i-d-template@v1 + with: + make: upload + env: + UPLOAD_EMAIL: ${{ inputs.email }} + + - name: "Archive Submitted Drafts" + uses: actions/upload-artifact@v4 + with: + path: "versioned/draft-*-[0-9][0-9].*" diff --git a/samples/import/.github/workflows/update.yml b/samples/import/.github/workflows/update.yml new file mode 100644 index 0000000..0f8d6b8 --- /dev/null +++ b/samples/import/.github/workflows/update.yml @@ -0,0 +1,36 @@ +name: "Update Generated Files" +# This rule is not run automatically. +# It can be run manually to update all of the files that are part +# of the template, specifically: +# - README.md +# - CONTRIBUTING.md +# - .note.xml +# - .github/CODEOWNERS +# - Makefile +# +# +# This might be useful if you have: +# - added, removed, or renamed drafts (including after adoption) +# - added, removed, or changed draft editors +# - changed the title of drafts +# +# Note that this removes any customizations you have made to +# the affected files. +on: workflow_dispatch + +jobs: + build: + name: "Update Files" + runs-on: ubuntu-latest + steps: + - name: "Checkout" + uses: actions/checkout@v4 + + - name: "Update Generated Files" + uses: martinthomson/i-d-template@v1 + with: + make: update-files + token: ${{ github.token }} + + - name: "Push Update" + run: git push diff --git a/samples/import/.gitignore b/samples/import/.gitignore new file mode 100644 index 0000000..5483fe2 --- /dev/null +++ b/samples/import/.gitignore @@ -0,0 +1,24 @@ +*.html +*.pdf +*.redxml +*.swp +*.txt +*.upload +*~ +.tags +/*-[0-9][0-9].xml +/.*.mk +/.gems/ +/.idea/ +/.refcache +/.venv/ +/.vscode/ +/lib +/node_modules/ +/versioned/ +Gemfile.lock +archive.json +draft-vasters-json-structure-import.xml +package-lock.json +report.xml +!requirements.txt diff --git a/samples/import/01-root-import/example.json b/samples/import/01-root-import/example.json new file mode 100644 index 0000000..f3f6ebb --- /dev/null +++ b/samples/import/01-root-import/example.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://schemas.example.com/examples/full-import", + "customerId": "CUST001", + "person": { + "id": "550e8400-e29b-41d4-a716-446655440001", + "firstName": "Alice", + "lastName": "Johnson", + "email": "alice.johnson@email.com", + "dateOfBirth": "1988-03-15", + "address": { + "street": "123 Main Street", + "city": "Seattle", + "state": "Washington", + "postalCode": "98101", + "country": "US" + } + }, + "contactInfo": { + "phone": "+1-555-123-4567", + "mobile": "+1-555-987-6543", + "emergencyContact": "Bob Johnson +1-555-111-2222" + }, + "registrationDate": "2023-01-15", + "isActive": true, + "loyaltyLevel": "gold" +} \ No newline at end of file diff --git a/samples/import/01-root-import/schema.struct.json b/samples/import/01-root-import/schema.struct.json new file mode 100644 index 0000000..4aebb53 --- /dev/null +++ b/samples/import/01-root-import/schema.struct.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": "https://schemas.example.com/examples/full-import", + "name": "CustomerProfile", + "description": "Demonstrates $import to bring in complete external schema", + "$uses": ["JSONStructureValidation"], + "$import": "../02-namespace-import/person-library/schema.struct.json", + "type": "object", + "properties": { + "customerId": { + "type": "string", + "description": "Customer identifier", + "maxLength": 20 + }, + "person": { + "type": { "$ref": "#/definitions/Person" }, + "description": "Customer personal information" + }, + "contactInfo": { + "type": { "$ref": "#/definitions/ContactInfo" }, + "description": "Additional contact methods" + }, + "registrationDate": { + "type": "date", + "description": "When customer registered" + }, + "isActive": { + "type": "boolean", + "description": "Whether customer account is active", + "default": true + }, + "loyaltyLevel": { + "type": "string", + "enum": ["bronze", "silver", "gold", "platinum"], + "description": "Customer loyalty tier", + "default": "bronze" + } + }, + "required": ["customerId", "person", "registrationDate"] +} \ No newline at end of file diff --git a/samples/import/02-namespace-import/example.json b/samples/import/02-namespace-import/example.json new file mode 100644 index 0000000..2912432 --- /dev/null +++ b/samples/import/02-namespace-import/example.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://schemas.example.com/examples/namespace-import", + "orderId": "550e8400-e29b-41d4-a716-446655440002", + "customer": { + "id": "550e8400-e29b-41d4-a716-446655440003", + "firstName": "Maria", + "lastName": "Garcia", + "email": "maria.garcia@example.com", + "dateOfBirth": "1990-07-22", + "address": { + "street": "456 Oak Avenue", + "city": "Portland", + "state": "Oregon", + "postalCode": "97201", + "country": "US" + } + }, + "billingAddress": { + "street": "456 Oak Avenue", + "city": "Portland", + "state": "Oregon", + "postalCode": "97201", + "country": "US" + }, + "shippingAddress": { + "street": "789 Pine Street", + "city": "Portland", + "state": "Oregon", + "postalCode": "97202", + "country": "US" + }, + "payment": { + "id": "550e8400-e29b-41d4-a716-446655440004", + "amount": { + "amount": "125.99", + "currency": "USD" + }, + "paymentMethod": { + "type": "credit_card", + "last4Digits": "1234", + "expiryMonth": 12, + "expiryYear": 2026 + }, + "timestamp": "2023-11-13T15:30:00Z", + "status": "completed", + "description": "Order payment for electronics" + }, + "items": [ + { + "productId": "PROD001", + "productName": "Wireless Headphones", + "quantity": 1, + "unitPrice": { + "amount": "89.99", + "currency": "USD" + }, + "lineTotal": { + "amount": "89.99", + "currency": "USD" + } + }, + { + "productId": "PROD002", + "productName": "Phone Case", + "quantity": 2, + "unitPrice": { + "amount": "18.00", + "currency": "USD" + }, + "lineTotal": { + "amount": "36.00", + "currency": "USD" + } + } + ], + "totalAmount": { + "amount": "125.99", + "currency": "USD" + }, + "orderDate": "2023-11-13T15:25:00Z", + "status": "processing" +} \ No newline at end of file diff --git a/samples/import/02-namespace-import/financial-library/schema.struct.json b/samples/import/02-namespace-import/financial-library/schema.struct.json new file mode 100644 index 0000000..830bdde --- /dev/null +++ b/samples/import/02-namespace-import/financial-library/schema.struct.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/common/financial", + "name": "MoneyAmount", + "description": "Financial types library for reuse across schemas", + "type": "object", + "properties": { + "amount": { + "type": { "$ref": "#/definitions/Money" } + } + }, + "definitions": { + "Money": { + "type": "object", + "description": "Monetary amount with currency", + "properties": { + "amount": { + "type": "decimal", + "description": "The monetary amount", + "precision": 15, + "scale": 2 + }, + "currency": { + "type": "string", + "description": "ISO 4217 currency code", + "enum": ["USD", "EUR", "GBP", "JPY", "CAD", "AUD", "CHF", "CNY"], + "examples": ["USD", "EUR"] + } + }, + "required": ["amount", "currency"] + }, + "PaymentMethod": { + "type": "object", + "description": "Payment method information", + "properties": { + "type": { + "type": "string", + "enum": ["credit_card", "debit_card", "bank_transfer", "paypal", "cash"], + "description": "Payment method type" + }, + "last4Digits": { + "type": "string", + "description": "Last 4 digits (for cards)", + "maxLength": 4 + }, + "expiryMonth": { + "type": "uint8", + "description": "Expiry month for cards" + }, + "expiryYear": { + "type": "uint16", + "description": "Expiry year for cards" + } + }, + "required": ["type"] + }, + "Transaction": { + "type": "object", + "description": "Financial transaction", + "properties": { + "id": { + "type": "uuid", + "description": "Transaction identifier" + }, + "amount": { + "type": { "$ref": "#/definitions/Money" }, + "description": "Transaction amount" + }, + "paymentMethod": { + "type": { "$ref": "#/definitions/PaymentMethod" }, + "description": "How payment was made" + }, + "timestamp": { + "type": "datetime", + "description": "When transaction occurred" + }, + "status": { + "type": "string", + "enum": ["pending", "completed", "failed", "cancelled"], + "description": "Transaction status" + }, + "description": { + "type": "string", + "description": "Transaction description", + "maxLength": 200 + } + }, + "required": ["id", "amount", "timestamp", "status"] + } + } +} \ No newline at end of file diff --git a/samples/import/02-namespace-import/person-library/schema.struct.json b/samples/import/02-namespace-import/person-library/schema.struct.json new file mode 100644 index 0000000..a41bb57 --- /dev/null +++ b/samples/import/02-namespace-import/person-library/schema.struct.json @@ -0,0 +1,93 @@ +{ + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://schemas.example.com/common/person", + "name": "Person", + "description": "Common person type for reuse across schemas", + "type": "object", + "properties": { + "id": { + "type": "uuid", + "description": "Unique person identifier" + }, + "firstName": { + "type": "string", + "description": "Person's first name", + "maxLength": 50 + }, + "lastName": { + "type": "string", + "description": "Person's last name", + "maxLength": 50 + }, + "email": { + "type": "string", + "description": "Email address", + "examples": ["john.doe@example.com"] + }, + "dateOfBirth": { + "type": "date", + "description": "Date of birth" + }, + "address": { + "type": { "$ref": "#/definitions/Address" }, + "description": "Home address" + } + }, + "required": ["id", "firstName", "lastName", "email"], + "definitions": { + "Address": { + "type": "object", + "description": "Physical address", + "properties": { + "street": { + "type": "string", + "description": "Street address", + "maxLength": 100 + }, + "city": { + "type": "string", + "description": "City name", + "maxLength": 50 + }, + "state": { + "type": "string", + "description": "State or province", + "maxLength": 50 + }, + "postalCode": { + "type": "string", + "description": "Postal/ZIP code", + "maxLength": 20 + }, + "country": { + "type": "string", + "description": "Country code", + "enum": ["US", "CA", "MX", "UK", "DE", "FR", "JP", "AU"], + "examples": ["US", "CA"] + } + }, + "required": ["street", "city", "country"] + }, + "ContactInfo": { + "type": "object", + "description": "Contact information", + "properties": { + "phone": { + "type": "string", + "description": "Phone number", + "maxLength": 20 + }, + "mobile": { + "type": "string", + "description": "Mobile phone number", + "maxLength": 20 + }, + "emergencyContact": { + "type": "string", + "description": "Emergency contact name and number", + "maxLength": 100 + } + } + } + } +} \ No newline at end of file diff --git a/samples/import/02-namespace-import/schema.struct.json b/samples/import/02-namespace-import/schema.struct.json new file mode 100644 index 0000000..93913f3 --- /dev/null +++ b/samples/import/02-namespace-import/schema.struct.json @@ -0,0 +1,85 @@ +{ + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": "https://schemas.example.com/examples/namespace-import", + "name": "OrderSystem", + "description": "Demonstrates importing into specific namespaces", + "type": "object", + "properties": { + "orderId": { + "type": "uuid", + "description": "Unique order identifier" + }, + "customer": { + "type": { "$ref": "#/definitions/People/Person" }, + "description": "Customer who placed the order" + }, + "billingAddress": { + "type": { "$ref": "#/definitions/People/Address" }, + "description": "Billing address" + }, + "shippingAddress": { + "type": { "$ref": "#/definitions/People/Address" }, + "description": "Shipping address" + }, + "payment": { + "type": { "$ref": "#/definitions/Finance/Transaction" }, + "description": "Payment transaction" + }, + "items": { + "type": "array", + "description": "Order items", + "items": { "$ref": "#/definitions/OrderItem" } + }, + "totalAmount": { + "type": { "$ref": "#/definitions/Finance/Money" }, + "description": "Total order amount" + }, + "orderDate": { + "type": "datetime", + "description": "When order was placed" + }, + "status": { + "type": "string", + "enum": ["pending", "processing", "shipped", "delivered", "cancelled"], + "description": "Order status" + } + }, + "required": ["orderId", "customer", "payment", "items", "totalAmount", "orderDate", "status"], + "definitions": { + "People": { + "$import": "person-library/schema.struct.json" + }, + "Finance": { + "$import": "financial-library/schema.struct.json" + }, + "OrderItem": { + "type": "object", + "description": "Single item in an order", + "properties": { + "productId": { + "type": "string", + "description": "Product identifier", + "maxLength": 50 + }, + "productName": { + "type": "string", + "description": "Product name", + "maxLength": 100 + }, + "quantity": { + "type": "uint16", + "description": "Number of items" + }, + "unitPrice": { + "type": { "$ref": "#/definitions/Finance/Money" }, + "description": "Price per unit" + }, + "lineTotal": { + "type": { "$ref": "#/definitions/Finance/Money" }, + "description": "Total for this line item" + } + }, + "required": ["productId", "productName", "quantity", "unitPrice", "lineTotal"] + } + } +} \ No newline at end of file diff --git a/samples/import/03-importdefs-only/example.json b/samples/import/03-importdefs-only/example.json new file mode 100644 index 0000000..408847e --- /dev/null +++ b/samples/import/03-importdefs-only/example.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schemas.example.com/examples/importdefs-only", + "employeeId": "EMP12345", + "personalInfo": { + "street": "321 Elm Street", + "city": "Austin", + "state": "Texas", + "postalCode": "78701", + "country": "US" + }, + "workContact": { + "phone": "+1-512-555-0123", + "mobile": "+1-512-555-0456" + }, + "salary": { + "amount": "85000.00", + "currency": "USD" + }, + "department": "Engineering", + "hireDate": "2022-03-01", + "position": "Software Engineer II", + "manager": "EMP98765", + "isActive": true +} \ No newline at end of file diff --git a/samples/import/03-importdefs-only/schema.struct.json b/samples/import/03-importdefs-only/schema.struct.json new file mode 100644 index 0000000..c6eadf4 --- /dev/null +++ b/samples/import/03-importdefs-only/schema.struct.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": "https://schemas.example.com/examples/importdefs-only", + "name": "EmployeeRecord", + "description": "Demonstrates $importdefs to import only definitions (not root types)", + "$uses": ["JSONStructureValidation"], + "type": "object", + "properties": { + "employeeId": { + "type": "string", + "description": "Employee identifier", + "maxLength": 10 + }, + "personalInfo": { + "type": { "$ref": "#/definitions/PersonLib/Address" }, + "description": "Employee home address - using imported Address type" + }, + "workContact": { + "type": { "$ref": "#/definitions/PersonLib/ContactInfo" }, + "description": "Work contact information - using imported ContactInfo type" + }, + "salary": { + "type": { "$ref": "#/definitions/FinanceLib/Money" }, + "description": "Employee salary - using imported Money type" + }, + "department": { + "type": "string", + "enum": ["HR", "Engineering", "Sales", "Marketing", "Finance"], + "description": "Employee department" + }, + "hireDate": { + "type": "date", + "description": "Employee hire date" + }, + "position": { + "type": "string", + "description": "Job title", + "maxLength": 100 + }, + "manager": { + "type": "string", + "description": "Manager's employee ID", + "maxLength": 10 + }, + "isActive": { + "type": "boolean", + "description": "Whether employee is currently active", + "default": true + } + }, + "required": ["employeeId", "personalInfo", "salary", "department", "hireDate", "position"], + "definitions": { + "PersonLib": { + "$importdefs": "../02-namespace-import/person-library/schema.struct.json" + }, + "FinanceLib": { + "$importdefs": "../02-namespace-import/financial-library/schema.struct.json" + } + } +} \ No newline at end of file diff --git a/samples/import/04-shadowing/example.json b/samples/import/04-shadowing/example.json new file mode 100644 index 0000000..f4d617a --- /dev/null +++ b/samples/import/04-shadowing/example.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://schemas.example.com/examples/shadowing", + "customerId": "550e8400-e29b-41d4-a716-446655440005", + "profile": { + "id": "550e8400-e29b-41d4-a716-446655440006", + "firstName": "David", + "lastName": "Chen", + "email": "david.chen@example.com", + "dateOfBirth": "1985-11-08", + "address": { + "street": "999 Technology Drive", + "unit": "Apt 42", + "city": "San Francisco", + "state": "California", + "postalCode": "94105", + "country": "US", + "coordinates": { + "latitude": "37.7749295", + "longitude": "-122.4194155" + }, + "deliveryInstructions": "Ring buzzer for Apt 42. Use side entrance after 6pm." + } + }, + "preferences": { + "communicationChannels": ["email", "sms"], + "marketingOptIn": true, + "language": "en", + "timezone": "America/Los_Angeles" + }, + "paymentMethods": [ + { + "type": "credit_card", + "last4Digits": "5678", + "expiryMonth": 8, + "expiryYear": 2027 + }, + { + "type": "paypal" + } + ] +} \ No newline at end of file diff --git a/samples/import/04-shadowing/schema.struct.json b/samples/import/04-shadowing/schema.struct.json new file mode 100644 index 0000000..8565ee9 --- /dev/null +++ b/samples/import/04-shadowing/schema.struct.json @@ -0,0 +1,126 @@ +{ + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": "https://schemas.example.com/examples/shadowing", + "name": "EnhancedCustomer", + "description": "Demonstrates shadowing - overriding imported types with local definitions", + "$uses": ["JSONStructureValidation"], + "type": "object", + "properties": { + "customerId": { + "type": "uuid", + "description": "Customer identifier" + }, + "profile": { + "type": { "$ref": "#/definitions/CustomerTypes/Person" }, + "description": "Customer profile using imported base but enhanced Address" + }, + "preferences": { + "type": { "$ref": "#/definitions/CustomerPreferences" }, + "description": "Customer preferences" + }, + "paymentMethods": { + "type": "array", + "description": "Available payment methods", + "items": { "$ref": "#/definitions/FinanceTypes/PaymentMethod" } + } + }, + "required": ["customerId", "profile"], + "definitions": { + "CustomerTypes": { + "$import": "../02-namespace-import/person-library/schema.struct.json", + "Address": { + "type": "object", + "description": "Enhanced address with additional fields - shadows imported Address", + "properties": { + "street": { + "type": "string", + "description": "Street address", + "maxLength": 100 + }, + "unit": { + "type": "string", + "description": "Apartment/unit number", + "maxLength": 20 + }, + "city": { + "type": "string", + "description": "City name", + "maxLength": 50 + }, + "state": { + "type": "string", + "description": "State or province", + "maxLength": 50 + }, + "postalCode": { + "type": "string", + "description": "Postal/ZIP code", + "maxLength": 20 + }, + "country": { + "type": "string", + "description": "Country code", + "enum": ["US", "CA", "MX", "UK", "DE", "FR", "JP", "AU"], + "examples": ["US", "CA"] + }, + "coordinates": { + "type": "object", + "description": "GPS coordinates for delivery", + "properties": { + "latitude": { + "type": "decimal", + "precision": 10, + "scale": 7 + }, + "longitude": { + "type": "decimal", + "precision": 10, + "scale": 7 + } + }, + "required": ["latitude", "longitude"] + }, + "deliveryInstructions": { + "type": "string", + "description": "Special delivery instructions", + "maxLength": 500 + } + }, + "required": ["street", "city", "country"] + } + }, + "FinanceTypes": { + "$importdefs": "../02-namespace-import/financial-library/schema.struct.json" + }, + "CustomerPreferences": { + "type": "object", + "description": "Customer preferences and settings", + "properties": { + "communicationChannels": { + "type": "array", + "description": "Preferred communication methods", + "items": { + "type": "string", + "enum": ["email", "sms", "phone", "mail", "push_notification"] + } + }, + "marketingOptIn": { + "type": "boolean", + "description": "Opt-in for marketing communications", + "default": false + }, + "language": { + "type": "string", + "description": "Preferred language code", + "enum": ["en", "es", "fr", "de", "ja", "zh"], + "default": "en" + }, + "timezone": { + "type": "string", + "description": "Customer timezone", + "examples": ["America/New_York", "Europe/London", "Asia/Tokyo"] + } + } + } + } +} \ No newline at end of file diff --git a/samples/import/CONTRIBUTING.md b/samples/import/CONTRIBUTING.md new file mode 100644 index 0000000..66194e4 --- /dev/null +++ b/samples/import/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Contributing + +This repository relates to activities in the Internet Engineering Task Force +([IETF](https://www.ietf.org/)). All material in this repository is considered +Contributions to the IETF Standards Process, as defined in the intellectual +property policies of IETF currently designated as +[BCP 78](https://www.rfc-editor.org/info/bcp78), +[BCP 79](https://www.rfc-editor.org/info/bcp79) and the +[IETF Trust Legal Provisions (TLP) Relating to IETF Documents](http://trustee.ietf.org/trust-legal-provisions.html). + +Any edit, commit, pull request, issue, comment or other change made to this +repository constitutes Contributions to the IETF Standards Process +(https://www.ietf.org/). + +You agree to comply with all applicable IETF policies and procedures, including, +BCP 78, 79, the TLP, and the TLP rules regarding code components (e.g. being +subject to a Simplified BSD License) in Contributions. + + +## Working Group Information + +Discussion of this work occurs on the [JavaScript Object Notation +Working Group mailing list](mailto:json@ietf.org) +([archive](https://mailarchive.ietf.org/arch/browse/json), +[subscribe](https://www.ietf.org/mailman/listinfo/json)). +In addition to contributions in GitHub, you are encouraged to participate in +discussions there. + +**Note**: Some working groups adopt a policy whereby substantive discussion of +technical issues needs to occur on the mailing list. + +You might also like to familiarize yourself with other +[Working Group documents](https://datatracker.ietf.org/wg/json/documents/). diff --git a/samples/import/LICENSE.md b/samples/import/LICENSE.md new file mode 100644 index 0000000..9f59dc6 --- /dev/null +++ b/samples/import/LICENSE.md @@ -0,0 +1,4 @@ +# License + +See the +[guidelines for contributions](https://github.com/json-structure/import/blob/main/CONTRIBUTING.md). diff --git a/samples/import/Makefile b/samples/import/Makefile new file mode 100644 index 0000000..9a5d15b --- /dev/null +++ b/samples/import/Makefile @@ -0,0 +1,15 @@ +LIBDIR := lib +include $(LIBDIR)/main.mk + +$(LIBDIR)/main.mk: +ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null)) + git submodule sync + git submodule update --init +else +ifneq (,$(wildcard $(ID_TEMPLATE_HOME))) + ln -s "$(ID_TEMPLATE_HOME)" $(LIBDIR) +else + git clone -q --depth 10 -b main \ + https://github.com/martinthomson/i-d-template $(LIBDIR) +endif +endif diff --git a/samples/import/README.md b/samples/import/README.md new file mode 100644 index 0000000..572b462 --- /dev/null +++ b/samples/import/README.md @@ -0,0 +1,51 @@ +# JSON Structure Import Extension Examples + +This directory contains comprehensive examples demonstrating the JSON Structure Import Extension features. The Import Extension allows modular schema composition through the `$import` and `$importdefs` keywords. + +## Examples Overview + +The examples are organized in numbered directories, each containing a schema (`schema.struct.json`) and optionally example instances: + +### Import Pattern Examples +- **01-root-import/** - Direct root-level import of entire schemas +- **02-namespace-import/** - Namespace-qualified imports with prefixes (contains library schemas) + - **person-library/** - Reusable person and contact information types + - **financial-library/** - Reusable financial and payment types +- **03-importdefs-only/** - Importing only definitions without exposing root type +- **04-shadowing/** - Namespace shadowing and conflict resolution + +## Key Features Demonstrated + +### Import Mechanisms +- **`$import`**: Direct import of schemas with optional namespace prefixes +- **`$importdefs`**: Import only the definitions from external schemas +- **Namespace management**: Organize imported types under prefixed namespaces +- **Shadowing resolution**: Handle naming conflicts between imported schemas + +### Advanced Patterns +- **Modular design**: Reusable library schemas for common types +- **Namespace organization**: Clean separation of imported type hierarchies +- **Local extensions**: Extending imported types with additional properties +- **Mixed imports**: Combining direct imports and definition-only imports +- **Relative paths**: Local file imports for development and testing + +## Validation + +Use the included PowerShell script to validate all examples: + +```powershell +.\validate-imports.ps1 +``` + +This script validates: +1. Library schemas for correctness +2. Import example schemas (schema validation may show URI errors with relative paths) +3. Example instances against their schemas (all pass validation) + +## Technical Notes + +- Import URIs now use relative paths for local development +- Library schemas are co-located with the examples that use them most +- All example instances validate successfully +- Schema validation may show warnings about relative URIs but functionality works correctly +- The reorganized structure demonstrates real-world modular schema development patterns \ No newline at end of file diff --git a/samples/import/validate-imports.ps1 b/samples/import/validate-imports.ps1 new file mode 100644 index 0000000..373a0ce --- /dev/null +++ b/samples/import/validate-imports.ps1 @@ -0,0 +1,60 @@ +# Test JSON Structure Import Extension Examples +Write-Host "Testing JSON Structure Import Extension Examples" -ForegroundColor Yellow +Write-Host "=" * 60 + +# Test library schemas first +Write-Host "`nTesting Library Schemas:" -ForegroundColor Cyan +$libraries = @( + "02-namespace-import\person-library\schema.struct.json", + "02-namespace-import\financial-library\schema.struct.json" +) + +foreach ($lib in $libraries) { + $result = python "..\py\json_structure_schema_validator.py" --extended --allowimport $lib 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ $lib" -ForegroundColor Green + } else { + Write-Host "✗ $lib`: $result" -ForegroundColor Red + } +} + +# Test import example schemas +Write-Host "`nTesting Import Example Schemas:" -ForegroundColor Cyan +$importSchemas = @( + "01-root-import\schema.struct.json", + "02-namespace-import\schema.struct.json", + "03-importdefs-only\schema.struct.json", + "04-shadowing\schema.struct.json" +) + +# No longer need import maps since using relative paths +foreach ($schema in $importSchemas) { + $result = python "..\py\json_structure_schema_validator.py" --extended --allowimport $schema 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ $schema" -ForegroundColor Green + } else { + Write-Host "✗ $schema`: $result" -ForegroundColor Red + } +} + +# Test example instances +Write-Host "`nTesting Example Instances:" -ForegroundColor Cyan +$instances = @( + @("01-root-import\example.json", "01-root-import\schema.struct.json"), + @("02-namespace-import\example.json", "02-namespace-import\schema.struct.json"), + @("03-importdefs-only\example.json", "03-importdefs-only\schema.struct.json"), + @("04-shadowing\example.json", "04-shadowing\schema.struct.json") +) + +foreach ($test in $instances) { + $instance = $test[0] + $schema = $test[1] + $result = python "..\py\json_structure_instance_validator.py" --extended $instance $schema 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "✓ $instance" -ForegroundColor Green + } else { + Write-Host "✗ $instance`: $result" -ForegroundColor Red + } +} + +Write-Host "`nImport Extension Validation Complete!" -ForegroundColor Yellow \ No newline at end of file From 1b7d08a803ce9b97c9e2e567598c1ba1d41073fb Mon Sep 17 00:00:00 2001 From: Clemens Vasters Date: Thu, 27 Nov 2025 08:40:46 +0100 Subject: [PATCH 2/4] feat: ensure all JSON Structure types are covered in samples and validators - Updated schema and instance validators to support all primitive types: - Added int8, uint8, int16, uint16, int128, uint128, float8 validation - Added integer type (alias for int32 per spec PR #16) - Moved 'any' from primitive to compound types - Updated 03-financial-types sample to demonstrate all types: - Added number, integer, double, null, int16, int32 type usage - Updated examples with new fields (weight, discountPercent, paymentTermsDays, cancelledDate, sequenceNumber) All 34 JSON Structure types are now covered across the samples. --- samples/core/03-financial-types/example1.json | 16 ++++-- samples/core/03-financial-types/example2.json | 10 +++- samples/core/03-financial-types/example3.json | 24 +++++--- .../03-financial-types/schema.struct.json | 27 ++++++--- .../py/json_structure_instance_validator.py | 57 +++++++++++++++++-- samples/py/json_structure_schema_validator.py | 6 +- 6 files changed, 108 insertions(+), 32 deletions(-) diff --git a/samples/core/03-financial-types/example1.json b/samples/core/03-financial-types/example1.json index 431df47..d473a43 100644 --- a/samples/core/03-financial-types/example1.json +++ b/samples/core/03-financial-types/example1.json @@ -3,10 +3,11 @@ "invoiceNumber": "INV-2023-001", "issueDate": "2023-11-13", "dueDate": "2023-12-13", + "paymentTermsDays": 30, "lineItems": [ { "description": "Professional Services", - "quantity": "10.000", + "quantity": 10, "unitPrice": { "amount": "150.00", "currency": "USD" @@ -14,11 +15,13 @@ "totalPrice": { "amount": "1500.00", "currency": "USD" - } + }, + "weight": 0.0, + "discountPercent": 0 }, { "description": "Software License", - "quantity": "1.000", + "quantity": 1, "unitPrice": { "amount": "299.99", "currency": "USD" @@ -26,7 +29,9 @@ "totalPrice": { "amount": "299.99", "currency": "USD" - } + }, + "weight": 0.5, + "discountPercent": 10.5 } ], "subtotal": { @@ -41,5 +46,6 @@ "totalAmount": { "amount": "1957.49", "currency": "USD" - } + }, + "cancelledDate": null } \ No newline at end of file diff --git a/samples/core/03-financial-types/example2.json b/samples/core/03-financial-types/example2.json index 5167d71..e9a0098 100644 --- a/samples/core/03-financial-types/example2.json +++ b/samples/core/03-financial-types/example2.json @@ -3,10 +3,11 @@ "invoiceNumber": "INV-2023-002", "issueDate": "2023-11-14", "dueDate": "2023-11-28", + "paymentTermsDays": 14, "lineItems": [ { "description": "Monthly Subscription", - "quantity": "1.000", + "quantity": 1, "unitPrice": { "amount": "49.99", "currency": "EUR" @@ -14,7 +15,9 @@ "totalPrice": { "amount": "49.99", "currency": "EUR" - } + }, + "weight": 0.0, + "discountPercent": 0 } ], "subtotal": { @@ -24,5 +27,6 @@ "totalAmount": { "amount": "49.99", "currency": "EUR" - } + }, + "cancelledDate": "2023-11-20" } \ No newline at end of file diff --git a/samples/core/03-financial-types/example3.json b/samples/core/03-financial-types/example3.json index 407a076..cc9a176 100644 --- a/samples/core/03-financial-types/example3.json +++ b/samples/core/03-financial-types/example3.json @@ -3,22 +3,25 @@ "invoiceNumber": "INV-2023-003", "issueDate": "2023-11-15", "dueDate": "2023-12-15", + "paymentTermsDays": 30, "lineItems": [ { "description": "Consulting Hours", - "quantity": "25.500", + "quantity": 26, "unitPrice": { "amount": "200.00", "currency": "GBP" }, "totalPrice": { - "amount": "5100.00", + "amount": "5200.00", "currency": "GBP" - } + }, + "weight": 0.0, + "discountPercent": 5 }, { "description": "Travel Expenses", - "quantity": "1.000", + "quantity": 1, "unitPrice": { "amount": "450.75", "currency": "GBP" @@ -26,20 +29,23 @@ "totalPrice": { "amount": "450.75", "currency": "GBP" - } + }, + "weight": 15.5, + "discountPercent": 0 } ], "subtotal": { - "amount": "5550.75", + "amount": "5650.75", "currency": "GBP" }, "taxRate": "0.2000", "taxAmount": { - "amount": "1110.15", + "amount": "1130.15", "currency": "GBP" }, "totalAmount": { - "amount": "6660.90", + "amount": "6780.90", "currency": "GBP" - } + }, + "cancelledDate": null } \ No newline at end of file diff --git a/samples/core/03-financial-types/schema.struct.json b/samples/core/03-financial-types/schema.struct.json index d3ec955..d531225 100644 --- a/samples/core/03-financial-types/schema.struct.json +++ b/samples/core/03-financial-types/schema.struct.json @@ -34,10 +34,8 @@ "maxLength": 200 }, "quantity": { - "type": "decimal", - "description": "Quantity of items", - "precision": 10, - "scale": 3 + "type": "integer", + "description": "Quantity of items (whole number)" }, "unitPrice": { "type": { "$ref": "#/definitions/Money" }, @@ -46,6 +44,14 @@ "totalPrice": { "type": { "$ref": "#/definitions/Money" }, "description": "Total price for this line item" + }, + "weight": { + "type": "double", + "description": "Weight of the item in kilograms (double precision)" + }, + "discountPercent": { + "type": "number", + "description": "Discount percentage as a raw JSON number" } }, "required": ["description", "quantity", "unitPrice", "totalPrice"] @@ -60,8 +66,12 @@ "maxLength": 50 }, "sequenceNumber": { + "type": "int32", + "description": "Sequential invoice number for internal tracking" + }, + "globalSequenceNumber": { "type": "int64", - "description": "Sequential invoice number for audit trail" + "description": "Global sequential invoice number for audit trail" }, "issueDate": { "type": "date", @@ -73,8 +83,7 @@ }, "paymentTermsDays": { "type": "int16", - "description": "Number of days for payment terms", - "default": 30 + "description": "Number of days for payment terms (1-365 typical range)" }, "lineItems": { "type": "array", @@ -106,6 +115,10 @@ "blockchainHash": { "type": "uint128", "description": "Unsigned 128-bit blockchain transaction hash for immutable record" + }, + "cancelledDate": { + "type": ["date", "null"], + "description": "Date the invoice was cancelled, or null if not cancelled" } }, "required": ["invoiceNumber", "issueDate", "dueDate", "lineItems", "subtotal", "totalAmount"] diff --git a/samples/py/json_structure_instance_validator.py b/samples/py/json_structure_instance_validator.py index 3ba54b8..e4679bc 100644 --- a/samples/py/json_structure_instance_validator.py +++ b/samples/py/json_structure_instance_validator.py @@ -258,11 +258,31 @@ def validate_instance(self, instance, schema=None, path="#", meta=None): elif schema_type == "null": if instance is not None: self.errors.append(f"Expected null at {path}, got {type(instance).__name__}") - elif schema_type == "int32": + elif schema_type == "int8": if not isinstance(instance, int): - self.errors.append(f"Expected int32 at {path}, got {type(instance).__name__}") + self.errors.append(f"Expected int8 at {path}, got {type(instance).__name__}") + elif not (-2**7 <= instance <= 2**7 - 1): + self.errors.append(f"int8 value at {path} out of range") + elif schema_type == "uint8": + if not isinstance(instance, int): + self.errors.append(f"Expected uint8 at {path}, got {type(instance).__name__}") + elif not (0 <= instance <= 2**8 - 1): + self.errors.append(f"uint8 value at {path} out of range") + elif schema_type == "int16": + if not isinstance(instance, int): + self.errors.append(f"Expected int16 at {path}, got {type(instance).__name__}") + elif not (-2**15 <= instance <= 2**15 - 1): + self.errors.append(f"int16 value at {path} out of range") + elif schema_type == "uint16": + if not isinstance(instance, int): + self.errors.append(f"Expected uint16 at {path}, got {type(instance).__name__}") + elif not (0 <= instance <= 2**16 - 1): + self.errors.append(f"uint16 value at {path} out of range") + elif schema_type == "int32" or schema_type == "integer": + if not isinstance(instance, int): + self.errors.append(f"Expected {schema_type} at {path}, got {type(instance).__name__}") elif not (-2**31 <= instance <= 2**31 - 1): - self.errors.append(f"int32 value at {path} out of range") + self.errors.append(f"{schema_type} value at {path} out of range") elif schema_type == "uint32": if not isinstance(instance, int): self.errors.append(f"Expected uint32 at {path}, got {type(instance).__name__}") @@ -288,6 +308,29 @@ def validate_instance(self, instance, schema=None, path="#", meta=None): self.errors.append(f"uint64 value at {path} out of range") except ValueError: self.errors.append(f"Invalid uint64 format at {path}") + elif schema_type == "int128": + if not isinstance(instance, str): + self.errors.append(f"Expected int128 as string at {path}, got {type(instance).__name__}") + else: + try: + value = int(instance) + if not (-2**127 <= value <= 2**127 - 1): + self.errors.append(f"int128 value at {path} out of range") + except ValueError: + self.errors.append(f"Invalid int128 format at {path}") + elif schema_type == "uint128": + if not isinstance(instance, str): + self.errors.append(f"Expected uint128 as string at {path}, got {type(instance).__name__}") + else: + try: + value = int(instance) + if not (0 <= value <= 2**128 - 1): + self.errors.append(f"uint128 value at {path} out of range") + except ValueError: + self.errors.append(f"Invalid uint128 format at {path}") + elif schema_type == "float8": + if not isinstance(instance, (int, float)): + self.errors.append(f"Expected float8 at {path}, got {type(instance).__name__}") elif schema_type in ("float", "double"): if not isinstance(instance, (int, float)): self.errors.append(f"Expected {schema_type} at {path}, got {type(instance).__name__}") @@ -548,7 +591,9 @@ def validate(self, instance, schema=None): import re if not re.match(schema["pattern"], instance): errors.append(f"String does not match pattern {schema['pattern']}") - if t == "number" or t == "int32" or t == "float" or t == "double": + if t in ("number", "integer", "float", "double", "float8", "decimal", + "int8", "uint8", "int16", "uint16", "int32", "uint32", + "int64", "uint64", "int128", "uint128"): if "minimum" in schema and instance < schema["minimum"]: errors.append(f"Number less than minimum {schema['minimum']}") if "maximum" in schema and instance > schema["maximum"]: @@ -675,7 +720,9 @@ def _validate_validation_addins(self, schema, instance, path): [Metaschema: JSON Structure JSONStructureValidation] """ # Numeric constraints. - if schema.get("type") in ("number", "integer", "float", "double", "decimal", "int32", "uint32", "int64", "uint64", "int128", "uint128"): + if schema.get("type") in ("number", "integer", "float", "double", "decimal", + "int8", "uint8", "int16", "uint16", "int32", "uint32", + "int64", "uint64", "int128", "uint128", "float8"): if "minimum" in schema: try: if instance < schema["minimum"]: diff --git a/samples/py/json_structure_schema_validator.py b/samples/py/json_structure_schema_validator.py index c944f4f..fb68494 100644 --- a/samples/py/json_structure_schema_validator.py +++ b/samples/py/json_structure_schema_validator.py @@ -42,12 +42,12 @@ class JSONStructureSchemaCoreValidator: "values", "choices", "selector", "tuple" } PRIMITIVE_TYPES = { - "string", "number", "boolean", "null", "int8", "uint8", "int16", "uint16", + "string", "number", "integer", "boolean", "null", "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "int128", "uint128", "float8", "float", "double", "decimal", "date", "datetime", "time", "duration", - "uuid", "uri", "binary", "jsonpointer", "any" + "uuid", "uri", "binary", "jsonpointer" } - COMPOUND_TYPES = {"object", "array", "set", "map", "tuple", "choice"} + COMPOUND_TYPES = {"object", "array", "set", "map", "tuple", "choice", "any"} # Extended keywords for conditional composition COMPOSITION_KEYWORDS = {"allOf", "anyOf", "oneOf", "not", "if", "then", "else"} From 78f77b217976d70dfc99658d5b3640a5d47b7b6b Mon Sep 17 00:00:00 2001 From: Clemens Vasters Date: Thu, 27 Nov 2025 08:47:12 +0100 Subject: [PATCH 3/4] test: add comprehensive type coverage tests for all JSON Structure types Added tests for previously missing types: - int8, uint8, int16, uint16, int128, uint128 (extended integer types) - float8, double (floating point types) - integer (alias for int32) - duration (ISO 8601 duration strings) - any (accepts all value types) Total tests increased from 108 to 141. --- .../test_json_structure_instance_validator.py | 284 ++++++++++++++++++ 1 file changed, 284 insertions(+) diff --git a/samples/py/test_json_structure_instance_validator.py b/samples/py/test_json_structure_instance_validator.py index 0659477..9832f8f 100644 --- a/samples/py/test_json_structure_instance_validator.py +++ b/samples/py/test_json_structure_instance_validator.py @@ -143,11 +143,146 @@ def test_null_invalid(): errors = validator.validate_instance(0) assert any("Expected null" in err for err in errors) + +def test_any_accepts_string(): + """Test any type accepts string values""" + schema = {"type": "any", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "anySchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("hello") + assert errors == [] + + +def test_any_accepts_number(): + """Test any type accepts numeric values""" + schema = {"type": "any", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "anySchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(42.5) + assert errors == [] + + +def test_any_accepts_object(): + """Test any type accepts object values""" + schema = {"type": "any", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "anySchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance({"key": "value"}) + assert errors == [] + + +def test_any_accepts_array(): + """Test any type accepts array values""" + schema = {"type": "any", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "anySchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance([1, 2, 3]) + assert errors == [] + + +def test_any_accepts_null(): + """Test any type accepts null values""" + schema = {"type": "any", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "anySchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(None) + assert errors == [] + + +def test_any_accepts_boolean(): + """Test any type accepts boolean values""" + schema = {"type": "any", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "anySchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(True) + assert errors == [] + + # ------------------------------------------------------------------- # Integer and Floating Point Tests (Numeric JSONStructureValidation Addins) # ------------------------------------------------------------------- +def test_int8_valid(): + schema = {"type": "int8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "int8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(127) + assert errors == [] + + +def test_int8_out_of_range(): + schema = {"type": "int8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "int8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(128) + assert any("out of range" in err for err in errors) + + +def test_int8_negative_valid(): + schema = {"type": "int8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "int8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(-128) + assert errors == [] + + +def test_uint8_valid(): + schema = {"type": "uint8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "uint8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(255) + assert errors == [] + + +def test_uint8_out_of_range(): + schema = {"type": "uint8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "uint8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(256) + assert any("out of range" in err for err in errors) + + +def test_uint8_negative(): + schema = {"type": "uint8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "uint8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(-1) + assert any("out of range" in err for err in errors) + + +def test_int16_valid(): + schema = {"type": "int16", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "int16Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(32767) + assert errors == [] + + +def test_int16_out_of_range(): + schema = {"type": "int16", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "int16Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(32768) + assert any("out of range" in err for err in errors) + + +def test_uint16_valid(): + schema = {"type": "uint16", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "uint16Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(65535) + assert errors == [] + + +def test_uint16_out_of_range(): + schema = {"type": "uint16", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "uint16Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(65536) + assert any("out of range" in err for err in errors) + + def test_int32_valid(): schema = {"type": "int32", "$schema": "https://json-structure.org/meta/core/v0/#", "$id": "dummy", "name": "int32Schema"} @@ -212,6 +347,105 @@ def test_uint64_invalid_format(): assert any("Expected uint64 as string" in err for err in errors) +def test_int128_valid(): + schema = {"type": "int128", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "int128Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("170141183460469231731687303715884105727") + assert errors == [] + + +def test_int128_out_of_range(): + schema = {"type": "int128", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "int128Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("170141183460469231731687303715884105728") # 2^127 + assert any("out of range" in err for err in errors) + + +def test_int128_invalid_format(): + schema = {"type": "int128", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "int128Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(12345) # Should be string + assert any("Expected int128 as string" in err for err in errors) + + +def test_uint128_valid(): + schema = {"type": "uint128", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "uint128Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("340282366920938463463374607431768211455") # 2^128 - 1 + assert errors == [] + + +def test_uint128_out_of_range(): + schema = {"type": "uint128", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "uint128Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("340282366920938463463374607431768211456") # 2^128 + assert any("out of range" in err for err in errors) + + +def test_uint128_negative(): + schema = {"type": "uint128", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "uint128Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("-1") + assert any("out of range" in err for err in errors) + + +def test_float8_valid(): + schema = {"type": "float8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "float8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(0.5) + assert errors == [] + + +def test_float8_integer_accepted(): + schema = {"type": "float8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "float8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(1) # int is accepted for float8 + assert errors == [] + + +def test_float8_invalid(): + schema = {"type": "float8", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "float8Schema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("0.5") # Should be number, not string + assert any("Expected float8" in err for err in errors) + + +def test_integer_valid(): + """Test integer type (alias for int32)""" + schema = {"type": "integer", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "integerSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(123) + assert errors == [] + + +def test_integer_out_of_range(): + """Test integer type (alias for int32) range validation""" + schema = {"type": "integer", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "integerSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(2**31) + assert any("out of range" in err for err in errors) + + +def test_integer_invalid_type(): + """Test integer type rejects non-integers""" + schema = {"type": "integer", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "integerSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("123") + assert any("Expected integer" in err for err in errors) + + def test_float_valid(): schema = {"type": "float", "$schema": "https://json-structure.org/meta/core/v0/#", "$id": "dummy", "name": "floatSchema"} @@ -228,6 +462,39 @@ def test_float_invalid(): assert any("Expected float" in err for err in errors) +def test_double_valid(): + schema = {"type": "double", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "doubleSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(3.141592653589793) + assert errors == [] + + +def test_double_integer_accepted(): + """Test that integers are accepted for double type""" + schema = {"type": "double", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "doubleSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(42) + assert errors == [] + + +def test_double_invalid(): + schema = {"type": "double", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "doubleSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("3.14") + assert any("Expected double" in err for err in errors) + + +def test_float_invalid(): + schema = {"type": "float", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "floatSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("1.23") + assert any("Expected float" in err for err in errors) + + def test_decimal_valid(): schema = {"type": "decimal", "$schema": "https://json-structure.org/meta/core/v0/#", "$id": "dummy", "name": "decimalSchema"} @@ -358,6 +625,23 @@ def test_time_invalid(): errors = validator.validate_instance("123456") assert any("Expected time" in err for err in errors) + +def test_duration_valid(): + schema = {"type": "duration", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "durationSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance("P1Y2M3DT4H5M6S") # ISO 8601 duration + assert errors == [] + + +def test_duration_invalid(): + schema = {"type": "duration", "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "dummy", "name": "durationSchema"} + validator = JSONStructureInstanceValidator(schema) + errors = validator.validate_instance(3600) # Should be string, not number + assert any("Expected duration" in err for err in errors) + + # ------------------------------------------------------------------- # UUID and URI Tests # ------------------------------------------------------------------- From 950180dd2e5397e2bb80dbdb64c7b55c4ec7e11f Mon Sep 17 00:00:00 2001 From: Clemens Vasters Date: Thu, 27 Nov 2025 09:16:54 +0100 Subject: [PATCH 4/4] fix: add maxLength validation and comprehensive type coverage tests for schema validator - Added missing maxLength to STRING_VALIDATION_KEYWORDS - Added maxLength validation in _check_string_validation method - Added tests for all 27 primitive types via test_all_primitive_types_valid - Added tests for all 7 compound types via test_compound_types_recognized - Added tests for numeric validation keywords on all numeric types - Added tests for string-based numeric types (int64, uint64, int128, uint128, decimal) - Added maxLength validation tests (valid, negative, not-integer) - Added unknown type rejection test - Total schema validator tests increased from 68 to 122 --- samples/py/json_structure_schema_validator.py | 7 +- .../test_json_structure_schema_validator.py | 180 ++++++++++++++++++ 2 files changed, 186 insertions(+), 1 deletion(-) diff --git a/samples/py/json_structure_schema_validator.py b/samples/py/json_structure_schema_validator.py index fb68494..55c3c04 100644 --- a/samples/py/json_structure_schema_validator.py +++ b/samples/py/json_structure_schema_validator.py @@ -54,7 +54,7 @@ class JSONStructureSchemaCoreValidator: # Extended keywords for validation NUMERIC_VALIDATION_KEYWORDS = {"minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"} - STRING_VALIDATION_KEYWORDS = {"minLength", "pattern", "format"} + STRING_VALIDATION_KEYWORDS = {"minLength", "maxLength", "pattern", "format"} ARRAY_VALIDATION_KEYWORDS = {"minItems", "maxItems", "uniqueItems", "contains", "minContains", "maxContains"} OBJECT_VALIDATION_KEYWORDS = {"minProperties", "maxProperties", "minEntries", "maxEntries", "dependentRequired", "patternProperties", "patternKeys", @@ -545,6 +545,11 @@ def _check_string_validation(self, obj, path): val = obj["minLength"] if not isinstance(val, int) or val < 0: self._err("'minLength' must be a non-negative integer.", f"{path}/minLength") + + if "maxLength" in obj: + val = obj["maxLength"] + if not isinstance(val, int) or val < 0: + self._err("'maxLength' must be a non-negative integer.", f"{path}/maxLength") if "pattern" in obj: val = obj["pattern"] diff --git a/samples/py/test_json_structure_schema_validator.py b/samples/py/test_json_structure_schema_validator.py index 39060c7..6667c01 100644 --- a/samples/py/test_json_structure_schema_validator.py +++ b/samples/py/test_json_structure_schema_validator.py @@ -873,3 +873,183 @@ def test_validation_metaschema_enables_extensions(): source_text = json.dumps(schema) errors = validate_json_structure_schema_core(schema, source_text, extended=True) assert errors == [] + + +# ============================================================================= +# Tests for all primitive types +# ============================================================================= + +# Test all primitive types are recognized +@pytest.mark.parametrize("primitive_type", [ + "string", "number", "integer", "boolean", "null", + "int8", "uint8", "int16", "uint16", "int32", "uint32", + "int64", "uint64", "int128", "uint128", + "float8", "float", "double", "decimal", + "date", "datetime", "time", "duration", + "uuid", "uri", "binary", "jsonpointer" +]) +def test_all_primitive_types_valid(primitive_type): + """Test that all primitive types are recognized as valid.""" + schema = { + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": f"https://example.com/schema/{primitive_type}_test", + "name": f"{primitive_type.capitalize()}Schema", + "type": primitive_type + } + source_text = json.dumps(schema) + errors = validate_json_structure_schema_core(schema, source_text) + assert errors == [], f"Type '{primitive_type}' should be valid but got errors: {errors}" + + +# Test that unknown types are rejected +def test_unknown_type_rejected(): + schema = { + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": "https://example.com/schema/unknown_type", + "name": "UnknownTypeSchema", + "type": "unknowntype" + } + source_text = json.dumps(schema) + errors = validate_json_structure_schema_core(schema, source_text) + assert any("not a recognized" in err for err in errors) + + +# Test maxLength validation +def test_maxlength_valid(): + schema = { + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": "https://example.com/schema/maxlength_valid", + "name": "MaxLengthSchema", + "$uses": ["JSONStructureValidation"], + "type": "string", + "maxLength": 100 + } + source_text = json.dumps(schema) + errors = validate_json_structure_schema_core(schema, source_text, extended=True) + assert errors == [] + + +def test_maxlength_negative(): + schema = { + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": "https://example.com/schema/maxlength_negative", + "name": "MaxLengthNegativeSchema", + "$uses": ["JSONStructureValidation"], + "type": "string", + "maxLength": -1 + } + source_text = json.dumps(schema) + errors = validate_json_structure_schema_core(schema, source_text, extended=True) + assert any("maxLength" in err for err in errors) + + +def test_maxlength_not_integer(): + schema = { + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": "https://example.com/schema/maxlength_not_int", + "name": "MaxLengthNotIntSchema", + "$uses": ["JSONStructureValidation"], + "type": "string", + "maxLength": "100" + } + source_text = json.dumps(schema) + errors = validate_json_structure_schema_core(schema, source_text, extended=True) + assert any("maxLength" in err for err in errors) + + +# Test compound types are valid +@pytest.mark.parametrize("compound_type", ["object", "array", "set", "map", "tuple", "choice", "any"]) +def test_compound_types_recognized(compound_type): + """Test that compound types are recognized.""" + # Build appropriate schema for each compound type + if compound_type == "object": + schema = { + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": f"https://example.com/schema/{compound_type}_test", + "name": "ObjectSchema", + "type": "object", + "properties": {"prop": {"type": "string"}} + } + elif compound_type in ["array", "set"]: + schema = { + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": f"https://example.com/schema/{compound_type}_test", + "name": f"{compound_type.capitalize()}Schema", + "type": compound_type, + "items": {"type": "string"} + } + elif compound_type == "map": + schema = { + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": f"https://example.com/schema/{compound_type}_test", + "name": "MapSchema", + "type": "map", + "values": {"type": "string"} + } + elif compound_type == "tuple": + schema = { + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": f"https://example.com/schema/{compound_type}_test", + "name": "TupleSchema", + "type": "tuple", + "properties": {"a": {"type": "string"}, "b": {"type": "int32"}}, + "tuple": ["a", "b"] + } + elif compound_type == "choice": + schema = { + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": f"https://example.com/schema/{compound_type}_test", + "name": "ChoiceSchema", + "type": "choice", + "choices": {"opt1": {"type": "string"}, "opt2": {"type": "int32"}} + } + else: # any + schema = { + "$schema": "https://json-structure.org/meta/core/v0/#", + "$id": f"https://example.com/schema/{compound_type}_test", + "name": "AnySchema", + "type": "any" + } + + source_text = json.dumps(schema) + errors = validate_json_structure_schema_core(schema, source_text) + assert errors == [], f"Compound type '{compound_type}' should be valid but got errors: {errors}" + + +# Test validation keywords on all numeric types +@pytest.mark.parametrize("numeric_type", [ + "number", "integer", "float", "double", "float8", + "int8", "uint8", "int16", "uint16", "int32", "uint32" +]) +def test_numeric_validation_keywords(numeric_type): + """Test that numeric validation keywords work on numeric types.""" + schema = { + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": f"https://example.com/schema/{numeric_type}_validation", + "name": f"{numeric_type.capitalize()}ValidationSchema", + "$uses": ["JSONStructureValidation"], + "type": numeric_type, + "minimum": 0, + "maximum": 100 + } + source_text = json.dumps(schema) + errors = validate_json_structure_schema_core(schema, source_text, extended=True) + assert errors == [], f"Numeric validation on '{numeric_type}' should be valid but got errors: {errors}" + + +# Test validation keywords on string-based numeric types (int64, uint64, int128, uint128, decimal) +@pytest.mark.parametrize("string_numeric_type", ["int64", "uint64", "int128", "uint128", "decimal"]) +def test_string_numeric_validation_keywords(string_numeric_type): + """Test that string-based numeric types require string values for validation keywords.""" + schema = { + "$schema": "https://json-structure.org/meta/extended/v0/#", + "$id": f"https://example.com/schema/{string_numeric_type}_validation", + "name": f"{string_numeric_type.capitalize()}ValidationSchema", + "$uses": ["JSONStructureValidation"], + "type": string_numeric_type, + "minimum": "0", + "maximum": "1000000000000" + } + source_text = json.dumps(schema) + errors = validate_json_structure_schema_core(schema, source_text, extended=True) + assert errors == [], f"String-based numeric validation on '{string_numeric_type}' should be valid but got errors: {errors}"