Skip to content

Commit 018735c

Browse files
committed
support generic records
1 parent 7d83be5 commit 018735c

File tree

4 files changed

+122
-7
lines changed

4 files changed

+122
-7
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.example.customer.generics;
2+
3+
import io.avaje.jsonb.Json;
4+
5+
@Json
6+
public record MyGenericHolderRecord<T>(String title, String author, T document) {}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package org.example.customer.generics;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.LinkedHashMap;
6+
7+
import org.example.customer.Address;
8+
import org.junit.jupiter.api.Test;
9+
10+
import io.avaje.jsonb.JsonType;
11+
import io.avaje.jsonb.Jsonb;
12+
import io.avaje.jsonb.Types;
13+
14+
class MyGenericHolderRecordTest {
15+
16+
Jsonb jsonb = Jsonb.builder().build();
17+
18+
private static MyGenericHolderRecord<Address> createTestData() {
19+
return new MyGenericHolderRecord<>("hello", "art", new Address(90L, "one"));
20+
}
21+
22+
@SuppressWarnings({"rawtypes"})
23+
@Test
24+
void toJson() {
25+
final var bean = createTestData();
26+
27+
final var type = jsonb.type(MyGenericHolderRecord.class);
28+
29+
final var asJson = type.toJson(bean);
30+
assertThat(asJson)
31+
.isEqualTo(
32+
"{\"title\":\"hello\",\"author\":\"art\",\"document\":{\"id\":90,\"street\":\"one\"}}");
33+
assertThat(jsonb.toJson(bean)).isEqualTo(asJson);
34+
35+
final var pageResult = type.fromJson(asJson);
36+
final var document = pageResult.document();
37+
// reading via Object means the list contains LinkedHashMap
38+
assertThat(document).isInstanceOf(LinkedHashMap.class);
39+
final var asMap = (LinkedHashMap) document;
40+
assertThat(asMap.get("street")).isEqualTo("one");
41+
42+
final var view = type.view("author,document(id)");
43+
final var partialJson2 = view.toJson(bean);
44+
// not supporting partial on the generic object (output includes street)
45+
assertThat(partialJson2)
46+
.isEqualTo("{\"author\":\"art\",\"document\":{\"id\":90,\"street\":\"one\"}}");
47+
}
48+
49+
@Test
50+
void toJson_withGenericParam() {
51+
final var bean = createTestData();
52+
53+
final var jsonb = Jsonb.builder().build();
54+
final JsonType<MyGenericHolderRecord<Address>> type =
55+
jsonb.type(Types.newParameterizedType(MyGenericHolderRecord.class, Address.class));
56+
57+
final var asJson = type.toJson(bean);
58+
assertThat(asJson)
59+
.isEqualTo(
60+
"{\"title\":\"hello\",\"author\":\"art\",\"document\":{\"id\":90,\"street\":\"one\"}}");
61+
assertThat(jsonb.toJson(bean)).isEqualTo(asJson);
62+
63+
final var genericResult = type.fromJson(asJson);
64+
final var document = genericResult.document();
65+
66+
assertThat(document.getId()).isEqualTo(90L);
67+
assertThat(document.getStreet()).isEqualTo("one");
68+
69+
final var partial = type.view("author,document(*)");
70+
final var partialJson = partial.toJson(bean);
71+
assertThat(partialJson)
72+
.isEqualTo("{\"author\":\"art\",\"document\":{\"id\":90,\"street\":\"one\"}}");
73+
74+
final var partial2 = type.view("author,document(id)");
75+
final var partialJson2 = partial2.toJson(bean);
76+
assertThat(partialJson2).isEqualTo("{\"author\":\"art\",\"document\":{\"id\":90}}");
77+
}
78+
}

jsonb-generator/src/main/java/io/avaje/jsonb/generator/BeanReader.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package io.avaje.jsonb.generator;
22

3-
import io.avaje.jsonb.Json;
4-
5-
import javax.lang.model.element.Element;
6-
import javax.lang.model.element.TypeElement;
3+
import java.lang.reflect.InvocationTargetException;
74
import java.util.HashSet;
85
import java.util.List;
96
import java.util.Set;
107
import java.util.TreeSet;
118

9+
import javax.lang.model.element.Element;
10+
import javax.lang.model.element.TypeElement;
11+
12+
import io.avaje.jsonb.Json;
13+
1214
class BeanReader {
1315

1416
private final TypeElement beanType;
@@ -25,6 +27,7 @@ class BeanReader {
2527
private final boolean nonAccessibleField;
2628
private FieldReader unmappedField;
2729
private boolean hasRaw;
30+
private final boolean isRecord;
2831

2932
BeanReader(TypeElement beanType, ProcessingContext context) {
3033
this.beanType = beanType;
@@ -40,6 +43,7 @@ class BeanReader {
4043
this.hasSubTypes = typeReader.hasSubTypes();
4144
this.allFields = typeReader.allFields();
4245
this.constructor = typeReader.constructor();
46+
this.isRecord = isRecord(beanType);
4347
}
4448

4549
public BeanReader(TypeElement beanType, TypeElement mixInElement, ProcessingContext context) {
@@ -55,6 +59,21 @@ public BeanReader(TypeElement beanType, TypeElement mixInElement, ProcessingCont
5559
this.hasSubTypes = typeReader.hasSubTypes();
5660
this.allFields = typeReader.allFields();
5761
this.constructor = typeReader.constructor();
62+
this.isRecord = isRecord(beanType);
63+
}
64+
65+
boolean isRecord(TypeElement beanType) {
66+
try {
67+
final List<? extends Element> recordComponents =
68+
(List<? extends Element>)
69+
TypeElement.class.getMethod("getRecordComponents").invoke(beanType);
70+
return !recordComponents.isEmpty();
71+
} catch (IllegalAccessException
72+
| InvocationTargetException
73+
| NoSuchMethodException
74+
| SecurityException e) {
75+
return false;
76+
}
5877
}
5978

6079
int genericTypeParamsCount() {
@@ -279,9 +298,14 @@ void writeFromJson(Append writer) {
279298
// default public constructor
280299
writer.append(" %s _$%s = new %s();", shortName, varName, shortName).eol();
281300
} else {
282-
writer.append(" // variables to read json values into, constructor params don't need _set$ flags").eol();
283-
for (FieldReader allField : allFields) {
284-
if (allField.includeFromJson()) {
301+
writer
302+
.append(
303+
" // variables to read json values into, constructor params don't need _set$ flags")
304+
.eol();
305+
for (final FieldReader allField : allFields) {
306+
if (isRecord) {
307+
allField.writeFromJsonVariablesRecord(writer);
308+
} else if (allField.includeFromJson()) {
285309
allField.writeFromJsonVariables(writer);
286310
}
287311
}

jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,13 @@ void writeFromJsonVariables(Append writer) {
288288
writer.eol();
289289
}
290290

291+
void writeFromJsonVariablesRecord(Append writer) {
292+
final String type = genericTypeParameter ? "Object" : genericType.shortType();
293+
writer.append(" %s _val$%s = %s;", pad(type), fieldName, defaultValue);
294+
295+
writer.eol();
296+
}
297+
291298
private String pad(String value) {
292299
final int pad = 10 - value.length();
293300
if (pad < 1) {

0 commit comments

Comments
 (0)