@@ -300,7 +300,8 @@ private BeanModel parseBean(SourceType<Class<?>> sourceClass, List<String> class
300
300
301
301
final Pair <Class <?>, JsonTypeInfo > classWithJsonTypeInfo = Pair .of (sourceClass .type , sourceClass .type .getAnnotation (JsonTypeInfo .class ));
302
302
final Pair <Class <?>, JsonTypeInfo > parentClassWithJsonTypeInfo ;
303
- if (isTaggedUnion (classWithJsonTypeInfo )) {
303
+ final boolean isTaggedUnionParent = isTaggedUnion (classWithJsonTypeInfo );
304
+ if (isTaggedUnionParent ) {
304
305
// this is parent
305
306
final JsonTypeInfo jsonTypeInfo = classWithJsonTypeInfo .getValue2 ();
306
307
discriminantProperty = getDiscriminantPropertyName (jsonTypeInfo );
@@ -336,16 +337,11 @@ private BeanModel parseBean(SourceType<Class<?>> sourceClass, List<String> class
336
337
}
337
338
}
338
339
339
- final List <Class <?>> taggedUnionClasses ;
340
- final JsonSubTypes jsonSubTypes = sourceClass .type .getAnnotation (JsonSubTypes .class );
341
- if (jsonSubTypes != null ) {
342
- taggedUnionClasses = new ArrayList <>();
343
- for (JsonSubTypes .Type type : jsonSubTypes .value ()) {
344
- addBeanToQueue (new SourceType <>(type .value (), sourceClass .type , "<subClass>" ));
345
- taggedUnionClasses .add (type .value ());
346
- }
347
- } else {
348
- taggedUnionClasses = null ;
340
+ final List <Class <?>> taggedUnionClasses = getSubClassesFromAnnotation (sourceClass .type )
341
+ .or (() -> isTaggedUnionParent ? getSubClassesFromResolver (sourceClass .type ) : Optional .empty ())
342
+ .orElse (null );
343
+ if (taggedUnionClasses != null ) {
344
+ taggedUnionClasses .forEach (subClass -> addBeanToQueue (new SourceType <>(subClass , sourceClass .type , "<subClass>" )));
349
345
}
350
346
final Type superclass = sourceClass .type .getGenericSuperclass () == Object .class ? null : sourceClass .type .getGenericSuperclass ();
351
347
if (superclass != null ) {
@@ -444,46 +440,63 @@ private String getDiscriminantPropertyName(JsonTypeInfo jsonTypeInfo) {
444
440
}
445
441
446
442
private String getTypeName (Class <?> cls ) {
447
- final List <String > typeNames = getTypeNamesOrEmptyOrNull (cls );
448
- return typeNames != null && !typeNames .isEmpty () ? typeNames .get (0 ) : null ;
449
- }
450
-
451
- private List <String > getTypeNamesOrEmptyOrNull (Class <?> cls ) {
452
443
try {
453
444
final SerializationConfig config = objectMapper .getSerializationConfig ();
454
445
final JavaType javaType = config .constructType (cls );
455
446
final TypeSerializer typeSerializer = objectMapper .getSerializerProviderInstance ().findTypeSerializer (javaType );
456
447
final TypeIdResolver typeIdResolver = typeSerializer .getTypeIdResolver ();
457
448
if (typeIdResolver .getMechanism () == JsonTypeInfo .Id .NAME ) {
458
- final SubtypeResolver subtypeResolver = config .getSubtypeResolver ();
459
- final BeanDescription beanDescription = config .introspectClassAnnotations (cls );
460
- final AnnotatedClass annotatedClass = beanDescription .getClassInfo ();
461
- final Collection <NamedType > serializationSubtypes = subtypeResolver .collectAndResolveSubtypesByClass (config , annotatedClass );
462
- final Collection <NamedType > deserializationSubtypes = subtypeResolver .collectAndResolveSubtypesByTypeId (config , annotatedClass );
463
- final List <String > serializationTypeNames = getTypeNamesFromSubtypes (serializationSubtypes , cls ); // 0 or 1
464
- final List <String > deserializationTypeNames = getTypeNamesFromSubtypes (deserializationSubtypes , cls ); // 0 or n
465
- final LinkedHashSet <String > typeNames = Stream
466
- .concat (serializationTypeNames .stream (), deserializationTypeNames .stream ())
467
- .collect (Collectors .toCollection (LinkedHashSet ::new ));
468
- if (typeNames .isEmpty ()) {
469
- return isInterfaceOrAbstract (cls ) ? null : Utils .listFromNullable (typeIdResolver .idFromBaseType ());
449
+ final List <NamedType > subtypes = getSubtypesFromResolver (cls );
450
+ final String typeName = subtypes .stream ()
451
+ .filter (subtype -> Objects .equals (subtype .getType (), cls ))
452
+ .filter (NamedType ::hasName )
453
+ .map (NamedType ::getName )
454
+ .findFirst ()
455
+ .orElse (null );
456
+ if (typeName == null ) {
457
+ return isInterfaceOrAbstract (cls ) ? null : typeIdResolver .idFromBaseType ();
470
458
} else {
471
- return new ArrayList <>( typeNames ) ;
459
+ return typeName ;
472
460
}
473
461
} else {
474
- return Utils . listFromNullable ( typeIdResolver .idFromBaseType () );
462
+ return typeIdResolver .idFromBaseType ();
475
463
}
476
464
} catch (Exception e ) {
477
465
return null ;
478
466
}
479
467
}
480
468
481
- private static List <String > getTypeNamesFromSubtypes (Collection <NamedType > subtypes , Class <?> cls ) {
482
- return subtypes .stream ()
483
- .filter (subtype -> Objects .equals (subtype .getType (), cls ))
484
- .filter (NamedType ::hasName )
485
- .map (NamedType ::getName )
486
- .collect (Collectors .toList ());
469
+ private Optional <List <Class <?>>> getSubClassesFromAnnotation (Class <?> cls ) {
470
+ return Optional .ofNullable (cls .getAnnotation (JsonSubTypes .class ))
471
+ .map (jsonSubTypes -> Arrays .stream (jsonSubTypes .value ())
472
+ .map (jsonSubType -> jsonSubType .value ())
473
+ .collect (Collectors .toList ()));
474
+ }
475
+
476
+ private Optional <List <Class <?>>> getSubClassesFromResolver (Class <?> cls ) {
477
+ final List <NamedType > subtypes = getSubtypesFromResolver (cls );
478
+ final List <Class <?>> subClasses = subtypes .stream ()
479
+ .map (subtype -> subtype .getType ())
480
+ .filter (subClass -> !Objects .equals (subClass , cls ))
481
+ .collect (Collectors .toList ());
482
+ return subClasses .isEmpty () ? Optional .empty () : Optional .of (subClasses );
483
+ }
484
+
485
+ /**
486
+ * @return subtypes of specified class including the class itself
487
+ */
488
+ private List <NamedType > getSubtypesFromResolver (Class <?> cls ) {
489
+ final SerializationConfig config = objectMapper .getSerializationConfig ();
490
+ final SubtypeResolver subtypeResolver = config .getSubtypeResolver ();
491
+ final BeanDescription beanDescription = config .introspectClassAnnotations (cls );
492
+ final AnnotatedClass annotatedClass = beanDescription .getClassInfo ();
493
+ final Collection <NamedType > deserializationSubtypes = subtypeResolver .collectAndResolveSubtypesByTypeId (config , annotatedClass );
494
+ final Collection <NamedType > serializationSubtypes = subtypeResolver .collectAndResolveSubtypesByClass (config , annotatedClass );
495
+ final LinkedHashSet <NamedType > subtypes = Stream
496
+ .concat (deserializationSubtypes .stream (), serializationSubtypes .stream ())
497
+ .filter (namedType -> cls .isAssignableFrom (namedType .getType ())) // `SubtypeResolver` returns all types from `JsonSubTypes` annotations, not only subtypes
498
+ .collect (Collectors .toCollection (LinkedHashSet ::new ));
499
+ return new ArrayList <>(subtypes );
487
500
}
488
501
489
502
private boolean isInterfaceOrAbstract (Class <?> cls ) {
0 commit comments