Version: 1.8.0 Date: September 2026 Status: Draft
Okyline is a JSON-based schema language for validating JSON documents by example. You write a JSON document with real example values, then add constraints directly in field names.
Okyline is a declarative language designed to describe the structure and constraints of JSON documents in a lightweight and readable manner. It enriches JSON examples with inline constraints, enabling data validation while keeping schemas human-friendly.
Okyline was designed from the outset to support structural validation, conditional logic, and computed business invariants within JSON document definitions.
For conditional rules ($requiredIf, $forbiddenIf, $appliedIf, …), see the companion Okyline Core Quick Reference - Conditional Directives.
A minimal Okyline schema:
{
"$oky": {
"name|@": "Alice",
"age|(18..120)": 30
}
}↳ Two fields: name required string, age integer between 18 and 120
" fieldName | constraints | label ": exampleValue
| Part | Required | Description |
|---|---|---|
fieldName |
Yes | JSON attribute name |
constraints |
No | Space-separated constraint symbols |
label |
No | Human-readable description (no \| allowed) |
A key starting with $ is a directive, unless the first non-blank character after the initial word is a |: "$oid|@" is a field named $oid.
Example:
"email|@ {5,100} ~$Email~|User email": "[email protected]"↳ Required string, 5-100 chars, must match email format, labeled “User email”
The example value determines the expected type:
| Example value | Inferred type |
|---|---|
"text" |
String |
42 |
Integer |
3.14 |
Number |
"78.00" |
Number (auto-convert) |
true / false |
Boolean |
[1, 2] |
Array[Integer] |
{"k": "v"} |
Object |
Note: Since JSON serialization doesn’t guarantee trailing zeros are preserved, you can express decimal numbers as strings (e.g., "78.00"). These are auto-converted to Number. Use $str to force string type if needed.
Modifiers: - $str - force string type (disable decimal auto-conversion) - $obj - treat array example as multiple examples of a single-value field
| Symbol | Applies to | Meaning |
|---|---|---|
@ |
All | Required |
? |
All | Nullable |
# |
Scalars | Key field (for uniqueness in lists) |
{max} |
String | Max length |
{min,max} |
String | Length range |
{min,*} |
String | Min only (no max) |
{*} |
String | Any length |
(min..max) |
Number | Inclusive range |
(>n) (<n) (>=n) (<=n) |
Number | Comparison |
('a','b','c') |
String, Number | Enum values |
(1,2..5,>10) |
Number | Combined alternatives (OR); ('A'..'Z') for a string range |
~pattern~ |
String | Inline regex |
~$Name~ |
String | Named format |
Example combining constraints:
"price|@ ? (0.01..9999.99)": 19.99↳ Required, nullable, decimal between 0.01 and 9999.99
| Syntax | Applies to | Meaning |
|---|---|---|
[max] |
List | Max size |
[min,max] |
List | Size range |
[min,*] |
List | Min only (no max) |
[*] |
List | Any size |
-> constraints |
List/Map | Apply constraints to each element |
! |
List | Uniqueness; a list of objects needs a # key field |
[*:max] |
Map | Any key, max entries |
[*:*] |
Map | Any key, any entries |
[~pattern~:max] |
Map | Key pattern, max entries |
[~pattern~:*] |
Map | Key pattern, any entries |
Examples:
"tags|[1,5] -> {2,20}!": ["eco", "bio"]↳ List of 1-5 unique strings, each 2-20 characters
"scores|[15] -> (0..100)": [85, 92]↳ List of maximum 15 elements, each element between 0 and 100
"translations|[~^[a-z]{2}$~:10] -> {1,100}": { "en": "Hello", "fr": "Bonjour" }↳ Map with 2-letter keys, max 10 entries, values 1-100 characters
"users|[10] -> !": [{ "id|#@": 1, "name|@": "Alice" }]↳ List of max 10 unique objects - uniqueness determined by # key field (id)
An empty example ([], { }) leaves the elements untyped: only the size and the key pattern apply; -> constraints and ! are rejected.
Conditions and references can target fields in parent or root objects:
| Prefix | Resolves from |
|---|---|
| (none) | Current object |
this. |
Current object (explicit) |
parent. |
Parent of the current context |
root. |
Document root |
Note: parent skips over arrays - it always refers to the nearest parent object, not the array containing the current item.
Example:
"$requiredIf parent.status('ACTIVE')": ["code"]↳ In a nested object: code required when the parent’s status equals “ACTIVE”
Nomenclature - reusable value lists:
"$nomenclature": { "STATUS": "ACTIVE,INACTIVE,PENDING" }Usage: ($STATUS)
Format - reusable regex patterns:
"$format": { "Phone": "^\\+[0-9]{10,15}$" }Usage: ~$Phone~
A key may carry a label after |: "STATUS | Account status": "ACTIVE,INACTIVE".
| Format | Description |
|---|---|
$Date |
ISO 8601 date (semantic validation) |
$DateTime |
ISO 8601 datetime (semantic validation) |
$Time |
RFC 3339 time |
$Email |
Email address |
$Uri |
URI with scheme |
$Uuid |
UUID v1-v5 |
$Ipv4 |
IPv4 address |
$Ipv6 |
IPv6 address |
$Hostname |
DNS hostname |
A $format declared with the same name as a built-in replaces it.
Test the runtime type of a field in conditions. Useful when a field can have different types and you need different validation rules for each.
| Guard | Matches |
|---|---|
_String_ |
string |
_Integer_ |
integer (no decimals) |
_Number_ |
number (int or decimal) |
_Boolean_ |
boolean |
_Null_ |
null |
_Object_ |
object |
_EmptyList_, _ListOfString_, _ListOfInteger_, _ListOfNumber_, _ListOfBoolean_, _ListOfObject_, _ListOfNull_ |
array by element type |
Example:
"data": "example",
"$appliedIf data(_String_)": {
"length": 7,
"$else": { "type": "non-string" }
}↳ length is added when data is a string, type otherwise
For fields that accept different structures. For conditional variants, prefer $appliedIf (see Conditional Directives Quick Reference). Use $oneOf/$anyOf when variants are unrelated to other field values.
| Modifier | Meaning |
|---|---|
$oneOf |
Value must match exactly one variant |
$anyOf |
Value must match at least one variant (default) |
Example:
"payment|@ $obj $oneOf": [
{ "type|@ ('card')": "card", "number|@": "1234..." },
{ "type|@ ('paypal')": "paypal", "email|@": "[email protected]" }
]↳ payment expects ONE object matching either card or paypal variant; $obj makes it a single value, without it the field is a list
Prefix attribute name with // to ignore it and its entire subtree:
"//user|@": { "id": 1, "name": "ignored" }↳ The entire user object and its children are ignored by the parser
Complete schema with all optional metadata:
{
"$oky": { ... },
"$id": "my.schema",
"$version": "1.0.0",
"$state": "DRAFT",
"$title": "My Schema",
"$description": "Schema description",
"$additionalProperties": false,
"$sequence": false,
"$nullAsAbsentIfUndeclared": false,
"$decimalScale": 6,
"$nomenclature": { ... },
"$format": { ... },
"$okylineVersion": "x.y.z"
}| Key | Required | Purpose |
|---|---|---|
$oky |
Yes | Schema definition (null: no root document, definitions only) |
$id |
No | Schema identifier |
$version |
No | Schema version |
$state |
No | Lifecycle state: "DRAFT" (default), "DRAFT-FINAL" or "FINAL" |
$title |
No | Schema title |
$description |
No | Schema description |
$additionalProperties |
No | Allow unknown fields (default: false). Local overrides global for that object only, child objects are not affected. |
$sequence |
No | Enforce field order in validated objects (default: false). Local overrides global for that object only, child objects are not affected. |
$nullAsAbsentIfUndeclared |
No | Treat null on non-? fields as absent (default: false) |
$decimalScale |
No | Number of fractional digits (scale) for numeric comparisons, ranges and computes (default 6) |
$nomenclature |
No | Reusable value lists |
$format |
No | Reusable regex patterns |
$okylineVersion |
No | Spec version (indicative only) |
Root keys are closed: any unknown $-key at the root fails loading.
See the per-annex Quick References for: $compute (C), $defs (D), external imports & visibility (E), $field (F).
Okyline® is a registered trademark of Akwatype.