Examples

Each example starts from the same type definition and shows how a different module uses it — no extra mapping, no separate DTOs.

Shared type definition

All examples below use this single GlobType:

public class ProductType {
    public static final GlobType     TYPE;
    public static final LongField    id;
    public static final StringField  title;
    public static final DoubleField  price;
    public static final BooleanField published;

    static {
        GlobTypeBuilder b = GlobTypeBuilderFactory.create("Product");
        id        = b.declareLongField("id");
        title     = b.declareStringField("title");
        price     = b.declareDoubleField("price");
        published = b.declareBooleanField("published");
        TYPE      = b.build();
    }
}

JSON — globs-gson

Serialize a Glob to JSON and deserialize it back:

import org.globsframework.json.GSonUtils;

// Create a product Glob
MutableGlob product = ProductType.TYPE.instantiate()
    .set(ProductType.id,        1L)
    .set(ProductType.title,     "XPhone")
    .set(ProductType.price,     999.0)
    .set(ProductType.published, true);

// Encode to JSON
String json = GSonUtils.encode(product, false);
// → {"id":1,"title":"XPhone","price":999.0,"published":true}

// Decode from JSON (only fields present in ProductType.TYPE are read)
Glob decoded = GSonUtils.decode(json, ProductType.TYPE);
String title = decoded.get(ProductType.title); // "XPhone"

Sparse / PATCH semantics

Unset fields are not emitted — so partial updates carry only the fields that changed:

MutableGlob patch = ProductType.TYPE.instantiate()
    .set(ProductType.price, 799.0);
// only "price" is set; the others are unset

String patchJson = GSonUtils.encode(patch, false);
// → {"price":799.0}

Database — globs-db

Create a table, insert a row, and query it back:

// Create table from GlobType (no SQL string needed)
sqlConnection.createTable(ProductType.TYPE);

// Insert
sqlConnection.getCreateBuilder(ProductType.TYPE)
    .set(ProductType.id,        1L)
    .set(ProductType.title,     "XPhone")
    .set(ProductType.price,     999.0)
    .set(ProductType.published, true)
    .getRequest().run();

sqlConnection.commit();

// Query with a typed constraint
List<Glob> results = sqlConnection
    .getQueryBuilder(ProductType.TYPE,
        Constraints.and(
            Constraints.equal(ProductType.published, true),
            Constraints.lessThan(ProductType.price, 1000.0)))
    .selectAll()
    .getQuery()
    .executeAsGlobs();

for (Glob g : results) {
    System.out.println(g.get(ProductType.title) + " — $" + g.get(ProductType.price));
}

HTTP API — globs-http

Declare URL parameters and query parameters as GlobTypes, register handlers, and start the server. An OpenAPI JSON is generated automatically.

public class UrlParam {
    public static GlobType  TYPE;
    public static LongField id;
    static {
        GlobTypeBuilder b = GlobTypeBuilderFactory.create("UrlParam");
        id   = b.declareLongField("id");
        TYPE = b.build();
    }
}

public class QueryParam {
    public static GlobType     TYPE;
    public static BooleanField includeUnpublished;
    static {
        GlobTypeBuilder b = GlobTypeBuilderFactory.create("QueryParam");
        includeUnpublished = b.declareBooleanField("includeUnpublished");
        TYPE               = b.build();
    }
}

HttpServerRegister server = new HttpServerRegister("ProductAPI/1.0");

server.register("/products/{id}", UrlParam.TYPE)
    .get(QueryParam.TYPE, (body, url, query, headers) -> {
        long id = url.getNotNull(UrlParam.id);
        boolean all = Boolean.TRUE.equals(query.get(QueryParam.includeUnpublished));

        // fetch from DB...
        Glob product = /* DB lookup */ null;

        return CompletableFuture.completedFuture(product);
    })
    .post(ProductType.TYPE, (body, url, query, headers) -> {
        // body is already a Glob of ProductType
        return CompletableFuture.completedFuture(body);
    });

GlobHttpApacheBuilder builder = new GlobHttpApacheBuilder(server);
builder.startAndWaitForStartup(bootstrap, 8080);

Command-line parsing — globs-commandline

Define your CLI flags as a GlobType; the parser fills them in from argv and auto-generates --help output from annotations.

public class CliArgs {
    public static final GlobType     TYPE;
    public static final StringField  inputFile;
    public static final StringField  outputFile;
    public static final BooleanField verbose;

    static {
        GlobTypeBuilder b = GlobTypeBuilderFactory.create("CliArgs");
        inputFile  = b.declareStringField("inputFile");
        outputFile = b.declareStringField("outputFile");
        verbose    = b.declareBooleanField("verbose");
        TYPE       = b.build();
    }
}

public static void main(String[] args) {
    Glob parsed = CommandLineParser.parse(args, CliArgs.TYPE);

    String  input   = parsed.get(CliArgs.inputFile);
    String  output  = parsed.get(CliArgs.outputFile);
    boolean verbose = Boolean.TRUE.equals(parsed.get(CliArgs.verbose));

    // proceed with input / output / verbose ...
}

Invoked as: myapp --input-file data.csv --output-file out.json --verbose

Nested Globs

GlobField and GlobArrayField embed Globs inside Globs — great for hierarchical data like an order with line items:

public class LineItemType {
    public static final GlobType     TYPE;
    public static final StringField  sku;
    public static final IntegerField qty;
    public static final DoubleField  unitPrice;
    static {
        GlobTypeBuilder b = GlobTypeBuilderFactory.create("LineItem");
        sku       = b.declareStringField("sku");
        qty       = b.declareIntegerField("qty");
        unitPrice = b.declareDoubleField("unitPrice");
        TYPE      = b.build();
    }
}

public class OrderType {
    public static final GlobType       TYPE;
    public static final StringField    orderId;
    public static final GlobArrayField items; // ← nested array of LineItemType
    static {
        GlobTypeBuilder b = GlobTypeBuilderFactory.create("Order");
        orderId = b.declareStringField("orderId");
        items   = b.declareGlobArrayField("items", () -> LineItemType.TYPE);
        TYPE    = b.build();
    }
}

// Build nested structure
MutableGlob line1 = LineItemType.TYPE.instantiate()
    .set(LineItemType.sku,       "SKU-001")
    .set(LineItemType.qty,       2)
    .set(LineItemType.unitPrice, 49.99);

MutableGlob order = OrderType.TYPE.instantiate()
    .set(OrderType.orderId, "ORD-2024-001")
    .set(OrderType.items,   new Glob[]{ line1 });

// Serialize the whole tree to JSON in one call
String json = GSonUtils.encode(order, false);
// → {"orderId":"ORD-2024-001","items":[{"sku":"SKU-001","qty":2,"unitPrice":49.99}]}

Dynamic types from JSON schema

When the schema is not known at compile time — e.g. reading a configuration format defined externally — build a GlobType at runtime:

// Suppose we receive this JSON describing the type
String schemaJson = """
    {"name":"Event","fields":[
      {"name":"timestamp","type":"Long"},
      {"name":"source","type":"String"},
      {"name":"payload","type":"String"}
    ]}
    """;

GlobType eventType = GlobTypeDeserializer.parse(schemaJson);

// Now use it exactly like a statically-defined type
Glob event = GSonUtils.decode(incomingJson, eventType);
Field sourceField = eventType.getField("source");
String source = (String) event.getValue(sourceField);