diff --git a/annotation-processor-common/src/testFixtures/java/ru/tinkoff/kora/annotation/processor/common/CompileResult.java b/annotation-processor-common/src/testFixtures/java/ru/tinkoff/kora/annotation/processor/common/CompileResult.java
index 011410830..70d0faa22 100644
--- a/annotation-processor-common/src/testFixtures/java/ru/tinkoff/kora/annotation/processor/common/CompileResult.java
+++ b/annotation-processor-common/src/testFixtures/java/ru/tinkoff/kora/annotation/processor/common/CompileResult.java
@@ -71,9 +71,9 @@ public RuntimeException compilationException() {
.filter(d -> d.getKind() == Diagnostic.Kind.ERROR)
.map(Object::toString)
.collect(Collectors.joining("\n"));
- throw new RuntimeException("CompilationError: \n" + errors.indent(2) + "\n" + j.toString().indent(2));
+ return new RuntimeException("CompilationError: \n" + errors.indent(2) + "\n" + j.toString().indent(2));
} catch (IOException e) {
- throw new RuntimeException(e);
+ return new RuntimeException(e);
}
}
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/JsonProcessor.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/JsonProcessor.java
index 1c1fe83f3..6429907d5 100644
--- a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/JsonProcessor.java
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/JsonProcessor.java
@@ -1,18 +1,23 @@
package ru.tinkoff.kora.json.annotation.processor;
import com.squareup.javapoet.JavaFile;
+import javax.lang.model.element.AnnotationMirror;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import ru.tinkoff.kora.annotation.processor.common.AnnotationUtils;
import ru.tinkoff.kora.annotation.processor.common.CommonUtils;
import ru.tinkoff.kora.annotation.processor.common.ComparableTypeMirror;
+import ru.tinkoff.kora.annotation.processor.common.ProcessingErrorException;
import ru.tinkoff.kora.annotation.processor.common.SealedTypeUtils;
import ru.tinkoff.kora.json.annotation.processor.reader.EnumReaderGenerator;
import ru.tinkoff.kora.json.annotation.processor.reader.JsonReaderGenerator;
+import ru.tinkoff.kora.json.annotation.processor.reader.UnboxedReaderGenerator;
import ru.tinkoff.kora.json.annotation.processor.reader.ReaderTypeMetaParser;
import ru.tinkoff.kora.json.annotation.processor.reader.SealedInterfaceReaderGenerator;
import ru.tinkoff.kora.json.annotation.processor.writer.EnumWriterGenerator;
import ru.tinkoff.kora.json.annotation.processor.writer.JsonWriterGenerator;
import ru.tinkoff.kora.json.annotation.processor.writer.SealedInterfaceWriterGenerator;
+import ru.tinkoff.kora.json.annotation.processor.writer.UnboxedWriterGenerator;
import ru.tinkoff.kora.json.annotation.processor.writer.WriterTypeMetaParser;
import javax.annotation.processing.ProcessingEnvironment;
@@ -38,6 +43,8 @@ public class JsonProcessor {
private final SealedInterfaceWriterGenerator sealedWriterGenerator;
private final EnumReaderGenerator enumReaderGenerator;
private final EnumWriterGenerator enumWriterGenerator;
+ private final UnboxedReaderGenerator unboxedReaderGenerator;
+ private final UnboxedWriterGenerator unboxedWriterGenerator;
public JsonProcessor(ProcessingEnvironment processingEnv) {
this.processingEnv = processingEnv;
@@ -52,6 +59,8 @@ public JsonProcessor(ProcessingEnvironment processingEnv) {
this.sealedWriterGenerator = new SealedInterfaceWriterGenerator(this.processingEnv);
this.enumReaderGenerator = new EnumReaderGenerator();
this.enumWriterGenerator = new EnumWriterGenerator();
+ this.unboxedReaderGenerator = new UnboxedReaderGenerator();
+ this.unboxedWriterGenerator = new UnboxedWriterGenerator();
}
public void generateReader(TypeElement jsonElement) {
@@ -62,6 +71,13 @@ public void generateReader(TypeElement jsonElement) {
if (readerElement != null) {
return;
}
+
+ var unboxedAnnotation = AnnotationUtils.findAnnotation(jsonElement, JsonTypes.jsonUnboxed);
+ if (unboxedAnnotation != null) {
+ this.generateUnboxedReader(jsonElement, jsonElementType, unboxedAnnotation);
+ return;
+ }
+
if (jsonElement.getKind() == ElementKind.ENUM) {
this.generateEnumReader(jsonElement);
return;
@@ -98,6 +114,22 @@ private void generateDtoReader(TypeElement typeElement, TypeMirror jsonTypeMirro
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
}
+ private void generateUnboxedReader(
+ TypeElement typeElement,
+ TypeMirror jsonTypeMirror,
+ AnnotationMirror unboxedAnnotation
+ ) {
+ checkUnboxedTarget(typeElement, unboxedAnnotation);
+
+ var packageElement = JsonUtils.jsonClassPackage(this.elements, typeElement);
+ var meta = Objects.requireNonNull(this.readerTypeMetaParser.parseUnboxed(typeElement, jsonTypeMirror));
+ var readerType = Objects.requireNonNull(this.unboxedReaderGenerator.generateForUnboxed(meta));
+
+ var javaFile = JavaFile.builder(packageElement, readerType).build();
+
+ CommonUtils.safeWriteTo(this.processingEnv, javaFile);
+ }
+
private void generateEnumWriter(TypeElement jsonElement) {
var packageElement = JsonUtils.jsonClassPackage(this.elements, jsonElement);
var enumWriterType = this.enumWriterGenerator.generateEnumWriter(jsonElement);
@@ -113,6 +145,11 @@ public void generateWriter(TypeElement jsonElement) {
if (writerElement != null) {
return;
}
+ var unboxedAnnotation = AnnotationUtils.findAnnotation(jsonElement, JsonTypes.jsonUnboxed);
+ if (unboxedAnnotation != null) {
+ this.generateUnboxedWriter(jsonElement, jsonElement.asType(), unboxedAnnotation);
+ return;
+ }
if (jsonElement.getKind() == ElementKind.ENUM) {
this.generateEnumWriter(jsonElement);
return;
@@ -133,6 +170,22 @@ private void generateSealedWriter(TypeElement jsonElement) {
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
}
+ private void generateUnboxedWriter(
+ TypeElement typeElement,
+ TypeMirror jsonTypeMirror,
+ AnnotationMirror unboxedAnnotation
+ ) {
+ checkUnboxedTarget(typeElement, unboxedAnnotation);
+
+ var meta = Objects.requireNonNull(this.writerTypeMetaParser.parseUnboxed(typeElement, jsonTypeMirror));
+
+ var writerType = Objects.requireNonNull(this.unboxedWriterGenerator.generate(meta));
+ var packageElement = JsonUtils.jsonClassPackage(this.elements, typeElement);
+ var javaFile = JavaFile.builder(packageElement, writerType).build();
+
+ CommonUtils.safeWriteTo(this.processingEnv, javaFile);
+ }
+
private void tryGenerateWriter(TypeElement jsonElement, TypeMirror jsonTypeMirror) {
var meta = Objects.requireNonNull(this.writerTypeMetaParser.parse(jsonElement, jsonTypeMirror));
var packageElement = JsonUtils.jsonClassPackage(this.elements, jsonElement);
@@ -141,4 +194,16 @@ private void tryGenerateWriter(TypeElement jsonElement, TypeMirror jsonTypeMirro
var javaFile = JavaFile.builder(packageElement, writerType).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
}
+
+ private void checkUnboxedTarget(TypeElement typeElement, AnnotationMirror unboxedAnnotation) {
+ var kind = typeElement.getKind();
+
+ if (kind != ElementKind.CLASS && kind != ElementKind.RECORD) {
+ throw new ProcessingErrorException(
+ "@JsonUnboxed supported only for classes and records",
+ typeElement,
+ unboxedAnnotation
+ );
+ }
+ }
}
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/JsonTypes.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/JsonTypes.java
index 3f840d456..107c767b9 100644
--- a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/JsonTypes.java
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/JsonTypes.java
@@ -7,6 +7,7 @@ public class JsonTypes {
public static final ClassName jsonInclude = ClassName.get("ru.tinkoff.kora.json.common.annotation", "JsonInclude");
public static final ClassName jsonDiscriminatorField = ClassName.get("ru.tinkoff.kora.json.common.annotation", "JsonDiscriminatorField");
public static final ClassName jsonDiscriminatorValue = ClassName.get("ru.tinkoff.kora.json.common.annotation", "JsonDiscriminatorValue");
+ public static final ClassName jsonUnboxed = ClassName.get("ru.tinkoff.kora.json.common.annotation", "JsonUnboxed");
public static final ClassName jsonReaderAnnotation = ClassName.get("ru.tinkoff.kora.json.common.annotation", "JsonReader");
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/JsonReaderGenerator.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/JsonReaderGenerator.java
index 4c1f5676a..f3cd400f6 100644
--- a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/JsonReaderGenerator.java
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/JsonReaderGenerator.java
@@ -65,7 +65,7 @@ private TypeSpec generateForClass(JsonClassReaderMeta meta) {
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addException(IOException.class)
.addParameter(JsonTypes.jsonParser, "_parser")
- .returns(TypeName.get(meta.typeElement().asType()))
+ .returns(TypeName.get(meta.typeMirror()))
.addAnnotation(Override.class)
.addAnnotation(Nullable.class);
method.addStatement("var _token = _parser.currentToken()");
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/ReaderTypeMetaParser.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/ReaderTypeMetaParser.java
index 3e024b29d..57fc4233e 100644
--- a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/ReaderTypeMetaParser.java
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/ReaderTypeMetaParser.java
@@ -49,6 +49,30 @@ public JsonClassReaderMeta parse(TypeElement jsonClass, TypeMirror typeMirror) t
return new JsonClassReaderMeta(typeMirror, jsonClass, fields);
}
+ public UnboxedReaderMeta parseUnboxed(TypeElement jsonClass, TypeMirror typeMirror) throws ProcessingErrorException {
+ if (jsonClass.getKind() != ElementKind.CLASS && jsonClass.getKind() != ElementKind.RECORD) {
+ throw new IllegalArgumentException("Should not be called for non class elements");
+ }
+ if (jsonClass.getModifiers().contains(Modifier.ABSTRACT)) {
+ throw new IllegalArgumentException("Should not be called for abstract elements");
+ }
+
+ var jsonConstructor = Objects.requireNonNull(this.findJsonConstructor(jsonClass));
+
+ if (jsonConstructor.getParameters().size() != 1) {
+ throw new ProcessingErrorException(
+ "@JsonUnboxed JsonReader can be created only for constructors with single parameter",
+ jsonConstructor
+ );
+ }
+
+ VariableElement parameter = jsonConstructor.getParameters().get(0);
+
+ var fieldMeta = new UnboxedReaderMeta.FieldMeta(parameter, TypeName.get(parameter.asType()));
+
+ return new UnboxedReaderMeta(typeMirror, jsonClass, fieldMeta);
+ }
+
@Nullable
public ReaderFieldType parseReaderFieldType(TypeMirror jsonClass) {
var knownType = this.knownTypes.detect(jsonClass);
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/UnboxedReaderGenerator.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/UnboxedReaderGenerator.java
new file mode 100644
index 000000000..5c6587830
--- /dev/null
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/UnboxedReaderGenerator.java
@@ -0,0 +1,93 @@
+package ru.tinkoff.kora.json.annotation.processor.reader;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import jakarta.annotation.Nullable;
+import java.io.IOException;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeParameterElement;
+import ru.tinkoff.kora.annotation.processor.common.CommonClassNames;
+import ru.tinkoff.kora.annotation.processor.common.CommonUtils;
+import ru.tinkoff.kora.json.annotation.processor.JsonTypes;
+import ru.tinkoff.kora.json.annotation.processor.JsonUtils;
+
+public class UnboxedReaderGenerator {
+
+ public TypeSpec generateForUnboxed(UnboxedReaderMeta meta) {
+
+ var typeBuilder = TypeSpec.classBuilder(JsonUtils.jsonReaderName(meta.typeElement()))
+ .addAnnotation(AnnotationSpec.builder(CommonClassNames.koraGenerated)
+ .addMember("value", CodeBlock.of("$S", UnboxedReaderGenerator.class.getCanonicalName()))
+ .build())
+ .addSuperinterface(ParameterizedTypeName.get(JsonTypes.jsonReader, TypeName.get(meta.typeMirror())))
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addOriginatingElement(meta.typeElement());
+
+ for (TypeParameterElement typeParameter : meta.typeElement().getTypeParameters()) {
+ typeBuilder.addTypeVariable(TypeVariableName.get(typeParameter));
+ }
+
+ var field = meta.field();
+
+ var fieldName = this.readerFieldName(field);
+ var fieldType = ParameterizedTypeName.get(JsonTypes.jsonReader, field.typeName());
+ var readerField = FieldSpec.builder(fieldType, fieldName, Modifier.PRIVATE, Modifier.FINAL).build();
+ var readerParameter = ParameterSpec.builder(fieldType, fieldName).build();
+
+ var constructor = MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(readerParameter)
+ .addStatement("this.$N = $N", readerField, readerParameter);
+
+ typeBuilder.addField(readerField);
+ typeBuilder.addMethod(constructor.build());
+
+ var method = MethodSpec.methodBuilder("read")
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addException(IOException.class)
+ .addParameter(JsonTypes.jsonParser, "_parser")
+ .returns(TypeName.get(meta.typeMirror()))
+ .addAnnotation(Override.class)
+ .addAnnotation(Nullable.class);
+
+ method.addStatement("var _token = _parser.currentToken()");
+ method.beginControlFlow("if (_token == $T.VALUE_NULL)", JsonTypes.jsonToken);
+
+ if (isNullable(field)) {
+ method.addStatement("return new $T(null)", meta.typeElement());
+ } else {
+ method.addStatement(
+ "throw new $T(_parser, $S)",
+ JsonTypes.jsonParseException,
+ "Expecting nonnull value, got VALUE_NULL token"
+ );
+ }
+
+ method.endControlFlow();
+
+ method.addStatement("return new $T($N.read(_parser))", meta.typeElement(), readerField);
+
+ typeBuilder.addMethod(method.build());
+
+ return typeBuilder.build();
+ }
+
+ private String readerFieldName(UnboxedReaderMeta.FieldMeta field) {
+ return field.parameter().getSimpleName() + "Reader";
+ }
+
+ private boolean isNullable(UnboxedReaderMeta.FieldMeta field) {
+ if (field.parameter().asType().getKind().isPrimitive()) {
+ return false;
+ }
+
+ return CommonUtils.isNullable(field.parameter());
+ }
+}
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/UnboxedReaderMeta.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/UnboxedReaderMeta.java
new file mode 100644
index 000000000..a33d6aa0c
--- /dev/null
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/reader/UnboxedReaderMeta.java
@@ -0,0 +1,10 @@
+package ru.tinkoff.kora.json.annotation.processor.reader;
+
+import com.squareup.javapoet.TypeName;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+public record UnboxedReaderMeta(TypeMirror typeMirror, TypeElement typeElement, FieldMeta field) {
+ public record FieldMeta(VariableElement parameter, TypeName typeName) {}
+}
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/UnboxedWriterGenerator.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/UnboxedWriterGenerator.java
new file mode 100644
index 000000000..4d8843dec
--- /dev/null
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/UnboxedWriterGenerator.java
@@ -0,0 +1,68 @@
+package ru.tinkoff.kora.json.annotation.processor.writer;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import jakarta.annotation.Nullable;
+import java.io.IOException;
+import javax.lang.model.element.Modifier;
+import ru.tinkoff.kora.annotation.processor.common.CommonClassNames;
+import ru.tinkoff.kora.json.annotation.processor.JsonTypes;
+import ru.tinkoff.kora.json.annotation.processor.JsonUtils;
+
+public class UnboxedWriterGenerator {
+
+ @Nullable
+ public TypeSpec generate(UnboxedWriterMeta meta) {
+ var typeBuilder = TypeSpec.classBuilder(JsonUtils.jsonWriterName(meta.typeElement()))
+ .addAnnotation(AnnotationSpec.builder(CommonClassNames.koraGenerated)
+ .addMember("value", CodeBlock.of("$S", UnboxedWriterGenerator.class.getCanonicalName()))
+ .build())
+ .addSuperinterface(ParameterizedTypeName.get(JsonTypes.jsonWriter, TypeName.get(meta.typeMirror())))
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addOriginatingElement(meta.typeElement());
+
+ for (var typeParameter : meta.typeElement().getTypeParameters()) {
+ typeBuilder.addTypeVariable(TypeVariableName.get(typeParameter));
+ }
+
+ var field = meta.field();
+
+ var fieldName = this.writerFieldName(field);
+ var fieldType = ParameterizedTypeName.get(JsonTypes.jsonWriter, TypeName.get(field.typeMirror()));
+
+ var writerField = FieldSpec.builder(fieldType, fieldName, Modifier.PRIVATE, Modifier.FINAL).build();
+ var writerParameter = ParameterSpec.builder(fieldType, fieldName).build();
+
+ var constructor = MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(writerParameter)
+ .addStatement("this.$N = $N", writerField, writerParameter);
+
+ typeBuilder.addField(writerField);
+ typeBuilder.addMethod(constructor.build());
+
+ var method = MethodSpec.methodBuilder("write")
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addException(IOException.class)
+ .addParameter(JsonTypes.jsonGenerator, "_gen")
+ .addParameter(ParameterSpec.builder(TypeName.get(meta.typeMirror()), "_object").addAnnotation(Nullable.class).build())
+ .addAnnotation(Override.class)
+ .addCode("if (_object == null) {$>\n_gen.writeNull();\nreturn;$<\n}\n");
+
+ method.addStatement("$N.write(_gen, _object.$L)", writerField, field.accessor());
+
+ typeBuilder.addMethod(method.build());
+ return typeBuilder.build();
+ }
+
+ private String writerFieldName(UnboxedWriterMeta.FieldMeta field) {
+ return field.accessor().getSimpleName() + "Writer";
+ }
+}
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/UnboxedWriterMeta.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/UnboxedWriterMeta.java
new file mode 100644
index 000000000..0e8ed23bd
--- /dev/null
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/UnboxedWriterMeta.java
@@ -0,0 +1,16 @@
+package ru.tinkoff.kora.json.annotation.processor.writer;
+
+import com.squareup.javapoet.TypeName;
+import jakarta.annotation.Nullable;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+public record UnboxedWriterMeta(TypeMirror typeMirror, TypeElement typeElement, FieldMeta field) {
+ public record FieldMeta(
+ VariableElement field,
+ TypeMirror typeMirror,
+ ExecutableElement accessor
+ ) {}
+}
diff --git a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/WriterTypeMetaParser.java b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/WriterTypeMetaParser.java
index 9d059b6ed..5bf114b06 100644
--- a/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/WriterTypeMetaParser.java
+++ b/json/json-annotation-processor/src/main/java/ru/tinkoff/kora/json/annotation/processor/writer/WriterTypeMetaParser.java
@@ -54,6 +54,34 @@ public JsonClassWriterMeta parse(TypeElement jsonClass, TypeMirror typeMirror) {
return new JsonClassWriterMeta(typeMirror, jsonClass, fieldMetas);
}
+ public UnboxedWriterMeta parseUnboxed(TypeElement jsonClass, TypeMirror typeMirror) {
+ if (jsonClass.getKind() != ElementKind.CLASS && jsonClass.getKind() != ElementKind.RECORD) {
+ throw new IllegalArgumentException("Should not be called for non classes");
+ }
+ if (jsonClass.getModifiers().contains(Modifier.ABSTRACT)) {
+ throw new IllegalArgumentException("Should not be called for abstract classes");
+ }
+
+ var fieldElements = this.parseFields(jsonClass);
+
+ if (fieldElements.size() != 1) {
+ throw new ProcessingErrorException(
+ "@JsonUnboxed JsonWriter can be created only for classes with single field",
+ jsonClass
+ );
+ }
+
+ VariableElement field = fieldElements.get(0);
+
+ var fieldMeta = new UnboxedWriterMeta.FieldMeta(
+ field,
+ field.asType(),
+ this.getAccessorMethod(jsonClass, field)
+ );
+
+ return new UnboxedWriterMeta(typeMirror, jsonClass, fieldMeta);
+ }
+
private List
+ * Given the following class:
+ * {@code
+ * @Json
+ * record ValueClass(String value) {}
+ * }
+ *
+ * Then {@code new ValueClass("test")} will be (de)serialized as:
+ * {@code
+ * {"value": "test"}
+ * }
+ *
+ * But after adding {@code @JsonUnboxed}:
+ * {@code
+ * @Json
+ * @JsonUnboxed
+ * record ValueClass(String value) {}
+ * }
+ *
+ * {@code new ValueClass("test")} will be (de)serialized as:
+ * {@code
+ * "test"
+ * }
+ *
+ * Old-fashioned Java value-classes, Kotlin {@code data} and {@code value} classes are also supported. + *
+ *+ * If annotation is placed on a class with more than one field, + * error will be raised. + *
+ *+ * Don't be confused with Jackson's {@code @JsonUnwrapped} annotation which is used + * to move object's fields one level up. + *
+ */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface JsonUnboxed { +}