JJTemplate
EN GitHub ↗
Java 11+ · v1.0.0 · JSON-совместимый результат

Шаблоны, которые остаются
близкими к данным.

JJT означает Java JSON Template: компактный язык шаблонов, который компилирует JSON-документы в оптимизированные деревья выражений для быстрого повторного рендеринга.

Одна компиляцияПовторное использование шаблона
Типизированные значенияНе только строковая подстановка
РасширяемостьФункции Java с пространствами имён
invoice.jjt
{
  "definitions": [
    { "name": "Ada" },
    { "price": 420 },
    { "quantity": 3 },
    {
      "total": "{{ math:mul .price, .quantity }}"
    }
  ],
  "template": {
    "customer": "{{ .name | string:trim }}",
    "total": "{{ .total }}",
    "tier": "{{ .total | gt 1000 ? 'priority' : 'standard' }}"
  }
}
результат { "customer": "Ada", "total": 1260, "tier": "priority" }
01

Быстрый старт

Добавьте библиотеку, скомпилируйте JSON-шаблон и передайте контекст для рендеринга.

implementation("io.github.sibmaks.jjtemplate:jjtemplate:1.0.0")
<dependency>
  <groupId>io.github.sibmaks.jjtemplate</groupId>
  <artifactId>jjtemplate</artifactId>
  <version>1.0.0</version>
  <type>pom</type>
</dependency>
  1. 1
    Опишите шаблон

    Передайте definitions и JSON-совместимый template в TemplateScript.

  2. 2
    Скомпилируйте один раз

    Компилятор свернёт константы и подготовит дерево выражений.

  3. 3
    Передайте контекст

    Рендерите разные данные без повторной компиляции.

Java
var script = TemplateScript.builder()
        .template(
                Map.of(
                        "message", "{{ string:concat 'Hello, ', .name }}",
                        "active", "{{ .enabled }}"
                )
        )
        .build();

var compiler = TemplateCompiler.getInstance();
var compiled = compiler.compile(script);

// Повторно используйте compiled для каждого контекста.
var result = compiled.render(
        Map.of(
                "name", "Alice",
                "enabled", true
        )
);
02

Компактный синтаксис

Структура JSON остаётся видимой, а выражения отвечают за подстановку, условия и разворачивание.

.user.name

Доступ к переменным

Читайте корневые и вложенные значения из контекста и definitions.

{{ expression }}

Подстановка

Вставляйте типизированный результат: boolean и number сохраняют тип.

{{? expression }}

Условная вставка

Пропускайте поле объекта или элемент массива, если результат равен null.

{{. expression }}

Разворачивание

Разворачивайте объект в объект, а коллекцию — в массив.

.value | string:trim

Pipe

Передавайте значение слева в функцию и составляйте цепочки преобразований.

.ready ? 'yes' : 'no'

Тернарный оператор

Выбирайте значение; ветви могут содержать вызовы, pipe и вложенные выражения.

.repository?.foo('bar')

Безопасный доступ

Получайте null, если свойства или подходящего метода нет; задавайте fallback через default.

Явно отмечайте ключи-выражения.

Ключи definitions для switch и range используют те же разделители {{ ... }}, что и другие выражения.

03

Изучайте на преобразованиях

Каждый пример сопоставляет JJT-документ и JSON-значение, полученное во время выполнения.

template.jjt

        
output.json

        
04

Справочник функций

Функции сгруппированы по пространствам имён. Глобальные логические функции вызываются без namespace.

cast

str · int · float · boolean

Преобразуйте значения через cast:str, cast:int, cast:float и cast:boolean.

string

concat · join · format

Объединяйте и форматируйте строки, при необходимости с locale.

string

lower · upper · trim

Меняйте регистр и удаляйте пробелы по краям строки.

string

len · empty · contains

Проверяйте длину, пустоту и наличие подстроки.

string

split · substr · indexOf

Разбивайте строки, получайте подстроки и ищите позиции.

string

replace · replaceAll

Заменяйте литералы и совпадения регулярных выражений.

list

new · concat

Создавайте списки и объединяйте массивы или коллекции.

list

len · head · tail · join

Проверяйте, выбирайте и объединяйте элементы списка.

map

new · collapse

Создавайте map и сворачивайте свойства объектов.

map

len · empty · contains

Проверяйте размер, пустоту и наличие ключей.

math

sum · sub · mul · div

Выполняйте десятичную арифметику, меняйте знак и scale.

date/time

now · parse · format

Создавайте, разбирайте и форматируйте даты и время.

locale

locale:new · numberFormat:new

Создавайте форматтеры чисел и валют с учётом locale.

global

eq · neq · lt · le · gt · ge

Сравнивайте значения без пространства имён.

global

not · and · or · xor

Составляйте логические выражения; and и or используют короткое замыкание.

global

default

Возвращайте fallback только при значении null.

05

Добавляйте свои функции

Реализуйте публичный контракт TemplateFunction, зарегистрируйте экземпляр и вызывайте его по namespace и имени.

ReverseTemplateFunction.java
public final class ReverseTemplateFunction
        implements TemplateFunction<String> {

    public String invoke(List<Object> args) {
        if (args.size() != 1) {
            throw fail("exactly 1 argument required");
        }
        return reverse(args.get(0));
    }

    public String invoke(List<Object> args, Object pipeArg) {
        if (!args.isEmpty()) {
            throw fail("no arguments expected");
        }
        return reverse(pipeArg);
    }

    public String getNamespace() { return "custom"; }
    public String getName() { return "reverse"; }
    public boolean isDynamic() { return false; }

    private String reverse(Object value) {
        return value == null ? null
                : new StringBuilder(value.toString()).reverse().toString();
    }
}
01

Регистрация

var evaluation = TemplateEvaluationOptions.builder()
    .functions(List.of(new ReverseTemplateFunction()))
    .build();

var compiler = TemplateCompiler.getInstance(
    TemplateCompileOptions.builder()
        .evaluationOptions(evaluation)
        .build());
02

Вызов

"{{ custom:reverse .value }}"
"{{ .value | custom:reverse }}"
СОВЕТ

Не обманывайте оптимизатор

Возвращайте false из isDynamic() только для детерминированных функций без побочных эффектов.

06

Практические рекомендации

Несколько правил делают шаблоны быстрыми, тестируемыми и предсказуемыми.

01

Компилируйте вне hot path

Создавайте CompiledTemplate при изменении конфигурации и повторно используйте его для разных контекстов.

02

Делайте функции чистыми

Пользовательские функции должны быть thread-safe и не иметь побочных эффектов.

03

Выделяйте namespace

Используйте стабильное пространство имён, например billing:tax.

04

Используйте lazy точечно

Переопределяйте isLazy() только для короткого замыкания и обращайтесь к аргументам по мере необходимости.

05

Тестируйте пары шаблон → результат

Храните рядом .jjt, контекст и ожидаемый JSON. Проверяйте значения, а не форматирование.

06

Сохраняйте JSON видимым

Предпочитайте небольшие definitions и pipe глубоко вложенным выражениям.

07

От исходника к значению

JJTemplate разделяет parsing, optimization и evaluation, чтобы сократить работу во время выполнения.

01Lexer

Разбивает JJT на токены.

02Parser

Строит синтаксические деревья.

03Compiler

Создаёт исполняемые узлы.

04Optimizer

Сворачивает и упрощает один раз.

05Runtime

Формирует JSON-совместимые значения.

Политика 1.0

Совместимость входит в release gate.

Поддерживаемые API lexer, parser, compiler, runtime, exceptions и functions проверяются относительно опубликованного baseline.

Открыть политику →

Готовы формировать JSON?

Оставьте шаблон читаемым.
Остальное сделает компилятор.