Skip to content

Commit e44299f

Browse files
Merge pull request #300 from vojtechhabarta/enums-using-to-string
Enum values using `toString()` method, internal change
2 parents abe5ddd + e27acca commit e44299f

File tree

4 files changed

+91
-78
lines changed

4 files changed

+91
-78
lines changed

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Jackson2Configuration.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,15 @@ public class Jackson2Configuration {
4545
*/
4646
public List<String> shapeConfigOverrides;
4747

48+
/**
49+
* Feature that determines standard Enum values representation:
50+
* if enabled, return value of <code>Enum.toString()</code> is used;
51+
* if disabled, return value of <code>Enum.name()</code> is used.<br>
52+
* (In <code>ObjectMapper</code> this feature is controlled using
53+
* <code>SerializationFeature.WRITE_ENUMS_USING_TO_STRING</code> and
54+
* <code>DeserializationFeature.READ_ENUMS_USING_TO_STRING</code> constants.)<br>
55+
* Default value is <code>false</code>.
56+
*/
57+
public boolean enumsUsingToString;
58+
4859
}

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Jackson2ConfigurationResolved.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class Jackson2ConfigurationResolved {
1616
public JsonAutoDetect.Visibility setterVisibility;
1717
public JsonAutoDetect.Visibility creatorVisibility;
1818
public Map<Class<?>, JsonFormat.Shape> shapeConfigOverrides;
19+
public boolean enumsUsingToString;
1920

2021
public static Jackson2ConfigurationResolved from(Jackson2Configuration configuration, ClassLoader classLoader) {
2122
final Jackson2ConfigurationResolved resolved = new Jackson2ConfigurationResolved();
@@ -26,6 +27,7 @@ public static Jackson2ConfigurationResolved from(Jackson2Configuration configura
2627
resolved.creatorVisibility = configuration.creatorVisibility;
2728
resolved.fieldVisibility = configuration.fieldVisibility;
2829
resolved.shapeConfigOverrides = resolveShapeConfigOverrides(configuration.shapeConfigOverrides, classLoader);
30+
resolved.enumsUsingToString = configuration.enumsUsingToString;
2931
return resolved;
3032
}
3133

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/Jackson2Parser.java

Lines changed: 12 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,23 @@
55
import com.fasterxml.jackson.annotation.JsonFormat;
66
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
77
import com.fasterxml.jackson.annotation.JsonIdentityReference;
8-
import com.fasterxml.jackson.annotation.JsonProperty;
98
import com.fasterxml.jackson.annotation.JsonSubTypes;
109
import com.fasterxml.jackson.annotation.JsonTypeInfo;
1110
import com.fasterxml.jackson.annotation.JsonTypeName;
1211
import com.fasterxml.jackson.annotation.JsonUnwrapped;
13-
import com.fasterxml.jackson.annotation.JsonValue;
1412
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
1513
import com.fasterxml.jackson.annotation.PropertyAccessor;
14+
import com.fasterxml.jackson.core.type.TypeReference;
1615
import com.fasterxml.jackson.databind.AnnotationIntrospector;
1716
import com.fasterxml.jackson.databind.BeanDescription;
17+
import com.fasterxml.jackson.databind.DeserializationFeature;
1818
import com.fasterxml.jackson.databind.JavaType;
1919
import com.fasterxml.jackson.databind.JsonMappingException;
2020
import com.fasterxml.jackson.databind.JsonNode;
2121
import com.fasterxml.jackson.databind.JsonSerializer;
2222
import com.fasterxml.jackson.databind.Module;
2323
import com.fasterxml.jackson.databind.ObjectMapper;
24+
import com.fasterxml.jackson.databind.SerializationFeature;
2425
import com.fasterxml.jackson.databind.cfg.MutableConfigOverride;
2526
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
2627
import com.fasterxml.jackson.databind.ser.BeanSerializer;
@@ -37,9 +38,6 @@
3738
import cz.habarta.typescript.generator.util.Predicate;
3839
import cz.habarta.typescript.generator.util.UnionType;
3940
import cz.habarta.typescript.generator.util.Utils;
40-
import java.beans.BeanInfo;
41-
import java.beans.Introspector;
42-
import java.beans.MethodDescriptor;
4341
import java.lang.annotation.Annotation;
4442
import java.lang.reflect.Field;
4543
import java.lang.reflect.Member;
@@ -72,6 +70,10 @@ public Jackson2Parser(Settings settings, TypeProcessor typeProcessor) {
7270
config.shapeConfigOverrides.entrySet()
7371
.forEach(entry -> setShapeOverride(entry.getKey(), entry.getValue()));
7472
}
73+
if (config.enumsUsingToString) {
74+
objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
75+
objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
76+
}
7577
}
7678
}
7779

@@ -386,35 +388,12 @@ private DeclarationModel parseEnumOrObjectEnum(SourceType<Class<?>> sourceClass)
386388
final Class<?> enumClass = (Class<?>) sourceClass.type;
387389

388390
try {
389-
Method valueMethod = null;
390-
Field valueField = null;
391-
392-
Field[] allEnumFields = enumClass.getDeclaredFields();
393-
List<Field> constants = Arrays.stream(allEnumFields).filter(Field::isEnumConstant).collect(Collectors.toList());
394-
395-
final BeanInfo beanInfo = Introspector.getBeanInfo(enumClass);
396-
valueMethod = Arrays.stream(beanInfo.getMethodDescriptors())
397-
.map(MethodDescriptor::getMethod)
398-
.filter(method -> method.isAnnotationPresent(JsonValue.class))
399-
.findAny().orElse(null);
400-
401-
if (valueMethod == null) {
402-
List<Field> instanceFields = Arrays.stream(allEnumFields).filter(field -> !field.isEnumConstant()).collect(Collectors.toList());
403-
valueField = instanceFields.stream()
404-
.filter(field -> field.isAnnotationPresent(JsonValue.class))
405-
.findAny().orElse(null);
406-
}
407-
408-
int index = 0;
391+
final Field[] allEnumFields = enumClass.getDeclaredFields();
392+
final List<Field> constants = Arrays.stream(allEnumFields).filter(Field::isEnumConstant).collect(Collectors.toList());
409393
for (Field constant : constants) {
410-
Object value;
411-
if (valueField != null) {
412-
value = getFieldJsonValue(constant, valueField);
413-
} else if (isNumberBased) {
414-
value = getNumberEnumValue(constant, valueMethod, index++);
415-
} else {
416-
value = getMethodEnumValue(constant, valueMethod);
417-
}
394+
constant.setAccessible(true);
395+
final String enumJson = objectMapper.writeValueAsString(constant.get(null));
396+
final Object value = objectMapper.readValue(enumJson, new TypeReference<Object>(){});
418397

419398
if (value instanceof String) {
420399
enumMembers.add(new EnumMemberModel(constant.getName(), (String) value, null));
@@ -433,44 +412,4 @@ private DeclarationModel parseEnumOrObjectEnum(SourceType<Class<?>> sourceClass)
433412
return new EnumModel(sourceClass.type, isNumberBased ? EnumKind.NumberBased : EnumKind.StringBased, enumMembers, null);
434413
}
435414

436-
private static Number getNumberEnumValue(Field field, Method valueMethod, int index) throws Exception {
437-
if (valueMethod != null) {
438-
final Object valueObject = invokeJsonValueMethod(field, valueMethod);
439-
if (valueObject instanceof Number) {
440-
return (Number) valueObject;
441-
}
442-
}
443-
return index;
444-
}
445-
446-
private static Object getMethodEnumValue(Field field, Method valueMethod) throws Exception {
447-
if (valueMethod != null) {
448-
final Object valueObject = invokeJsonValueMethod(field, valueMethod);
449-
if (valueObject instanceof String || valueObject instanceof Number) {
450-
return valueObject;
451-
}
452-
}
453-
if (field.isAnnotationPresent(JsonProperty.class)) {
454-
final JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
455-
if (!jsonProperty.value().equals(JsonProperty.USE_DEFAULT_NAME)) {
456-
return jsonProperty.value();
457-
}
458-
}
459-
return field.getName();
460-
}
461-
462-
private static Object invokeJsonValueMethod(Field field, Method valueMethod) throws ReflectiveOperationException {
463-
field.setAccessible(true);
464-
final Object constant = field.get(null);
465-
valueMethod.setAccessible(true);
466-
final Object valueObject = valueMethod.invoke(constant);
467-
return valueObject;
468-
}
469-
470-
private static Object getFieldJsonValue(Field field, Field jsonValueField) throws ReflectiveOperationException {
471-
field.setAccessible(true);
472-
final Object constant = field.get(null);
473-
jsonValueField.setAccessible(true);
474-
return jsonValueField.get(constant);
475-
}
476415
}

typescript-generator-core/src/test/java/cz/habarta/typescript/generator/EnumTest.java

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,36 @@ public void testEnumWithJsonPropertyAnnotations() {
129129
}
130130

131131
@Test
132-
public void testEnumWithJsonValueAnnotation() {
132+
public void testEnumWithJsonValueMethodAnnotation() {
133133
final Settings settings = TestUtils.settings();
134-
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(SideWithJsonValueAnnotations.class));
134+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(SideWithJsonValueMethodAnnotation.class));
135135
final String expected = (
136136
"\n" +
137-
"type SideWithJsonValueAnnotations = 'left-side' | 'right-side';\n"
137+
"type SideWithJsonValueMethodAnnotation = 'left-side' | 'right-side';\n"
138+
).replace("'", "\"");
139+
assertEquals(expected, output);
140+
}
141+
142+
@Test
143+
public void testEnumWithJsonValueFieldAnnotation() {
144+
final Settings settings = TestUtils.settings();
145+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(SideWithJsonValueFieldAnnotation.class));
146+
final String expected = (
147+
"\n" +
148+
"type SideWithJsonValueFieldAnnotation = 'left-side' | 'right-side';\n"
149+
).replace("'", "\"");
150+
assertEquals(expected, output);
151+
}
152+
153+
@Test
154+
public void testEnumUsingToString() {
155+
final Settings settings = TestUtils.settings();
156+
settings.jackson2Configuration = new Jackson2ConfigurationResolved();
157+
settings.jackson2Configuration.enumsUsingToString = true;
158+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(SideUsingToString.class));
159+
final String expected = (
160+
"\n" +
161+
"type SideUsingToString = 'toString:left-side' | 'toString:right-side';\n"
138162
).replace("'", "\"");
139163
assertEquals(expected, output);
140164
}
@@ -188,15 +212,15 @@ enum SideWithJsonPropertyAnnotations {
188212
Right
189213
}
190214

191-
enum SideWithJsonValueAnnotations {
215+
enum SideWithJsonValueMethodAnnotation {
192216
@JsonProperty("@JsonProperty ignored since @JsonValue has higher precedence")
193217
Left("left-side"),
194218
@JsonProperty("@JsonProperty ignored since @JsonValue has higher precedence")
195219
Right("right-side");
196220

197221
private final String jsonValue;
198222

199-
private SideWithJsonValueAnnotations(String jsonValue) {
223+
private SideWithJsonValueMethodAnnotation(String jsonValue) {
200224
this.jsonValue = jsonValue;
201225
}
202226

@@ -206,6 +230,43 @@ public Object getJsonValue() {
206230
}
207231
}
208232

233+
enum SideWithJsonValueFieldAnnotation {
234+
@JsonProperty("@JsonProperty ignored since @JsonValue has higher precedence")
235+
Left("left-side"),
236+
@JsonProperty("@JsonProperty ignored since @JsonValue has higher precedence")
237+
Right("right-side");
238+
239+
@JsonValue
240+
private final String jsonValue;
241+
242+
private SideWithJsonValueFieldAnnotation(String jsonValue) {
243+
this.jsonValue = jsonValue;
244+
}
245+
246+
@Override
247+
public String toString() {
248+
return "AAA " + name();
249+
}
250+
}
251+
252+
enum SideUsingToString {
253+
@JsonProperty("@JsonProperty ignored since toString() has higher precedence")
254+
Left("left-side"),
255+
@JsonProperty("@JsonProperty ignored since toString() has higher precedence")
256+
Right("right-side");
257+
258+
private final String jsonValue;
259+
260+
private SideUsingToString(String jsonValue) {
261+
this.jsonValue = jsonValue;
262+
}
263+
264+
@Override
265+
public String toString() {
266+
return "toString:" + jsonValue;
267+
}
268+
}
269+
209270
enum EmptyEnum {
210271
}
211272

0 commit comments

Comments
 (0)