
Типобезопасный движок шаблонов Java Mustache с проверкой шаблонов во время компиляции, статической привязкой значений и расширяемым экранированием для HTML и других типов контента.
Типобезопасный шаблонизатор Java Mustache.
Шаблоны компилируются в читаемый Java-исходный код, а привязки значений проверяются статически.
Документация также доступна на javadoc.io, но там она не агрегирована, как указано выше.
Агрегированная javadoc-документация является предпочтительной, а остальная часть этого readme
предназначена в основном для пропаганды маркетинга.
Для предыдущих релизов:
https://jstach.io/doc/jstachio/VERSION/apidocs
Где VERSION — нужная вам версия.
Рассмотрено в why_jstachio_is_better.md.
Синтаксис Mustache (v 1.3) без логики.
Получите поддержку, подобную JEP 430, уже сегодня, но с ещё большими возможностями.
Шаблоны компилируются в Java-код
Привязки значений проверяются статически.
В шаблонах можно ссылаться на методы, поля и getter-методы.
Понятные сообщения об ошибках с контекстом.
Нулевая конфигурация. Никаких плагинов или настроек не требуется. Всё делается стандартным javac в любой IDE и/или сборочной системе.
Поддерживаются не-HTML шаблоны. Набор поддерживаемых типов контента с экранированием расширяем.
Макеты поддерживаются через спецификацию наследования Mustache.
Точка расширения запасного сервиса рендеринга через ServiceLoader
Настройка допустимых типов, которые можно выводить; иначе — ошибка компилятора (чтобы избежать toString у классов, у которых нет дружелюбного toString).
Форматтер для кастомного toString переменных во время выполнения
Добавление дополнительных implements интерфейсов к сгенерированному коду для своего рода трейтов-дополнений (@JStacheInterfaces)
Мощная поддержка лямбд
Поддержка Map<String, ?>
Поддержка Optional<?>
Совместимость с расширениями индексов списков JMustache и Handlebars (например, -first, -last, -index)
Это безусловно самый быстрый Java-шаблонизатор, подобный Mustache, а также один из самых быстрых в целом.
Ноль зависимостей, кроме самого JStachio
Доступен вариант с абсолютно нулевыми зависимостями во время выполнения (весь необходимый код генерируется, и даже jstachio не нужен во время выполнения). Нет необходимости использовать Maven shade для процессоров аннотаций и других проектов с нулевыми зависимостями. Также полезно для нативных проектов Graal VM, чтобы максимально минимизировать занимаемое место.
Поддержка Spring Framework высшего уровня (то есть сам проект предоставляет плагины, в отличие от вспомогательного проекта)
Быть самым быстрым Java-шаблонизатором не является целью этого проекта!
(однако на момент последнего обновления этого readme это самый быстрый из известных мне)
Не то чтобы производительность имела большое значение для языков шаблонов (редко бывает узким местом), но JStachio очень быстрый:


@JStache(template = """
{{#people}}
{{message}} {{name}}! You are {{#ageInfo}}{{age}}{{/ageInfo}} years old!
{{#-last}}
That is all for now!
{{/-last}}
{{/people}}
""")
public record HelloWorld(String message, List<Person> people) implements AgeLambdaSupport {
}
public record Person(String name, LocalDate birthday) {
}
public record AgeInfo(long age, String date) {
}
public interface AgeLambdaSupport {
@JStacheLambda
default AgeInfo ageInfo(Person person) {
long age = ChronoUnit.YEARS.between(person.birthday(), LocalDate.now());
String date = person.birthday().format(DateTimeFormatter.ISO_DATE);
return new AgeInfo(age, date);
}
}
@Test
public void testPerson() throws Exception {
Person rick = new Person("Rick", LocalDate.now().minusYears(70));
Person morty = new Person("Morty", LocalDate.now().minusYears(14));
Person beth = new Person("Beth", LocalDate.now().minusYears(35));
Person jerry = new Person("Jerry", LocalDate.now().minusYears(35));
String actual = JStachio.render(new HelloWorld("Hello alien", List.of(rick, morty, beth, jerry)));
String expected = """
Hello alien Rick! You are 70 years old!
Hello alien Morty! You are 14 years old!
Hello alien Beth! You are 35 years old!
Hello alien Jerry! You are 35 years old!
That is all for now!
""";
assertEquals(expected, actual);
}
<properties>
<io.jstach.version>0.6.0-SNAPSHOT</io.jstach.version>
</properties>
...
<dependencies>
<dependency>
<groupId>io.jstach</groupId>
<artifactId>jstachio</artifactId>
<version>${io.jstach.version}</version>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source> <!-- 17 is the minimum -->
<target>17</target> <!-- 17 is the minimum -->
<annotationProcessorPaths>
<path>
<groupId>io.jstach</groupId>
<artifactId>jstachio-apt</artifactId>
<version>${io.jstach.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Прим.: jar с аннотациями (jstachio-annotation) подтягивается транзитивно
dependencies {
implementation 'io.jstach:jstachio:VERSION'
annotationProcessor 'io.jstach:jstachio-apt:VERSION'
}
{{#name}}
<p>Name: {{.}}, Name Length is {{length}}</p>
{{/name}}
<p>Age: {{ age }}</p>
<p>Achievements:</p>
<ul>
{{#array}}
<li>{{.}}</li>
{{/array}}
</ul>
{{^array}}
<p>No achievements</p>
{{/array}}
<p>Items:</p>
<ol>
{{#list1}}
<li>{{value}}</li>
{{/list1}}
</ol>
Следующий класс можно использовать для предоставления фактических данных для заполнения шаблона выше.
@JStache(
// points to src/main/resources/user.mustache file
path = "user.mustache",
// or alternatively you can inline the template
template = "",
)
public record User(String name, int age, String[] array, List<Item<String>> list) {
public static class Item<T> {
private final T value;
public Item(T value) {
this.value = value;
}
T value() {
return value;
}
}
}
Новый класс UserRenderer будет автоматически сгенерирован из приведённого выше кода.
Этот класс можно использовать для рендеринга шаблона с фактическими данными. Для рендеринга шаблона можно использовать следующий код:
class Main {
public static void main(String[] args) throws IOException {
User user = new User("John Doe", 21, new String[] {"Knowns nothing"}, list);
StringBuilder appendable = new StringBuilder();
JStachio.render(user, appendable);
}
}
Результат выполнения этого кода будет следующим:
<p>Name: John Doe, Name Length is 8</p>
<p>Age: 21</p>
<p>Achievements:</p>
<ul>
<li>Knowns nothing</li>
</ul>
<p>Items:</p>
<ol>
<li>helmet</li>
<li>shower</li>
</ol>
Ссылки на несуществующие поля или поля с нерендерящимся типом приводят к ошибкам компиляции. Эти ошибки выводятся во время компиляции вашего проекта вместе с другими возможными ошибками в Java-исходниках.
target/classes/user.mustache:5: error: Field not found in current context: 'age1'
<p>Age: {{ age1 }} ({{birthdate}}) </p>
^
symbol: mustache directive
location: mustache template
target/classes/user.mustache:5: error: Unable to render field: type error: Can't render data.birthdate expression of java.util.Date type
<p>Age: {{ age }} ({{birthdate}}) </p>
^
symbol: mustache directive
location: mustache template
Больше примеров см. в проекте test/examples.
По сути, перечисления имеют булевы ключи, которые являются именами перечислений (Enum.name()) и могут использоваться как условные секции.
Предположим, light — это перечисление вида:
public enum Light {
RED,
GREEN,
YELLOW
}
Вы можете условно выбирать по перечислению, как при сопоставлении с образцом:
{{#light.RED}}
STOP
{{/light.RED}}
{{#light.GREEN}}
GO
{{/light.GREEN}}
{{#light.YELLOW}}
Proceeed with caution
{{/light.YELLOW}}
JStachio совместим с ключами индексов как handlebars, так и JMustache для итерируемых секций.
-first — булево значение, истинное, когда вы находитесь на первом элементе-last — булево значение, истинное, когда вы находитесь на последнем элементе итерируемого объекта-index — индекс, начиная с единицы. Первый элемент будет 1, а не 0JStachio поддерживает вызовы лямбда-секций аналогично JMustache. Просто пометьте свои методы
аннотацией @JStacheLambda, и возвращаемые модели будут использоваться для рендеринга содержимого лямбда-секции.
Верхний элемент стека контекста может быть передан лямбде.
В отличие от спецификации, JStachio не поддерживает возврат динамических шаблонов, которые затем рендерятся против стека контекста. Однако динамический вывод может быть достигнут вызывающим кодом путём изменения содержимого лямбда-секции, так как содержимое секции действует как встроенный шаблон.
Идея состоит в создании шаблонизатора, сочетающего философию mustache без логики с принципом единственной ответственности Java и статической типизацией. Полная проверка синтаксиса и привязки данных на этапе компиляции является основным требованием.
В настоящее время для шаблонов генерируется Java-код. Сгенерированный Java-код никогда не должен приводить к ошибкам компиляции. Если из какого-то шаблона невозможно сгенерировать корректный Java-код, должна выдаваться понятная ошибка компиляции с указанием на файл шаблона. Пользователи никогда не должны сталкиваться со сгенерированным Java-кодом.
Оригинальный mustache использует JavaScript-объекты для определения контекста рендеринга. Поля выбранных JavaScript-объектов связываются с полями шаблона.
Статический mustache использует Java-объекты для определения контекста рендеринга. Привязка полей шаблона определяется и проверяется на этапе компиляции. Отсутствующие поля являются ошибкой компиляции.
JStachio распространяется под лицензией BSD 3-clause.