Skip to content

support multiple names for jackson subtype #798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,10 @@ private <T> TsBeanModel processBean(SymbolTable symbolTable, Model model, Map<Ty
isTaggedUnion = false;
isDisciminantProperty = false;
}
if (descendant.getDiscriminantLiteral() != null) {
literals.add(new TsType.StringLiteralType(descendant.getDiscriminantLiteral()));
if (descendant.getDiscriminantLiterals() != null && !descendant.getDiscriminantLiterals().isEmpty()) {
for (String discriminantLiteral : descendant.getDiscriminantLiterals()) {
literals.add(new TsType.StringLiteralType(discriminantLiteral));
}
}
}
final List<BeanModel> descendants = selfAndDescendants.subList(1, selfAndDescendants.size());
Expand Down Expand Up @@ -339,7 +341,7 @@ private <T> TsBeanModel processBean(SymbolTable symbolTable, Model model, Map<Ty
/*methods*/ null,
bean.getComments());
return isTaggedUnion
? tsBean.withTaggedUnion(bean.getTaggedUnionClasses(), bean.getDiscriminantProperty(), bean.getDiscriminantLiteral())
? tsBean.withTaggedUnion(bean.getTaggedUnionClasses(), bean.getDiscriminantProperty(), bean.getDiscriminantLiterals())
: tsBean;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,9 @@ private void emitTsSwitchStatement(TsSwitchStatement switchStatement) {
writeIndentedLine("switch (" + switchStatement.getExpression().format(settings) + ") {");
indent++;
for (TsSwitchCaseClause caseClause : switchStatement.getCaseClauses()) {
writeIndentedLine("case " + caseClause.getExpression().format(settings) + ":");
for (TsExpression expression : caseClause.getExpressions()) {
writeIndentedLine("case " + expression.format(settings) + ":");
}
indent++;
emitStatements(caseClause.getStatements());
indent--;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class TsBeanModel extends TsDeclarationModel {
private final List<TsType> implementsList;
private final List<Class<?>> taggedUnionClasses;
private final String discriminantProperty;
private final String discriminantLiteral;
private final List<String> discriminantLiterals;
private final TsAliasModel taggedUnionAlias;
private final List<TsPropertyModel> properties;
private final TsConstructorModel constructor;
Expand Down Expand Up @@ -52,7 +52,7 @@ private TsBeanModel(
List<TsType> implementsList,
List<Class<?>> taggedUnionClasses,
String discriminantProperty,
String discriminantLiteral,
List<String> discriminantLiterals,
TsAliasModel taggedUnionAlias,
List<TsPropertyModel> properties,
TsConstructorModel constructor,
Expand All @@ -67,7 +67,7 @@ private TsBeanModel(
this.implementsList = Utils.listFromNullable(implementsList);
this.taggedUnionClasses = Utils.listFromNullable(taggedUnionClasses);
this.discriminantProperty = discriminantProperty;
this.discriminantLiteral = discriminantLiteral;
this.discriminantLiterals = discriminantLiterals;
this.taggedUnionAlias = taggedUnionAlias;
this.properties = Utils.listFromNullable(properties);
this.constructor = constructor;
Expand All @@ -83,7 +83,7 @@ public List<TsDecorator> getDecorators() {
}

public TsBeanModel withDecorators(List<TsDecorator> decorators) {
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiteral, taggedUnionAlias, properties, constructor, methods, comments);
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiterals, taggedUnionAlias, properties, constructor, methods, comments);
}

public List<TsType.GenericVariableType> getTypeParameters() {
Expand Down Expand Up @@ -117,44 +117,44 @@ public String getDiscriminantProperty() {
return discriminantProperty;
}

public String getDiscriminantLiteral() {
return discriminantLiteral;
public List<String> getDiscriminantLiterals() {
return discriminantLiterals;
}

public TsBeanModel withTaggedUnion(List<Class<?>> taggedUnionClasses, String discriminantProperty, String discriminantLiteral) {
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiteral, taggedUnionAlias, properties, constructor, methods, comments);
public TsBeanModel withTaggedUnion(List<Class<?>> taggedUnionClasses, String discriminantProperty, List<String> discriminantLiterals) {
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiterals, taggedUnionAlias, properties, constructor, methods, comments);
}

public TsAliasModel getTaggedUnionAlias() {
return taggedUnionAlias;
}

public TsBeanModel withTaggedUnionAlias(TsAliasModel taggedUnionAlias) {
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiteral, taggedUnionAlias, properties, constructor, methods, comments);
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiterals, taggedUnionAlias, properties, constructor, methods, comments);
}

public List<TsPropertyModel> getProperties() {
return properties;
}

public TsBeanModel withProperties(List<TsPropertyModel> properties) {
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiteral, taggedUnionAlias, properties, constructor, methods, comments);
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiterals, taggedUnionAlias, properties, constructor, methods, comments);
}

public TsConstructorModel getConstructor() {
return constructor;
}

public TsBeanModel withConstructor(TsConstructorModel constructor) {
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiteral, taggedUnionAlias, properties, constructor, methods, comments);
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiterals, taggedUnionAlias, properties, constructor, methods, comments);
}

public List<TsMethodModel> getMethods() {
return methods;
}

public TsBeanModel withMethods(List<TsMethodModel> methods) {
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiteral, taggedUnionAlias, properties, constructor, methods, comments);
return new TsBeanModel(origin, category, isClass, decorators, name, typeParameters, parent, extendsList, implementsList, taggedUnionClasses, discriminantProperty, discriminantLiterals, taggedUnionAlias, properties, constructor, methods, comments);
}

public boolean isJaxrsApplicationClientBean() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@

public class TsSwitchCaseClause extends TsStatement {

private final TsExpression expression;
private final List<TsExpression> expressions;
private final List<TsStatement> statements;

public TsSwitchCaseClause(TsExpression expression, List<TsStatement> statements) {
this.expression = expression;
public TsSwitchCaseClause(List<TsExpression> expressions, List<TsStatement> statements) {
this.expressions = expressions;
this.statements = statements;
}

public TsExpression getExpression() {
return expression;
public List<TsExpression> getExpressions() {
return expressions;
}

public List<TsStatement> getStatements() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


public class JsonDeserializationExtension extends Extension {
Expand Down Expand Up @@ -321,7 +322,7 @@ private static TsMethodModel createDeserializationMethodForTaggedUnion(SymbolTab
for (Class<?> cls : bean.getTaggedUnionClasses()) {
final TsBeanModel tuBean = tsModel.getBean(cls);
caseClauses.add(new TsSwitchCaseClause(
new TsStringLiteral(tuBean.getDiscriminantLiteral()),
tuBean.getDiscriminantLiterals().stream().map(TsStringLiteral::new).collect(Collectors.toList()),
Arrays.<TsStatement>asList(new TsReturnStatement(
new TsCallExpression(
new TsMemberExpression(new TsTypeReferenceExpression(new TsType.ReferenceType(symbolTable.getSymbol(cls))), "fromData"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ public class BeanModel extends DeclarationModel {
private final Type parent;
private final List<Class<?>> taggedUnionClasses;
private final String discriminantProperty;
private final String discriminantLiteral;
private final List<String> discriminantLiterals;
private final List<Type> interfaces;
private final List<PropertyModel> properties;

public BeanModel(Class<?> origin, Type parent, List<Class<?>> taggedUnionClasses, String discriminantProperty, String discriminantLiteral, List<Type> interfaces, List<PropertyModel> properties, List<String> comments) {
public BeanModel(Class<?> origin, Type parent, List<Class<?>> taggedUnionClasses, String discriminantProperty, List<String> discriminantLiterals, List<Type> interfaces, List<PropertyModel> properties, List<String> comments) {
super(origin, comments);
this.parent = parent;
this.taggedUnionClasses = taggedUnionClasses;
this.discriminantProperty = discriminantProperty;
this.discriminantLiteral = discriminantLiteral;
this.discriminantLiterals = discriminantLiterals;
this.interfaces = Utils.listFromNullable(interfaces);
this.properties = properties;
}
Expand All @@ -39,8 +39,8 @@ public String getDiscriminantProperty() {
return discriminantProperty;
}

public String getDiscriminantLiteral() {
return discriminantLiteral;
public List<String> getDiscriminantLiterals() {
return discriminantLiterals;
}

public List<Type> getInterfaces() {
Expand Down Expand Up @@ -68,12 +68,12 @@ public PropertyModel getProperty(String name) {
}

public BeanModel withProperties(List<PropertyModel> properties) {
return new BeanModel(origin, parent, taggedUnionClasses, discriminantProperty, discriminantLiteral, interfaces, properties, comments);
return new BeanModel(origin, parent, taggedUnionClasses, discriminantProperty, discriminantLiterals, interfaces, properties, comments);
}

@Override
public BeanModel withComments(List<String> comments) {
return new BeanModel(origin, parent, taggedUnionClasses, discriminantProperty, discriminantLiteral, interfaces, properties, comments);
return new BeanModel(origin, parent, taggedUnionClasses, discriminantProperty, discriminantLiterals, interfaces, properties, comments);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ private BeanModel parseBean(SourceType<Class<?>> sourceClass, List<String> class

final String discriminantProperty;
final boolean syntheticDiscriminantProperty;
final String discriminantLiteral;
final List<String> discriminantLiterals;

final Pair<Class<?>, JsonTypeInfo> classWithJsonTypeInfo = Pair.of(sourceClass.type, sourceClass.type.getAnnotation(JsonTypeInfo.class));
final Pair<Class<?>, JsonTypeInfo> parentClassWithJsonTypeInfo;
Expand All @@ -305,18 +305,18 @@ private BeanModel parseBean(SourceType<Class<?>> sourceClass, List<String> class
final JsonTypeInfo jsonTypeInfo = classWithJsonTypeInfo.getValue2();
discriminantProperty = getDiscriminantPropertyName(jsonTypeInfo);
syntheticDiscriminantProperty = isDiscriminantPropertySynthetic(jsonTypeInfo);
discriminantLiteral = isInterfaceOrAbstract(sourceClass.type) ? null : getTypeName(sourceClass.type);
discriminantLiterals = isInterfaceOrAbstract(sourceClass.type) ? null : getTypeNames(sourceClass.type);
} else if (isTaggedUnion(parentClassWithJsonTypeInfo = getAnnotationRecursive(sourceClass.type, JsonTypeInfo.class))) {
// this is child class
final JsonTypeInfo parentJsonTypeInfo = parentClassWithJsonTypeInfo.getValue2();
discriminantProperty = getDiscriminantPropertyName(parentJsonTypeInfo);
syntheticDiscriminantProperty = isDiscriminantPropertySynthetic(parentJsonTypeInfo);
discriminantLiteral = getTypeName(sourceClass.type);
discriminantLiterals = getTypeNames(sourceClass.type);
} else {
// not part of explicit hierarchy
discriminantProperty = null;
syntheticDiscriminantProperty = false;
discriminantLiteral = null;
discriminantLiterals = null;
}

if (discriminantProperty != null) {
Expand Down Expand Up @@ -355,7 +355,7 @@ private BeanModel parseBean(SourceType<Class<?>> sourceClass, List<String> class
for (Type aInterface : interfaces) {
addBeanToQueue(new SourceType<>(aInterface, sourceClass.type, "<interface>"));
}
return new BeanModel(sourceClass.type, superclass, taggedUnionClasses, discriminantProperty, discriminantLiteral, interfaces, properties, classComments);
return new BeanModel(sourceClass.type, superclass, taggedUnionClasses, discriminantProperty, discriminantLiterals, interfaces, properties, classComments);
}

private static Integer getCreatorIndex(BeanProperty beanProperty) {
Expand Down Expand Up @@ -443,12 +443,7 @@ private String getDiscriminantPropertyName(JsonTypeInfo jsonTypeInfo) {
: jsonTypeInfo.property();
}

private String getTypeName(Class<?> cls) {
final List<String> typeNames = getTypeNamesOrEmptyOrNull(cls);
return typeNames != null && !typeNames.isEmpty() ? typeNames.get(0) : null;
}

private List<String> getTypeNamesOrEmptyOrNull(Class<?> cls) {
private List<String> getTypeNames(Class<?> cls) {
try {
final SerializationConfig config = objectMapper.getSerializationConfig();
final JavaType javaType = config.constructType(cls);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,18 @@ public void testTaggedUnion() {
Assertions.assertNull(bean2.getTaggedUnionClasses());
Assertions.assertNull(bean3.getTaggedUnionClasses());
Assertions.assertEquals("kind", bean0.getDiscriminantProperty());
Assertions.assertEquals("explicit-name1", bean1.getDiscriminantLiteral());
Assertions.assertEquals("SubType2", bean2.getDiscriminantLiteral());
Assertions.assertEquals("Jackson2ParserTest$SubTypeDiscriminatedByName3", bean3.getDiscriminantLiteral());
Assertions.assertEquals("Jackson2ParserTest$SubTypeDiscriminatedByName4", bean4.getDiscriminantLiteral());
Assertions.assertEquals(Arrays.asList("explicit-name1", "SubType1"), bean1.getDiscriminantLiterals());
Assertions.assertEquals(Arrays.asList("SubType2"), bean2.getDiscriminantLiterals());
Assertions.assertEquals(Arrays.asList("Jackson2ParserTest$SubTypeDiscriminatedByName3"), bean3.getDiscriminantLiterals());
Assertions.assertEquals(Arrays.asList("Jackson2ParserTest$SubTypeDiscriminatedByName4"), bean4.getDiscriminantLiterals());
}

@Test
public void testRegisteredSubtypeName() {
final Jackson2Parser jacksonParser = getJackson2Parser();
final Model model = jacksonParser.parseModel(SubTypeDiscriminatedByName5.class);
final BeanModel bean5 = model.getBean(SubTypeDiscriminatedByName5.class);
Assertions.assertEquals("NamedByModule", bean5.getDiscriminantLiteral());
Assertions.assertEquals(Arrays.asList("NamedByModule"), bean5.getDiscriminantLiterals());
}

static Jackson2Parser getJackson2Parser() {
Expand All @@ -132,7 +132,7 @@ public class InheritedClass {

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "kind")
@JsonSubTypes({
@JsonSubTypes.Type(value = SubTypeDiscriminatedByName1.class, name = "SubType1"), // value from @JsonTypeName is used
@JsonSubTypes.Type(value = SubTypeDiscriminatedByName1.class, name = "SubType1"),
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about this one

this is probably a breaking change, since @JsonTypeName would no longer have priority over @JsonSubTypes.Type, and all typenames would be included

@JsonSubTypes.Type(value = SubTypeDiscriminatedByName2.class, name = "SubType2"),
@JsonSubTypes.Type(value = SubTypeDiscriminatedByName3.class),
@JsonSubTypes.Type(value = SubTypeDiscriminatedByName4.class),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public static class Order {
@JsonSubTypes({
@JsonSubTypes.Type(Square.class),
@JsonSubTypes.Type(Rectangle.class),
@JsonSubTypes.Type(Circle.class),
@JsonSubTypes.Type(value = Circle.class, name = "circle2"),
})
private abstract static class Shape {
public ShapeMetadata metadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,4 +689,44 @@ public void testIntermediateUnions() {
Assertions.assertEquals(expected.trim(), output.trim());
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = LongDto.class, names = {"Long1", "Long2"}),
@JsonSubTypes.Type(value = BoolDto.class, names = {"Bool1", "Bool2"}),
})
private static abstract class AbstractDto {
public String type;
}

private static class LongDto extends AbstractDto {
public long value;
}

private static class BoolDto extends AbstractDto {
public boolean value;
}

@Test
public void testMultipleDiscriminantLiterals() {
final Settings settings = TestUtils.settings();
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(AbstractDto.class));
final String expected = ""
+ "interface AbstractDto {\n"
+ " type: \"Long1\" | \"Long2\" | \"Bool1\" | \"Bool2\";\n"
+ "}\n"
+ "\n"
+ "interface LongDto extends AbstractDto {\n"
+ " type: \"Long1\" | \"Long2\";\n"
+ " value: number;\n"
+ "}\n"
+ "\n"
+ "interface BoolDto extends AbstractDto {\n"
+ " type: \"Bool1\" | \"Bool2\";\n"
+ " value: boolean;\n"
+ "}\n"
+ "\n"
+ "type AbstractDtoUnion = LongDto | BoolDto;\n"
+ "";
Assertions.assertEquals(expected.trim(), output.trim());
}
}
Loading