Code Generation
For each object definition Nomos emits a case class, and for each discriminator a sealed trait plus variant case classes — each with a companion for parse/serialize/validate, plus a shared NomosFormats carrying the embedded template.
What gets generated
From a template at nomos/templates/com/example/models/user.json:
| File | Contents |
|---|---|
com/example/models/user/User.scala | The User case class + companion |
com/example/models/NomosFormats.scala | Embedded template and validator |
…/<Enum>.scala | One file per generated enum type |
The case class & companion
case class User(id: String, username: String, email: String, age: Option[Int], roles: List[String])
object User {
import com.example.models.NomosFormats._
import dev.cjfravel.nomos.json._
import dev.cjfravel.nomos.serialization.Codecs
import dev.cjfravel.nomos.validation.ValidationError
def decode(json: JsonValue): Either[String, User] = { ... } // built from Codecs combinators
def encode(obj: User): JsonValue = { ... }
def fromJson(json: String): Either[String, User] = Json.parse(json).right.flatMap(decode)
def toJson(obj: User): String = Json.write(encode(obj))
def validate(json: String): Either[List[ValidationError], User] = { ... }
}
Discriminated unions instead generate a sealed trait, a case class per variant, and a companion whose fromJson dispatches on the discriminator value. variantMatch: prefix uses startsWith; variantMatch: regex uses full-string regular-expression matching in declaration order and requires explicit variantNames. Value-preserving modes retain the original discriminator for round trips. A fallbackVariant adds a catch-all case that preserves an unknown discriminator value and its raw payload, so unrecognized types round-trip rather than fail.
NomosFormats
A single NomosFormats per base package holds the reconstructed MultiTemplate and a MultiValidator built from it. Generated companions import it to validate without the original template file.
object NomosFormats {
lazy val embeddedTemplate: MultiTemplate = ... // reconstructed from your template
lazy val validator: MultiValidator = new MultiValidator(embeddedTemplate)
}
Enum files
An enumType enum generates a self-contained sealed trait with case objects and string (de)serialization over JsonValue — no extra setup.
sealed trait Priority
object Priority {
case object Low extends Priority
case object High extends Priority
val values: List[Priority] = List(Low, High)
def fromString(s: String): Option[Priority] = ...
def asString(v: Priority): String = ...
def decode(json: JsonValue): Either[String, Priority] = ...
def encode(v: Priority): JsonValue = ...
}
A discriminator with discriminatorEnum renders its discriminator values as the same enum shape. When that union also sets fallbackVariant, the enum is open: it gains an Unknown(value) member and a total fromString, so an unrecognized discriminator value decodes to Unknown and encodes back to its original string.
Generation options
Set these at the top level of a template (project-wide in multi-file builds):
| Option | Default | Effect |
|---|---|---|
listType | "List" | List or Array |
mapType | "Map" | Map or java.util.Map for $map fields |
dateType | java.time.LocalDate | e.g. java.util.Date |
dateTimeType | java.time.LocalDateTime | Target for datetime |
fromJsonStyle | "either" | Only "throwing" is special (throwing fromJson); any other value yields the Either-returning fromJson. |
visibility | — | Access prefix on every generated top-level definition (e.g. private, private[models]). |
Per-field, nullable: true yields a raw nullable type (value types boxed as java.lang.Integer/Long/Double/Boolean) and accepts explicit JSON null. nullable: "surfaceOnly" generates the same raw, null-defaulted surface while rejecting explicit JSON null; an omitted field still decodes to null. default is accepted only on non-optional string, numeric, boolean, and enum fields; date, datetime, reference, collection, and object defaults — and an int/long default outside the target range — are rejected at parse time. Defaults render per type: enum → EnumName.Value, long → 123L, a whole-number number/double → 1.0, and decimal → BigDecimal("…").
fromJson style
{ "fromJsonStyle": "throwing", "definitions": [ ... ] }
either (default)
def fromJson(json: String): Either[String, T]
throwing
def fromJson(json: String): T // throws on failure
The style applies to plain definitions and to discriminated/variantNames unions alike; validate adapts to whichever style is generated.
File headers
Every generated file starts with a provenance header. Files tied to a template also point back to it.
// Code generated by Nomos. DO NOT EDIT.
// Source: com/example/models/user.json
package com.example.models.user
Next: Maven Plugin — wiring generation into your build.