From 7d83be5f65621df8d4d5f9f069296e58c204a709 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Wed, 14 Dec 2022 17:11:10 -0500 Subject: [PATCH 1/7] update mixin test --- .../src/main/java/org/example/customer/mixin/CrewMateMixIn.java | 1 + .../src/test/java/org/example/customer/mixin/MixinTest.java | 2 +- .../src/main/java/io/avaje/jsonb/generator/FieldReader.java | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blackbox-test/src/main/java/org/example/customer/mixin/CrewMateMixIn.java b/blackbox-test/src/main/java/org/example/customer/mixin/CrewMateMixIn.java index 51f4f045..c4049b03 100644 --- a/blackbox-test/src/main/java/org/example/customer/mixin/CrewMateMixIn.java +++ b/blackbox-test/src/main/java/org/example/customer/mixin/CrewMateMixIn.java @@ -8,6 +8,7 @@ public abstract class CrewMateMixIn { @Json.Property("color") private String c; + @Json.JsonAlias("heatLevel") @Json.Ignore(deserialize = true) private Integer susLv; diff --git a/blackbox-test/src/test/java/org/example/customer/mixin/MixinTest.java b/blackbox-test/src/test/java/org/example/customer/mixin/MixinTest.java index 17d6587b..1857f60c 100644 --- a/blackbox-test/src/test/java/org/example/customer/mixin/MixinTest.java +++ b/blackbox-test/src/test/java/org/example/customer/mixin/MixinTest.java @@ -17,7 +17,7 @@ void toJsonFromJson() { final var asJson = jsonb.toJson(bean); assertThat(asJson).isEqualTo("{\"color\":\"red\",\"taskNumber\":45}"); - final var fromJson = jsonb.type(CrewMate.class).fromJson("{\"color\":\"blue\",\"susLv\":\"0\",\"taskNumber\":45}}"); + final var fromJson = jsonb.type(CrewMate.class).fromJson("{\"color\":\"blue\",\"heatLevel\":\"0\",\"taskNumber\":45}}"); assertThat(fromJson.getC()).isEqualTo("blue"); assertThat(fromJson.getSusLv()).isZero(); assertThat(fromJson.getTaskNumber()).isEqualTo(45); diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java index f603ca10..1c15c596 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java @@ -306,7 +306,6 @@ void writeFromJsonSwitch(Append writer, boolean defaultConstructor, String varNa } if (aliases != null) { - for (final String alias : aliases) { writer.append(" case \"%s\":", alias); writer.eol(); From 018735cf8416f250053d8e53da244b46fe49dca3 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 27 Dec 2022 14:20:43 -0500 Subject: [PATCH 2/7] support generic records --- .../generics/MyGenericHolderRecord.java | 6 ++ .../generics/MyGenericHolderRecordTest.java | 78 +++++++++++++++++++ .../io/avaje/jsonb/generator/BeanReader.java | 38 +++++++-- .../io/avaje/jsonb/generator/FieldReader.java | 7 ++ 4 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolderRecord.java create mode 100644 blackbox-test/src/test/java/org/example/customer/generics/MyGenericHolderRecordTest.java diff --git a/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolderRecord.java b/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolderRecord.java new file mode 100644 index 00000000..124a82d2 --- /dev/null +++ b/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolderRecord.java @@ -0,0 +1,6 @@ +package org.example.customer.generics; + +import io.avaje.jsonb.Json; + +@Json +public record MyGenericHolderRecord(String title, String author, T document) {} diff --git a/blackbox-test/src/test/java/org/example/customer/generics/MyGenericHolderRecordTest.java b/blackbox-test/src/test/java/org/example/customer/generics/MyGenericHolderRecordTest.java new file mode 100644 index 00000000..a669ee7b --- /dev/null +++ b/blackbox-test/src/test/java/org/example/customer/generics/MyGenericHolderRecordTest.java @@ -0,0 +1,78 @@ +package org.example.customer.generics; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.LinkedHashMap; + +import org.example.customer.Address; +import org.junit.jupiter.api.Test; + +import io.avaje.jsonb.JsonType; +import io.avaje.jsonb.Jsonb; +import io.avaje.jsonb.Types; + +class MyGenericHolderRecordTest { + + Jsonb jsonb = Jsonb.builder().build(); + + private static MyGenericHolderRecord
createTestData() { + return new MyGenericHolderRecord<>("hello", "art", new Address(90L, "one")); + } + + @SuppressWarnings({"rawtypes"}) + @Test + void toJson() { + final var bean = createTestData(); + + final var type = jsonb.type(MyGenericHolderRecord.class); + + final var asJson = type.toJson(bean); + assertThat(asJson) + .isEqualTo( + "{\"title\":\"hello\",\"author\":\"art\",\"document\":{\"id\":90,\"street\":\"one\"}}"); + assertThat(jsonb.toJson(bean)).isEqualTo(asJson); + + final var pageResult = type.fromJson(asJson); + final var document = pageResult.document(); + // reading via Object means the list contains LinkedHashMap + assertThat(document).isInstanceOf(LinkedHashMap.class); + final var asMap = (LinkedHashMap) document; + assertThat(asMap.get("street")).isEqualTo("one"); + + final var view = type.view("author,document(id)"); + final var partialJson2 = view.toJson(bean); + // not supporting partial on the generic object (output includes street) + assertThat(partialJson2) + .isEqualTo("{\"author\":\"art\",\"document\":{\"id\":90,\"street\":\"one\"}}"); + } + + @Test + void toJson_withGenericParam() { + final var bean = createTestData(); + + final var jsonb = Jsonb.builder().build(); + final JsonType> type = + jsonb.type(Types.newParameterizedType(MyGenericHolderRecord.class, Address.class)); + + final var asJson = type.toJson(bean); + assertThat(asJson) + .isEqualTo( + "{\"title\":\"hello\",\"author\":\"art\",\"document\":{\"id\":90,\"street\":\"one\"}}"); + assertThat(jsonb.toJson(bean)).isEqualTo(asJson); + + final var genericResult = type.fromJson(asJson); + final var document = genericResult.document(); + + assertThat(document.getId()).isEqualTo(90L); + assertThat(document.getStreet()).isEqualTo("one"); + + final var partial = type.view("author,document(*)"); + final var partialJson = partial.toJson(bean); + assertThat(partialJson) + .isEqualTo("{\"author\":\"art\",\"document\":{\"id\":90,\"street\":\"one\"}}"); + + final var partial2 = type.view("author,document(id)"); + final var partialJson2 = partial2.toJson(bean); + assertThat(partialJson2).isEqualTo("{\"author\":\"art\",\"document\":{\"id\":90}}"); + } +} diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/BeanReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/BeanReader.java index 14a31897..d03ab4d7 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/BeanReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/BeanReader.java @@ -1,14 +1,16 @@ package io.avaje.jsonb.generator; -import io.avaje.jsonb.Json; - -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; +import java.lang.reflect.InvocationTargetException; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +import io.avaje.jsonb.Json; + class BeanReader { private final TypeElement beanType; @@ -25,6 +27,7 @@ class BeanReader { private final boolean nonAccessibleField; private FieldReader unmappedField; private boolean hasRaw; + private final boolean isRecord; BeanReader(TypeElement beanType, ProcessingContext context) { this.beanType = beanType; @@ -40,6 +43,7 @@ class BeanReader { this.hasSubTypes = typeReader.hasSubTypes(); this.allFields = typeReader.allFields(); this.constructor = typeReader.constructor(); + this.isRecord = isRecord(beanType); } public BeanReader(TypeElement beanType, TypeElement mixInElement, ProcessingContext context) { @@ -55,6 +59,21 @@ public BeanReader(TypeElement beanType, TypeElement mixInElement, ProcessingCont this.hasSubTypes = typeReader.hasSubTypes(); this.allFields = typeReader.allFields(); this.constructor = typeReader.constructor(); + this.isRecord = isRecord(beanType); + } + + boolean isRecord(TypeElement beanType) { + try { + final List recordComponents = + (List) + TypeElement.class.getMethod("getRecordComponents").invoke(beanType); + return !recordComponents.isEmpty(); + } catch (IllegalAccessException + | InvocationTargetException + | NoSuchMethodException + | SecurityException e) { + return false; + } } int genericTypeParamsCount() { @@ -279,9 +298,14 @@ void writeFromJson(Append writer) { // default public constructor writer.append(" %s _$%s = new %s();", shortName, varName, shortName).eol(); } else { - writer.append(" // variables to read json values into, constructor params don't need _set$ flags").eol(); - for (FieldReader allField : allFields) { - if (allField.includeFromJson()) { + writer + .append( + " // variables to read json values into, constructor params don't need _set$ flags") + .eol(); + for (final FieldReader allField : allFields) { + if (isRecord) { + allField.writeFromJsonVariablesRecord(writer); + } else if (allField.includeFromJson()) { allField.writeFromJsonVariables(writer); } } diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java index 1c15c596..819ced28 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java @@ -288,6 +288,13 @@ void writeFromJsonVariables(Append writer) { writer.eol(); } + void writeFromJsonVariablesRecord(Append writer) { + final String type = genericTypeParameter ? "Object" : genericType.shortType(); + writer.append(" %s _val$%s = %s;", pad(type), fieldName, defaultValue); + + writer.eol(); + } + private String pad(String value) { final int pad = 10 - value.length(); if (pad < 1) { From 2466394644b1061b27e0431d80c65e9252d6e676 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 27 Dec 2022 14:21:17 -0500 Subject: [PATCH 3/7] Revert "update mixin test" This reverts commit 7d83be5f65621df8d4d5f9f069296e58c204a709. --- .../src/main/java/org/example/customer/mixin/CrewMateMixIn.java | 1 - .../src/test/java/org/example/customer/mixin/MixinTest.java | 2 +- .../src/main/java/io/avaje/jsonb/generator/FieldReader.java | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blackbox-test/src/main/java/org/example/customer/mixin/CrewMateMixIn.java b/blackbox-test/src/main/java/org/example/customer/mixin/CrewMateMixIn.java index c4049b03..51f4f045 100644 --- a/blackbox-test/src/main/java/org/example/customer/mixin/CrewMateMixIn.java +++ b/blackbox-test/src/main/java/org/example/customer/mixin/CrewMateMixIn.java @@ -8,7 +8,6 @@ public abstract class CrewMateMixIn { @Json.Property("color") private String c; - @Json.JsonAlias("heatLevel") @Json.Ignore(deserialize = true) private Integer susLv; diff --git a/blackbox-test/src/test/java/org/example/customer/mixin/MixinTest.java b/blackbox-test/src/test/java/org/example/customer/mixin/MixinTest.java index 1857f60c..17d6587b 100644 --- a/blackbox-test/src/test/java/org/example/customer/mixin/MixinTest.java +++ b/blackbox-test/src/test/java/org/example/customer/mixin/MixinTest.java @@ -17,7 +17,7 @@ void toJsonFromJson() { final var asJson = jsonb.toJson(bean); assertThat(asJson).isEqualTo("{\"color\":\"red\",\"taskNumber\":45}"); - final var fromJson = jsonb.type(CrewMate.class).fromJson("{\"color\":\"blue\",\"heatLevel\":\"0\",\"taskNumber\":45}}"); + final var fromJson = jsonb.type(CrewMate.class).fromJson("{\"color\":\"blue\",\"susLv\":\"0\",\"taskNumber\":45}}"); assertThat(fromJson.getC()).isEqualTo("blue"); assertThat(fromJson.getSusLv()).isZero(); assertThat(fromJson.getTaskNumber()).isEqualTo(45); diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java index 819ced28..d0e4b7c2 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java @@ -313,6 +313,7 @@ void writeFromJsonSwitch(Append writer, boolean defaultConstructor, String varNa } if (aliases != null) { + for (final String alias : aliases) { writer.append(" case \"%s\":", alias); writer.eol(); From f4e8e5cabd8c0b1dffb70d774c467d7d5d54a377 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Wed, 28 Dec 2022 00:14:15 -0500 Subject: [PATCH 4/7] fixes generic type names --- .../src/main/java/io/avaje/jsonb/generator/FieldReader.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java index d0e4b7c2..568dce4d 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java @@ -84,10 +84,7 @@ private String initAdapterShortType(String shortType) { private String initShortName() { if (genericTypeParameter) { - String name = genericType.shortName(); - for (String typeParam : genericTypeParams) { - name = name.replace(typeParam, ""); - } + final String name = genericType.shortName(); return Util.initLower(name) + "JsonAdapterGeneric"; } return Util.initLower(genericType.shortName()) + "JsonAdapter"; From 3c8ec0991efcaec4cc35699b102a0218d753a9fc Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Wed, 28 Dec 2022 01:50:44 -0500 Subject: [PATCH 5/7] handle nested types --- .../io/avaje/jsonb/generator/SimpleAdapterWriter.java | 8 +++++++- .../avaje/jsonb/generator/SimpleComponentWriter.java | 11 ++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleAdapterWriter.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleAdapterWriter.java index 3e414229..75c81688 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleAdapterWriter.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleAdapterWriter.java @@ -53,8 +53,14 @@ void write() throws IOException { private void writeFactory() { if (genericParamsCount > 0) { + + String typeName = adapterShortName; + int nestedIndex = adapterShortName.indexOf("$"); + if (nestedIndex != -1) { + typeName = typeName.substring(nestedIndex + 1); + } writer.append(" public static final JsonAdapter.Factory Factory = (type, jsonb) -> {").eol(); - writer.append(" if (type instanceof ParameterizedType && Types.rawType(type) == %s.class) {", adapterShortName).eol(); + writer.append(" if (type instanceof ParameterizedType && Types.rawType(type) == %s.class) {", typeName).eol(); writer.append(" Type[] args = Types.typeArguments(type);").eol(); writer.append(" return new %sJsonAdapter(jsonb", adapterShortName); for (int i = 0; i < genericParamsCount; i++) { diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleComponentWriter.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleComponentWriter.java index c2c64a00..2a114a39 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleComponentWriter.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleComponentWriter.java @@ -53,11 +53,12 @@ void writeMetaInf() throws IOException { private void writeRegister() { writer.append(" @Override").eol(); writer.append(" public void register(Jsonb.Builder builder) {").eol(); - List strings = metaData.allFactories(); - for (String adapterFullName : strings) { - String adapterShortName = Util.shortName(adapterFullName); - String typeName = typeShortName(adapterShortName); - writer.append(" builder.add(%sJsonAdapter.Factory);", typeName).eol(); + final List strings = metaData.allFactories(); + for (final String adapterFullName : strings) { + final String adapterShortName = Util.shortName(adapterFullName); + final String typeName = typeShortName(adapterShortName); + + writer.append(" builder.add(%s.Factory);", adapterShortName).eol(); } for (String adapterFullName : metaData.all()) { String adapterShortName = Util.shortName(adapterFullName); From 9a2c4e56e869c140e3a00e61626587ca6946f4eb Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Wed, 28 Dec 2022 02:32:15 -0500 Subject: [PATCH 6/7] Update SimpleComponentWriter.java --- .../java/io/avaje/jsonb/generator/SimpleComponentWriter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleComponentWriter.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleComponentWriter.java index 2a114a39..98ca28ec 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleComponentWriter.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/SimpleComponentWriter.java @@ -56,7 +56,6 @@ private void writeRegister() { final List strings = metaData.allFactories(); for (final String adapterFullName : strings) { final String adapterShortName = Util.shortName(adapterFullName); - final String typeName = typeShortName(adapterShortName); writer.append(" builder.add(%s.Factory);", adapterShortName).eol(); } From 946f87d9a12b5411dba8a8c3d82ec2b45d9c782c Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Wed, 28 Dec 2022 11:11:49 -0500 Subject: [PATCH 7/7] test nested and 3 params --- .../org/example/customer/generics/MyGenericHolder.java | 3 +++ .../customer/generics/MyGenericHolderRecord.java | 6 ------ .../customer/generics/MyGenericHolderRecordTest.java | 9 ++++++--- jsonb/src/main/java/io/avaje/jsonb/core/Util.java | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) delete mode 100644 blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolderRecord.java diff --git a/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolder.java b/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolder.java index 08204631..60920ebe 100644 --- a/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolder.java +++ b/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolder.java @@ -9,6 +9,9 @@ public class MyGenericHolder { String author; T document; + @Json + public record MyGenericHolderRecord(T title, T2 author, T3 document) {} + public String getTitle() { return title; } diff --git a/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolderRecord.java b/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolderRecord.java deleted file mode 100644 index 124a82d2..00000000 --- a/blackbox-test/src/main/java/org/example/customer/generics/MyGenericHolderRecord.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.example.customer.generics; - -import io.avaje.jsonb.Json; - -@Json -public record MyGenericHolderRecord(String title, String author, T document) {} diff --git a/blackbox-test/src/test/java/org/example/customer/generics/MyGenericHolderRecordTest.java b/blackbox-test/src/test/java/org/example/customer/generics/MyGenericHolderRecordTest.java index a669ee7b..c678fd77 100644 --- a/blackbox-test/src/test/java/org/example/customer/generics/MyGenericHolderRecordTest.java +++ b/blackbox-test/src/test/java/org/example/customer/generics/MyGenericHolderRecordTest.java @@ -5,6 +5,7 @@ import java.util.LinkedHashMap; import org.example.customer.Address; +import org.example.customer.generics.MyGenericHolder.MyGenericHolderRecord; import org.junit.jupiter.api.Test; import io.avaje.jsonb.JsonType; @@ -15,7 +16,7 @@ class MyGenericHolderRecordTest { Jsonb jsonb = Jsonb.builder().build(); - private static MyGenericHolderRecord
createTestData() { + private static MyGenericHolderRecord createTestData() { return new MyGenericHolderRecord<>("hello", "art", new Address(90L, "one")); } @@ -51,8 +52,10 @@ void toJson_withGenericParam() { final var bean = createTestData(); final var jsonb = Jsonb.builder().build(); - final JsonType> type = - jsonb.type(Types.newParameterizedType(MyGenericHolderRecord.class, Address.class)); + final JsonType> type = + jsonb.type( + Types.newParameterizedType( + MyGenericHolderRecord.class, String.class, String.class, Address.class)); final var asJson = type.toJson(bean); assertThat(asJson) diff --git a/jsonb/src/main/java/io/avaje/jsonb/core/Util.java b/jsonb/src/main/java/io/avaje/jsonb/core/Util.java index bd6ebe4a..640b9ca0 100644 --- a/jsonb/src/main/java/io/avaje/jsonb/core/Util.java +++ b/jsonb/src/main/java/io/avaje/jsonb/core/Util.java @@ -286,13 +286,13 @@ static final class ParameterizedTypeImpl implements ParameterizedType { public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { // Require an owner type if the raw type needs it. - if (rawType instanceof Class) { + if (ownerType != null && rawType instanceof Class) { Class enclosingClass = ((Class) rawType).getEnclosingClass(); - if (ownerType != null) { - if (enclosingClass == null || Util.rawType(ownerType) != enclosingClass) { - throw new IllegalArgumentException( + + if (enclosingClass == null || Util.rawType(ownerType) != enclosingClass) { + throw new IllegalArgumentException( "unexpected owner type for " + rawType + ": " + ownerType); - } + } else if (enclosingClass != null) { throw new IllegalArgumentException("unexpected owner type for " + rawType + ": null"); }