Skip to content

Commit e3e556a

Browse files
authored
Merge pull request #178 from avaje/SentryMan-methods
support json property methods
2 parents ffbb2ba + 5271859 commit e3e556a

File tree

7 files changed

+60
-24
lines changed

7 files changed

+60
-24
lines changed

blackbox-test/src/main/java/org/example/customer/naming/WithName.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,11 @@ public record WithName(
1111
String simplePlus,
1212
int myOneRed) {
1313

14+
/**
15+
* A read-only JSON property without an underlying field.
16+
*/
17+
@Json.Property("derived")
18+
public int calculated() {
19+
return myOneRed * 2;
20+
}
1421
}

blackbox-test/src/test/java/org/example/customer/naming/WithNameTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ void toFrom() {
1717
WithName bean = new WithName("sim", "simPlus", 42);
1818

1919
String asJson = jsonType.toJson(bean);
20-
assertThat(asJson).isEqualTo("{\"Some Thing Odd\":\"sim\",\"simple-plus\":\"simPlus\",\"my-one-red\":42}");
20+
assertThat(asJson).isEqualTo("{\"Some Thing Odd\":\"sim\",\"simple-plus\":\"simPlus\",\"my-one-red\":42,\"derived\":84}");
2121

2222
WithName fromJson = jsonType.fromJson(asJson);
2323
assertEquals(fromJson, bean);

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,10 +424,12 @@ private void writeFromJsonImplementation(Append writer, String varName) {
424424
} else {
425425
writer.append(" // variables to read json values into, constructor params don't need _set$ flags").eol();
426426
for (final FieldReader allField : allFields) {
427-
if (isRecord) {
428-
allField.writeFromJsonVariablesRecord(writer);
429-
} else if (allField.includeFromJson()) {
430-
allField.writeFromJsonVariables(writer);
427+
if (allField.includeFromJson()) {
428+
if (isRecord) {
429+
allField.writeFromJsonVariablesRecord(writer);
430+
} else {
431+
allField.writeFromJsonVariables(writer);
432+
}
431433
}
432434
}
433435
}

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

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

33
import javax.lang.model.element.Element;
4+
import javax.lang.model.element.ExecutableElement;
45
import javax.lang.model.element.Modifier;
56
import java.util.*;
67

@@ -24,18 +25,20 @@ final class FieldReader {
2425
addSubType(subType);
2526
final PropertyIgnoreReader ignoreReader = new PropertyIgnoreReader(element);
2627
this.unmapped = ignoreReader.unmapped();
28+
var isMethod = element instanceof ExecutableElement;
2729
this.raw = ignoreReader.raw();
2830
this.serialize = ignoreReader.serialize();
29-
this.deserialize = ignoreReader.deserialize();
31+
this.deserialize = !isMethod && ignoreReader.deserialize();
3032

3133
final var fieldName = element.getSimpleName().toString();
32-
final var publicField = element.getModifiers().contains(Modifier.PUBLIC);
33-
this.property = new FieldProperty(element.asType(), raw, unmapped, genericTypeParams, publicField, fieldName);
34-
this.propertyName =
35-
PropertyPrism.getOptionalOn(element)
36-
.map(PropertyPrism::value)
37-
.map(Util::escapeQuotes)
38-
.orElse(namingConvention.from(fieldName));
34+
final var publicField = !isMethod && element.getModifiers().contains(Modifier.PUBLIC);
35+
final var type = isMethod ? ((ExecutableElement) element).getReturnType() : element.asType();
36+
37+
this.property = new FieldProperty(type, raw, unmapped, genericTypeParams, publicField, fieldName);
38+
this.propertyName = PropertyPrism.getOptionalOn(element)
39+
.map(PropertyPrism::value)
40+
.map(Util::escapeQuotes)
41+
.orElse(namingConvention.from(fieldName));
3942

4043
this.aliases = initAliases(element);
4144
}

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

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,9 @@ final class TypeReader {
5858
if (mixInType == null) {
5959
this.mixInFields = new HashMap<>();
6060
} else {
61-
this.mixInFields =
62-
mixInType.getEnclosedElements().stream()
63-
.filter(e -> e.getKind() == ElementKind.FIELD)
64-
.collect(Collectors.toMap(e -> e.getSimpleName().toString(), e -> e));
61+
this.mixInFields = mixInType.getEnclosedElements().stream()
62+
.filter(e -> e.getKind() == ElementKind.FIELD)
63+
.collect(Collectors.toMap(e -> e.getSimpleName().toString(), e -> e));
6564
}
6665
this.namingConvention = namingConvention;
6766
this.hasJsonAnnotation = JsonPrism.isPresent(baseType) || importedJson(baseType).isPresent();
@@ -94,7 +93,7 @@ void read(TypeElement type) {
9493
readField(element, localFields);
9594
break;
9695
case METHOD:
97-
readMethod(element, type);
96+
readMethod(element, type, localFields);
9897
break;
9998
}
10099
}
@@ -129,11 +128,18 @@ private void readField(Element element, List<FieldReader> localFields) {
129128
optional = true;
130129
}
131130
if (includeField(element)) {
132-
final var frequency = frequencyMap.compute(element.getSimpleName().toString(), (k, v) -> v == null ? 0 : v + 1);
131+
final var frequency = frequency(element.getSimpleName().toString());
133132
localFields.add(new FieldReader(element, namingConvention, currentSubType, genericTypeParams, frequency));
134133
}
135134
}
136135

136+
/**
137+
* Compute and return the frequency of the key / json property name.
138+
*/
139+
private Integer frequency(String key) {
140+
return frequencyMap.compute(key, (k, v) -> v == null ? 0 : v + 1);
141+
}
142+
137143
private boolean includeField(Element element) {
138144
if (extendsThrowable) {
139145
return !element.getModifiers().contains(Modifier.TRANSIENT)
@@ -169,7 +175,7 @@ private void readConstructor(Element element, TypeElement type) {
169175
}
170176
}
171177

172-
private void readMethod(Element element, TypeElement type) {
178+
private void readMethod(Element element, TypeElement type, List<FieldReader> localFields) {
173179
ExecutableElement methodElement = (ExecutableElement) element;
174180
if (checkMethod2(methodElement)) {
175181
List<? extends VariableElement> parameters = methodElement.getParameters();
@@ -186,6 +192,19 @@ private void readMethod(Element element, TypeElement type) {
186192
}
187193
}
188194
}
195+
// for getter/accessor methods only, not setters
196+
PropertyPrism.getOptionalOn(methodElement).ifPresent(propertyPrism -> {
197+
if (!methodElement.getParameters().isEmpty()) {
198+
logError("Json.Property can only be placed on Getter Methods, but on %s", methodElement);
199+
return;
200+
}
201+
202+
// getter property as simulated read-only field with getter method
203+
final var frequency = frequency(propertyPrism.value());
204+
final var reader = new FieldReader(element, namingConvention, currentSubType, genericTypeParams, frequency);
205+
reader.getterMethod(new MethodReader(methodElement, type));
206+
localFields.add(reader);
207+
});
189208
}
190209

191210
private boolean checkMethod2(ExecutableElement methodElement) {

jsonb-generator/src/test/java/io/avaje/jsonb/generator/models/valid/TestClass.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public class TestClass {
2525

2626
private Optional<String> op;
2727

28+
@Json.Property("what_a_save!")
29+
public String what_a_save() {
30+
return "Chat Disabled";
31+
}
32+
2833
public String getAlias() {
2934
return alias;
3035
}

jsonb/src/main/java/io/avaje/jsonb/Json.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110
* }</pre>
111111
*/
112112
@Retention(CLASS)
113-
@Target(FIELD)
113+
@Target({FIELD, METHOD})
114114
@interface Property {
115115

116116
/**
@@ -138,7 +138,7 @@
138138
}
139139

140140
/**
141-
* Deprecate - migrate to {@link Json.Alias}.
141+
* Deprecated - migrate to {@link Json.Alias}.
142142
*/
143143
@Deprecated
144144
@Retention(CLASS)
@@ -188,7 +188,7 @@
188188
* }</pre>
189189
*/
190190
@Retention(CLASS)
191-
@Target(FIELD)
191+
@Target({FIELD, METHOD})
192192
@interface Unmapped {
193193

194194
}
@@ -278,7 +278,7 @@
278278
* Marks a String field as containing raw JSON content.
279279
*/
280280
@Retention(CLASS)
281-
@Target(FIELD)
281+
@Target({FIELD, METHOD})
282282
@interface Raw {
283283

284284
}

0 commit comments

Comments
 (0)