Internet Object Schema
Internet Object Schema Specification
Internet Object schemas define the structure (“shape”) of objects in IO documents. Unlike verbose, map-based standards, IO schemas use the same concise object syntax as actual data, making them both human-friendly and machine-tractable.
Philosophy and Motivation
Internet Object schemas are designed for clarity, expressiveness, and minimalism. They avoid the verbosity of traditional schema languages (like JSON Schema or XML Schema) by using the same syntax for both data and schema. This makes it easy for humans to author, read, and maintain schemas, while keeping them fully machine-tractable for validation, tooling, and interoperation with other formats.
Schemas describe:
Field names (and order, if needed)
Types and constraints
Nesting and composition
Optional and dynamic fields (by convention)
Schema Structure and Syntax
Schema as Object Shape
A schema is written using the Internet Object object syntax:
Fields are comma-separated:
name, age, address
Each field can be:
Just a name (defaults to “any” type)
Typed (
name: string
)Nested (
address: { street: string, city: string }
)Constrained (
score: {int, min: 0, max: 100}
)
Fields may be marked as optional or dynamic using conventions (see “Semantic Field Modifiers”).
Examples:
# Minimal schema (all fields are "any" type)
name, age, address
# Typed schema
name: string, age: int, isActive: bool
# Nested schema
address: { street: string, city: string }
# Typed with constraints (MemberDef)
name: {string, maxLen: 100}, age: {int, min: 0, max: 120}
Open and Closed Schema Objects
Top-level schemas may use the open object form (no braces):
name, age, address
Nested objects (schemas for nested fields) must use
{ ... }
:address: { street: string, city: string }
Keyed and Positional Fields
Keyed fields: Schema and data map fields by name (
name: value
).Unkeyed (positional) fields: Supported for compact, CSV-like data. Recommendation: Use positional mapping only when all fields are required and unambiguous.
Mixed Mode
Unkeyed fields can appear before any keyed fields.
Once a keyed field appears, all remaining fields must be keyed.
Nesting and Reuse
Nested objects: Use
{ ... }
for fields whose value is itself an object.Reusable schemas: Named with
$
in the schema header; referenced as$name
.
Example:
~ $address: {street: string, city: string}
~ $user: {name: string, age: int, address: $address}
Syntax Summary Table
Field
name
Unkeyed field, type is any
Typed Field
name: string
Keyed field, explicit type
Constraint
age: {int, min: 0, max: 120}
With constraints
Optional
remark?
Field may be omitted
Nullable
address*
Field may be null
Dynamic
*, *: string
Allow extra fields
Nested
address: { street: string, city: string }
Nested object
Reusable
$address
Reference to a named schema
Schema Grammar (EBNF)
schema = objectEntries
objectEntries = memberDef *( "," memberDef )
memberDef = [key] [fieldModifier] [":" typeOrDef]
key = string
fieldModifier = "?" | "*" | "?*"
typeOrDef = type | memberDef | ref
type = "string" | "int" | "bool" | "object" | "array" | ...
ref = "$" name
Note: Modifiers and complex memberDefs are conventions, not core grammar.
Field Types and Constraints
Built-in Types
Internet Object supports the following built-in types:
string
,int
,bool
,float
,number
,object
,array
, and domain-specific types (date
,datetime
, etc.)Types may be extended or customized in a future version by user-defined type systems.
Constraints Reference
min / max / minLen / maxLen: For numbers, strings, arrays.
choices: For enums. Example:
{string, choices: [A, B, C]}
pattern: For regex constraints on strings. Example:
{string, pattern: "^[a-z]+$"}
default: Assigns a default value if missing.
Semantic Field Modifiers (Conventions)
Internet Object schemas use the following conventions (not syntax) for special field semantics:
Optional: Suffix
?
on field name (e.g.,age?
). Means the field may be omitted in data.Nullable: Suffix
*
(e.g.,remark*
). Means the field can benull
.Dynamic/extra fields: Use
*
at end (e.g.,name, age, *
or*: string
).These are interpreted by schema tooling, not by the object parser itself.
Optional and Nullable Field Semantics
Optional (
?
): Field can be omitted from the data object. If omitted, its value is undefined unless a default is provided.Nullable (
*
): Field can explicitly be set tonull
.Both (
?*
): Field can be omitted or set tonull
.
Examples:
email?: string # May be omitted
nickname*: string # May be null
bio?*: string # May be omitted or null
Dynamic/Extra Fields
*
at the end of a schema allows extra fields not specified in the schema.*: type
constrains the type of all extra fields.
Example:
name: string, *, # Allow any extra fields
*: int # All extra fields must be int
Recommendations on Modifiers
For strict validation and best interoperability, avoid
*
unless required.For positional schemas, avoid optionals except at the end.
Mapping to Industry Standards (for Interoperability)
Keyed schemas map directly to “properties” in JSON Schema, Avro, etc.
Optionals (
?
) are omitted from"required"
arrays.Dynamic fields (
*
) map toadditionalProperties
.Constraints map to field-level attributes in target schema (e.g., minLength, enum).
Canonicalization for Tooling
Recommendation: For robust tooling and validation, always canonicalize Internet Object schemas to a fully-keyed, explicit, and type-complete form internally. This enables safe mapping to and from JSON Schema, Avro, or other industry formats.
Mapping Table: IO Schema → JSON Schema
foo: string
{ "foo": { "type": "string" } }
age?: int
{ "age": { "type": "integer" } }, "required": []
*, *: string
additionalProperties: true
or { "type": "string" }
{ foo: {string, minLen:2} }
{ "foo": { "type": "string", "minLength": 2 } }
JSON Compatibility
A subset of Internet Object schemas and data are directly compatible with JSON and JSON Schema.
For full compatibility, use quoted keys and JSON-legal values.
Best Practices
Prefer explicit types for all fields in production schemas.
Use fully-keyed schemas for anything beyond trivial/CSV-like records.
Use optionals only at the end if using positional mapping.
Document and canonicalize mixed or dynamic schemas for robust tooling.
Common Schema Patterns
Flat (CSV-like):
name, age, score
Typed object:
name: string, age: int, score: float
Nested:
user: {name: string, address: {city: string}}
Optional/nullable:
comment?: string, timestamp*: datetime
Dynamic:
*, *: string
Open Object and Array Forms
Internet Object allows you to define fields that can accept any object or any array using open forms:
Any Object: {}
{}
Use
{}
as a schema for a field that may contain any object, regardless of fields or structure.This matches objects of any shape, including empty objects.
meta: {} # 'meta' can be any object, equivalent to `meta: object`
payload?: {} # 'payload' is optional, any object allowed
data: object # 'data' can also be written as `data: {}` for any object
Any Array: []
[]
extras: [] # 'extras' can be any array. Same as `extras: array`
tags?: [] # 'tags' is optional, any array allowed
choices: array # 'Can also be written as `choices: []` for any array'
Use
[]
as a schema for a field that may contain any array, regardless of element type or length.This matches all arrays, including empty arrays.
extras: [] # 'extras' can be any array
tags?: [] # 'tags' is optional, any array allowed
Why Use Open Forms?
Useful for fields where you expect unstructured, arbitrary data (e.g., “metadata,” “extension,” “blob,” or raw API fields).
No validation is performed on object keys or array elements—only the container type is enforced.
Contrast with Typed Forms
To restrict the allowed content, use typed or constrained schemas:
[int]
for an array of integers{ name: string }
for an object with required fields[ { name: string } ]
for an array of objects with shape
{}
Any object (no structure required)
[]
Any array (no type/length required)
[type]
Array of the specified type
[MemberDef]
Array validated by MemberDef
[{...}]
Array of objects with defined shape
Note: These open forms can also be used in MemberDefs for fields that may contain arbitrary objects or arrays.
**Summary:**
- Put this new section right after “Common Schema Patterns” and before your “Full Example.”
- This order introduces specific patterns, then the open (most general) forms, then illustrates usage in a complete example.
## **Full Example**
### **Complete Schema Example with Comments**
```ruby
# User schema
name: string, # Required
age?: int, # Optional
email: {string, pattern:"^[^@]+@[^@]+$"}, # Required, pattern constraint
isActive: bool, # Required
address?: { # Optional nested object
street: string,
city: string,
zip?: int # Optional zip code
},
*: string # Allow extra string fields
Valid Data:
{
name: John Doe,
isActive: T,
address: {
street: Bond Street,
city: New York
},
nickname: Johnny
}
Appendix: Object Syntax Reference
(Refer to your object.md
for formal object syntax and EBNF.)
Object Syntax EBNF (from Object Spec)
See [object.md] for formal definition; include diagrams or syntax trees as appendix if desired.
FAQ & Clarifications
*
and?
are schema conventions—they do not change object syntax.All schema fields are mapped to data fields using either position (unkeyed) or name (keyed).
For compatibility, always provide a canonical, fully-keyed, fully-typed version of the schema for external tooling.
Can I mix positional and keyed fields? Yes, but only unkeyed fields before any keys. Once a key is present, all subsequent fields must be keyed.
What happens if a required field is missing? Validation fails unless the field is optional (
?
) or has a default.Are keys case-sensitive? Yes.
"Name"
and"name"
are distinct.How are unknown fields handled? If
*
is present in the schema, unknown fields are accepted (and optionally typed); otherwise, they are rejected by validators.
Versioning and Evolution (Future Section)
Schema evolution, migration, backward compatibility best practices can be addressed in future versions.
Last updated
Was this helpful?