Version: 1.2.0
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.
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) |
Example:
"email|@ {5,100} ~$Email~|User email": "alice@example.com"↳ 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) |
% |
All | Default value (informational only) |
{max} |
String | Max length |
{min,max} |
String | Length range |
(min..max) |
Number | Inclusive range |
(>n) (<n) (>=n)
(<=n) |
Number | Comparison |
('a','b','c') |
All | Enum values |
($REF) |
All | Nomenclature reference |
~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 (by # keys for objects) |
[*:max] |
Map | Any key, max entries |
[~pattern~:max] |
Map | Key pattern, max 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)
| Directive | Value | Effect |
|---|---|---|
$requiredIf field(cond) |
[fields] |
Listed fields required when condition on field is true |
$requiredIfNot field(cond) |
[fields] |
Listed fields required when condition on field is false |
$requiredIfExist field |
[fields] |
Listed fields required when field exists |
$requiredIfNotExist field |
[fields] |
Listed fields required when field is absent |
$forbiddenIf field(cond) |
[fields] |
Listed fields forbidden when condition on field is true |
$forbiddenIfNot field(cond) |
[fields] |
Listed fields forbidden when condition on field is false |
$forbiddenIfExist field |
[fields] |
Listed fields forbidden when field exists |
$forbiddenIfNotExist field |
[fields] |
Listed fields forbidden when field is absent |
$appliedIf field(cond) |
{...} |
Structure applied when condition on field is true |
$appliedIfExist field |
{...} |
Structure applied when field exists |
$appliedIfNotExist field |
{...} |
Structure applied when field is absent |
Condition syntax: field(values) or
field(min..max) or field(_TypeGuard_)
Examples:
"$requiredIf age(<18)": ["parentConsent"]↳ parentConsent required when age is less
than 18
"$forbiddenIf status('CLOSED')": ["lastLogin"]↳ lastLogin forbidden when status equals
“CLOSED”
"$requiredIfExist shipping": ["shippingAddress"]↳ shippingAddress required when shipping
field exists
"$appliedIf status('ACTIVE')": {
"workDays|@ (1..22)": 20
}↳ workDays field added to schema when
status equals “ACTIVE”
Switch form:
"$appliedIf paymentMethod": {
"('CARD')": { "cardNumber|@ {16}": "1234..." },
"('PAYPAL')": { "email|@ ~$Email~": "a@b.c" },
"$else": { "reference|@": "REF-001" }
}↳ Different fields required depending on paymentMethod
value
Conditions can reference fields in parent or root objects:
| Prefix | Resolves from |
|---|---|
| (none) | Current object |
this. |
Current object (explicit) |
parent. |
Parent object |
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~
| 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 |
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 |
Example:
"$appliedIf data": {
"(_String_)":{"data|{5,50}": "Hello"},
"(_Integer_)":{"data|(1..200)": 100}
}↳ data validated as string (5-50 chars) or integer
(1-200) depending on its actual type
For fields that accept different structures. For conditional
variants, prefer $appliedIf (§6). 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|@ $oneOf": [
{ "type|@ ('card')": "card", "number|@": "1234..." },
{ "type|@ ('paypal')": "paypal", "email|@": "a@b.c" }
]↳ payment expects ONE object matching either card or
paypal variant (not an array)
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:
{
"$okylineVersion": "1.2.0",
"$version": "1.0.0",
"$title": "My Schema",
"$description": "Schema description",
"$id": "my.schema",
"$additionalProperties": false,
"$nomenclature": { ... },
"$format": { ... },
"$oky": { ... }
}| Key | Required | Purpose |
|---|---|---|
$oky |
Yes | Schema definition |
$okylineVersion |
No | Spec version ("1.2.0") |
$version |
No | Schema version |
$title |
No | Schema title |
$description |
No | Schema description |
$id |
No | Schema identifier |
$additionalProperties |
No | Allow unknown fields (default: false) |
$nomenclature |
No | Reusable value lists |
$format |
No | Reusable regex patterns |
See Annexes for: $defs (D),
$compute (C), $import (E)
Okyline® is a registered trademark of Akwatype.