• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

torand / jsonschema2java / 22553968753

01 Mar 2026 10:11PM UTC coverage: 83.689% (+1.3%) from 82.424%
22553968753

push

github

torand
feat: add config parameter 'durationClassName' to specify a custom class to represent string schemas with format "duration" in generated code

328 of 433 branches covered (75.75%)

Branch coverage included in aggregate %.

16 of 16 new or added lines in 5 files covered. (100.0%)

41 existing lines in 6 files now uncovered.

888 of 1020 relevant lines covered (87.06%)

5.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/src/main/java/io/github/torand/jsonschema2java/writers/kotlin/KotlinPojoWriter.java
1
/*
2
 * Copyright (c) 2024-2026 Tore Eide Andersen
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package io.github.torand.jsonschema2java.writers.kotlin;
17

18
import io.github.torand.jsonschema2java.generators.Options;
19
import io.github.torand.jsonschema2java.model.AnnotatedTypeName;
20
import io.github.torand.jsonschema2java.model.AnnotationInfo;
21
import io.github.torand.jsonschema2java.model.PojoInfo;
22
import io.github.torand.jsonschema2java.model.PropertyInfo;
23
import io.github.torand.jsonschema2java.utils.PackageUtils;
24
import io.github.torand.jsonschema2java.writers.BaseWriter;
25
import io.github.torand.jsonschema2java.writers.PojoWriter;
26

27
import java.io.Writer;
28
import java.util.List;
29
import java.util.Set;
30
import java.util.concurrent.atomic.AtomicInteger;
31
import java.util.function.Predicate;
32

33
import static io.github.torand.javacommons.collection.CollectionHelper.isEmpty;
34
import static io.github.torand.javacommons.collection.CollectionHelper.nonEmpty;
35
import static io.github.torand.javacommons.stream.StreamHelper.streamSafely;
36
import static io.github.torand.jsonschema2java.utils.KotlinTypeMapper.toKotlinNative;
37
import static io.github.torand.jsonschema2java.utils.PackageUtils.isFqnInPackage;
38
import static java.util.function.Predicate.not;
39

40
/**
41
 * Writes Kotlin code for a pojo.
42
 */
43
public class KotlinPojoWriter extends BaseWriter implements PojoWriter {
44

45
    public KotlinPojoWriter(Writer writer, Options opts) {
46
        super(writer, opts);
4✔
47
    }
1✔
48

49
    @Override
50
    public void write(PojoInfo pojoInfo) {
51
        writeLine("package %s", opts.getModelPackage(pojoInfo.modelSubpackage()));
13✔
52
        writeNewLine();
2✔
53

54
        writeImports(pojoInfo);
3✔
55

56
        if (pojoInfo.isDeprecated()) {
3!
UNCOV
57
            writeLine("@Deprecated(\"%s\")".formatted(pojoInfo.deprecationMessage()));
×
58
        }
59

60
        pojoInfo.annotations().forEach(a -> writeLine(a.annotation()));
12✔
61
        writeLine("@JvmRecord");
5✔
62

63
        writeLine("data class %s (".formatted(pojoInfo.name()));
13✔
64
        if (isEmpty(pojoInfo.properties())) {
4✔
65
            // Empty data classes not allowed in Kotlin
66
            writeNewLine();
2✔
67
            writeIndent(1);
3✔
68
            writeLine("val placeholder: String = \"\"");
6✔
69
        } else {
70
            AtomicInteger propNo = new AtomicInteger(1);
5✔
71
            pojoInfo.properties().forEach(propInfo -> {
7✔
72
                writeNewLine();
2✔
73
                writePropertyAnnotationLines(propInfo);
3✔
74
                writePropertyTypeAndName(propInfo);
3✔
75

76
                if (propNo.getAndIncrement() < pojoInfo.properties().size()) {
6✔
77
                    writeLine(",");
6✔
78
                } else {
79
                    writeNewLine();
2✔
80
                }
81
            });
1✔
82
        }
83

84
        writeLine(")");
5✔
85
    }
1✔
86

87
    private void writeImports(PojoInfo pojoInfo) {
88
        Predicate<String> isInSamePackage = fqn -> isFqnInPackage(fqn, opts.getModelPackage(pojoInfo.modelSubpackage()));
12✔
89

90
        List<String> imports = pojoInfo.aggregatedNormalImports().stream()
4✔
91
            .filter(not(PackageUtils::isFundamentalJavaClass))
3✔
92
            .filter(not(isInSamePackage))
4✔
93
            .filter(not("java.util.List"::equals))
4✔
94
            .filter(not("java.util.Map"::equals))
4✔
95
            .map("import %s"::formatted)
10✔
96
            .toList();
2✔
97

98
        if (nonEmpty(imports)) {
3!
99
            imports.forEach(this::writeLine);
10✔
100
            writeNewLine();
2✔
101
        }
102
    }
1✔
103

104
    private void writePropertyAnnotationLines(PropertyInfo propInfo) {
105
        if (propInfo.isDeprecated()) {
3✔
106
            writeIndent(1);
3✔
107
            writeLine("@Deprecated(\"%s\")".formatted(propInfo.deprecationMessage()));
13✔
108
        }
109
        streamSafely(propInfo.annotations())
4✔
110
            .map(AnnotationInfo::annotation)
3✔
111
            .map(this::prefixPropertyAnnotation)
3✔
112
            .forEach(a -> {
1✔
113
                writeIndent(1);
3✔
114
                writeLine(a);
5✔
115
            });
1✔
116
    }
1✔
117

118
    private void writePropertyTypeAndName(PropertyInfo propInfo) {
119
        AnnotatedTypeName annotatedTypeName = propInfo.type().getAnnotatedFullName();
4✔
120

121
        annotatedTypeName.annotations()
4✔
122
            .map(this::prefixPropertyAnnotation)
3✔
123
            .forEach(a -> {
1✔
124
                writeIndent(1);
3✔
125
                writeLine(a);
5✔
126
            });
1✔
127

128
        writeIndent(1);
3✔
129
        write("val %s: ", escapeReservedKeywords(propInfo.name()));
11✔
130
        write(toKotlinNative(annotatedTypeName.typeName()));
7✔
131

132
        if (!propInfo.required() || propInfo.type().nullable()) {
7✔
133
            write("? = null");
5✔
134
        }
135
    }
1✔
136

137
    private String prefixPropertyAnnotation(String annotation) {
138
        if (annotation.startsWith("@JsonProperty")) {
4!
UNCOV
139
            return annotation;
×
140
        }
141

142
        return "@field:"+annotation.substring(1);
5✔
143
    }
144

145
    private static String escapeReservedKeywords(String name) {
146
        return RESERVED_KEYWORDS.contains(name) ? "`%s`".formatted(name) : name;
6!
147
    }
148

149
    private static final Set<String> RESERVED_KEYWORDS = Set.of(
117✔
150
        "as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in", "interface",
151
        "is", "null", "object", "package", "return", "super", "this", "throw", "true", "try", "typealias",
152
        "typeof", "val", "var", "when", "while"
153
    );
154
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc