YAML-to-POJO for AsyncAPI v2.0/v2.6/v3.0
✅ Polymorphism ✅ Validation ✅ Lombok ✅ Collections/Map ✅ Enums with Descriptions ✅ Inheritance/Interfaces
❌ OpenAPI is not supported (AsyncAPI only)
| Specification | Status | Notes |
|---|---|---|
| AsyncAPI v2.0 / v2.6 | ✅ Full | Primary target version |
| AsyncAPI v3.0 (RC) | ✅ Experimental | Supports operations, channels, messages; payload: { schema: {...} } |
| OpenAPI 3.x | ❌ Not supported | Planned no earlier than 2026 |
💡 For OpenAPI, consider OpenAPI Generator. Yojo is exclusively focused on AsyncAPI.
All examples use valid AsyncAPI-compliant YAML — no abbreviated forms like items: { type: integer }.
YAML
listOfLongs:
type: array
items:
type: integer
format: int64
realization: ArrayList
setOfDates:
type: array
format: set
items:
type: string
format: date
realization: HashSet→ Java
private List<Long> listOfLongs = new ArrayList<>();
private Set<LocalDate> setOfDates = new HashSet<>();YAML
mapUUIDCustomObject:
type: object
format: uuid
additionalProperties:
$ref: '#/components/schemas/User'→ Java
private Map<UUID, User> mapUUIDCustomObject;YAML
Result:
type: object
enum:
- SUCCESS
- DECLINE
- error-case
x-enumNames:
SUCCESS: "Operation succeeded"
DECLINE: "Declined by policy"
error-case: "Legacy lowercase"→ Java
public enum Result {
SUCCESS("Operation succeeded"),
DECLINE("Declined by policy"),
ERROR_CASE("Legacy lowercase");
private final String value;
Result(String value) { this.value = value; }
public String getValue() { return value; }
}✅
error-case→ERROR_CASE,class→CLASS_FIELD
YAML
bigDecimalValue:
type: number
format: big-decimal
multipleOf: 0.01 # → fraction = 2
bigDecimalValue2:
type: number
format: big-decimal
multipleOf: 10.0 # → integer = 2, fraction = 1→ Java
@Digits(integer = 1, fraction = 2)
private BigDecimal bigDecimalValue;
@Digits(integer = 2, fraction = 1)
private BigDecimal bigDecimalValue2;
⚠️ digits: "integer = 2, fraction = 2"is legacy. PrefermultipleOf— it’s standards-compliant, editor-validated, and used for@Digitsinference.
YAML
polymorph:
oneOf:
- $ref: '#/components/schemas/StatusOnly' # { status: string }
- $ref: '#/components/schemas/StatusWithCode' # { status: string, code: integer }→ Java
public class PolymorphStatusOnlyStatusWithCode {
private String status; // from both
private Integer code; // only from StatusWithCode (nullable)
}YAML
UserDto:
type: object
extends:
fromClass: BaseEntity
fromPackage: com.my.base
properties:
name: { type: string }→ class UserDto extends BaseEntity { private String name; }
YAML
CreateUserMessage:
payload:
$ref: '#/components/schemas/UserDto'
extends:
fromClass: BaseEvent
fromPackage: com.my.events→ class CreateUserMessage extends BaseEvent { /* fields from UserDto */ }
(UserDto is not generated if UserDto == BaseEvent)
MyDto:
type: object
$ref: './User.yaml#/components/schemas/User'
extends:
fromClass: User
fromPackage: com.example.common→ class MyDto extends User { } — no field duplication.
| Attribute | YAML | → Java |
|---|---|---|
realization |
users: type: array items: $ref: '#/components/schemas/User' realization: LinkedList |
private List<User> users = new LinkedList<>(); |
cache: type: object realization: HashMap additionalProperties: type: string |
private Map<String, String> cache = new HashMap<>(); |
|
| Supported values | ArrayList, LinkedList, HashSet, HashMap, LinkedHashMap |
| Attribute | YAML | → Java |
|---|---|---|
format: existing |
user: type: object format: existing name: User package: com.my.domain |
private User user; + import com.my.domain.User; |
pathForGenerateMessage |
RequestDto: payload: pathForGenerateMessage: 'io.github.events' $ref: '#/components/schemas/Dto' |
class generated in .../io/github/events/RequestDto.java |
removeSchema: true |
payload: $ref: '#/components/schemas/Temp' removeSchema: true |
schema Temp is not generated, only fields in message |
| Type | YAML | → Java |
|---|---|---|
| Marker | Marker: type: object format: interface |
public interface Marker {} |
| With methods | UserService: type: object format: interface imports: - com.my.dto.User methods: createUser: description: "Creates a new user" definition: "User createUser(String email)" |
public interface UserService { /** Creates a new user */ User createUser(String email);} |
yojo {
configurations {
create("main") {
specificationProperties {
register("api") {
specName("test.yaml")
inputDirectory(layout.projectDirectory.dir("contract").asFile.absolutePath)
outputDirectory(layout.buildDirectory.dir("generated/sources/yojo/com/example/api").get().asFile.absolutePath)
packageLocation("com.example.api")
}
register("one-more-api") {
specName("test.yaml")
inputDirectory(layout.projectDirectory.dir("contract").asFile.absolutePath)
outputDirectory(layout.buildDirectory.dir("generated/sources/yojo/oneMoreApi").get().asFile.absolutePath)
packageLocation("oneMoreApi")
}
}
springBootVersion("3.2.0")
lombok {
enable(true)
allArgsConstructor(true)
noArgsConstructor(true)
accessors {
enable(true)
fluent(false)
chain(true)
}
equalsAndHashCode {
enable(true)
callSuper(false)
}
}
}
}
}
sourceSets {
main.java.srcDir(layout.buildDirectory.dir("generated/sources/yojo"))
}
tasks.compileJava {
dependsOn("generateClasses")
}🔗 Official Gradle Plugin
📦 GitHub
| YAML | → Java | Imports |
|---|---|---|
type: string, format: uuid |
UUID |
java.util.UUID |
type: object, format: date |
LocalDate |
java.time.LocalDate |
type: string, format: local-date-time |
LocalDateTime |
java.time.LocalDateTime |
type: object, format: date-time |
OffsetDateTime |
java.time.OffsetDateTime |
type: number, format: big-decimal, multipleOf: 0.01 |
BigDecimal |
java.math.BigDecimal, @Digits(integer = 1, fraction = 2) |
| YAML | → Java |
|---|---|
type: arrayitems: type: string |
List<String> |
type: arrayformat: setitems: type: integer |
Set<Integer> |
type: objectadditionalProperties: type: string |
Map<String, String> |
type: objectformat: uuidMapadditionalProperties: $ref: '#/components/schemas/User' |
Map<UUID, User> |
type: objectadditionalProperties: type: array format: set items: type: string |
Map<String, Set<String>> |
| Attribute | Where | Type | Example | Generation |
|---|---|---|---|---|
realization |
items, additionalProperties |
string |
realization: ArrayList |
= new ArrayList<>() |
primitive |
field | boolean |
primitive: true |
int, boolean, long |
multipleOf |
number | number |
multipleOf: 0.01 |
@Digits(integer = 1, fraction = 2) |
digits |
number | string |
digits: "integer=2,fraction=2" |
@Digits(...) (legacy) |
validationGroups |
schema | list |
validationGroups: [Create.class] |
@NotBlank(groups = {Create.class}) |
validationGroupsImports |
schema | list |
validationGroupsImports: [pkg.Validation] |
import pkg.Validation; |
validateByGroups |
schema | list |
validateByGroups: [name, email] |
annotations only on listed fields |
lombok.* |
schema/message | nested | see Gradle config | @Data, @Accessors, @AllArgsConstructor |
extends, implements |
schema/message | fromClass, fromPackage, fromInterface |
see examples | extends X implements Y |
format: interface |
schema | — | — | interface X { ... } |
format: existing |
field | name, package |
— | import, no class gen |
pathForGenerateMessage |
message.payload |
string |
io.github.events |
custom message package |
removeSchema |
message.payload |
boolean |
removeSchema: true |
skip DTO gen for $ref target |
x-enumNames |
enum | map |
SUCCESS: "Ok" |
enum + description field |
| Feature | Status | Description |
|---|---|---|
| Jackson annotations | ✅ In progress | @JsonProperty, @JsonFormat, @JsonInclude |
| AsyncAPI spec validation | ✅ In progress | Validate $ref, type, format, circular refs |
| Lombok extensions | ✅ In progress | @Builder, @Singular, @SuperBuilder |
| OpenAPI 3.1 support | 🚧 Planned for Q2 2026 | Only after AsyncAPI 3.0 stabilization |
| Freemarker templates | 🚧 Planned | For enterprise customizations |
- Author: Vladimir Morozkin
- 📧 Email:
jvmorozkin@gmail.com - 📟 Telegram:
@vmorozkin - GitHub: yojo-generator
🙌 Send your contracts — I’ll make a PoC.
🐞 PRs and issues are welcome!
Distributed under the Apache License 2.0.
See LICENSE.md.
🚀 Let’s generate some code!
AsyncAPI → Java — fast, precise, zero manual work.
