Error decoding JSON stream for type [B]: Failed to narrow type when trying to deserialize subtype #447

Closed
opened 2025-04-05 12:00:17 +02:00 by gschlueter-jaconi · 1 comment
gschlueter-jaconi commented 2025-04-05 12:00:17 +02:00 (Migrated from github.com)

Using this simple schema (using inheritance without discriminator):

components:
  schemas:
    Parent:
      type: object
      properties:
        id:
          type: string
    Child:
      allOf:
        - type: object
          properties:
            name:
              type: string
        - $ref: "#/components/schemas/Parent"
paths:
  /child:
    get:
      summary: 'Returns child'
      operationId: getChild
      tags:
        - child
      responses:
        200:
          description: 'A child'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Child'

When calling the controller method in a testcase, the following exception is thrown:

Caused by: io.micronaut.http.codec.CodecException: Error decoding JSON stream for type [B]: Failed to narrow type [simple type, class com.example.polymorphismtest.Child] with annotation (value com.example.polymorphismtest.ParentDefault), from 'com.example.polymorphismtest.Child': Class `com.example.polymorphismtest.ParentDefault` not subtype of `com.example.polymorphismtest.Child`
	at io.micronaut.json.body.JsonMessageHandler.decorateRead(JsonMessageHandler.java:100)
	at io.micronaut.json.body.JsonMessageHandler.read(JsonMessageHandler.java:114)
	at io.micronaut.http.netty.body.NettyJsonHandler.read(NettyJsonHandler.java:107)
	at io.micronaut.http.client.netty.FullNettyClientHttpResponse.convertByteBuf(FullNettyClientHttpResponse.java:226)
	at io.micronaut.http.client.netty.FullNettyClientHttpResponse.lambda$getBody$0(FullNettyClientHttpResponse.java:176)
	at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
	at io.micronaut.http.client.netty.FullNettyClientHttpResponse.getBody(FullNettyClientHttpResponse.java:171)
	at io.micronaut.http.client.netty.FullNettyClientHttpResponse.<init>(FullNettyClientHttpResponse.java:105)
	at io.micronaut.http.client.netty.DefaultHttpClient.handleExchangeResponse(DefaultHttpClient.java:946)
	... 48 more
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Failed to narrow type [simple type, class com.example.polymorphismtest.Child] with annotation (value com.example.polymorphismtest.ParentDefault), from 'com.example.polymorphismtest.Child': Class `com.example.polymorphismtest.ParentDefault` not subtype of `com.example.polymorphismtest.Child`
	at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector._databindException(JacksonAnnotationIntrospector.java:1686)
	at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector.refineDeserializationType(JacksonAnnotationIntrospector.java:1323)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.modifyTypeByAnnotation(DeserializerCache.java:587)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:389)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:317)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:284)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:174)
	at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:669)
	at com.fasterxml.jackson.databind.ObjectReader._findRootDeserializer(ObjectReader.java:2406)
	at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:2099)
	at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1249)
	at io.micronaut.jackson.databind.JacksonDatabindMapper.readValue(JacksonDatabindMapper.java:221)
	at io.micronaut.json.body.JsonMessageHandler.read(JsonMessageHandler.java:112)
	... 55 more
Caused by: java.lang.IllegalArgumentException: Class `com.example.polymorphismtest.ParentDefault` not subtype of `com.example.polymorphismtest.Child`
	at com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:473)
	at com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:435)
	at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector.refineDeserializationType(JacksonAnnotationIntrospector.java:1321)
	... 66 more

The reason is that all classes implementing the interface Parent are deserialized as ParentDefault (due to the JsonDeserialize(as = ParentDefault.class) annotation on the Parent interface). This also applies to the Child class since annotations of parent interfaces are taken into consideration as well.

I created a simple reproducer project here: https://github.com/gschlueter-jaconi/generator-polymorphism-demo

The ChildControllerTest there will fail while the ChildControllerWorkingTest will succeed. This test contains a potential solution by adding the following annotation to the Child model explicitly: @JsonDeserialize(as = Child.class). This could also be done by the generator (if no discriminator is used).

Using this simple schema (using inheritance without discriminator): ``` components: schemas: Parent: type: object properties: id: type: string Child: allOf: - type: object properties: name: type: string - $ref: "#/components/schemas/Parent" paths: /child: get: summary: 'Returns child' operationId: getChild tags: - child responses: 200: description: 'A child' content: application/json: schema: $ref: '#/components/schemas/Child' ``` When calling the controller method in a testcase, the following exception is thrown: ``` Caused by: io.micronaut.http.codec.CodecException: Error decoding JSON stream for type [B]: Failed to narrow type [simple type, class com.example.polymorphismtest.Child] with annotation (value com.example.polymorphismtest.ParentDefault), from 'com.example.polymorphismtest.Child': Class `com.example.polymorphismtest.ParentDefault` not subtype of `com.example.polymorphismtest.Child` at io.micronaut.json.body.JsonMessageHandler.decorateRead(JsonMessageHandler.java:100) at io.micronaut.json.body.JsonMessageHandler.read(JsonMessageHandler.java:114) at io.micronaut.http.netty.body.NettyJsonHandler.read(NettyJsonHandler.java:107) at io.micronaut.http.client.netty.FullNettyClientHttpResponse.convertByteBuf(FullNettyClientHttpResponse.java:226) at io.micronaut.http.client.netty.FullNettyClientHttpResponse.lambda$getBody$0(FullNettyClientHttpResponse.java:176) at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) at io.micronaut.http.client.netty.FullNettyClientHttpResponse.getBody(FullNettyClientHttpResponse.java:171) at io.micronaut.http.client.netty.FullNettyClientHttpResponse.<init>(FullNettyClientHttpResponse.java:105) at io.micronaut.http.client.netty.DefaultHttpClient.handleExchangeResponse(DefaultHttpClient.java:946) ... 48 more Caused by: com.fasterxml.jackson.databind.JsonMappingException: Failed to narrow type [simple type, class com.example.polymorphismtest.Child] with annotation (value com.example.polymorphismtest.ParentDefault), from 'com.example.polymorphismtest.Child': Class `com.example.polymorphismtest.ParentDefault` not subtype of `com.example.polymorphismtest.Child` at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector._databindException(JacksonAnnotationIntrospector.java:1686) at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector.refineDeserializationType(JacksonAnnotationIntrospector.java:1323) at com.fasterxml.jackson.databind.deser.DeserializerCache.modifyTypeByAnnotation(DeserializerCache.java:587) at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:389) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:317) at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:284) at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:174) at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:669) at com.fasterxml.jackson.databind.ObjectReader._findRootDeserializer(ObjectReader.java:2406) at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:2099) at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1249) at io.micronaut.jackson.databind.JacksonDatabindMapper.readValue(JacksonDatabindMapper.java:221) at io.micronaut.json.body.JsonMessageHandler.read(JsonMessageHandler.java:112) ... 55 more Caused by: java.lang.IllegalArgumentException: Class `com.example.polymorphismtest.ParentDefault` not subtype of `com.example.polymorphismtest.Child` at com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:473) at com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:435) at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector.refineDeserializationType(JacksonAnnotationIntrospector.java:1321) ... 66 more ``` The reason is that all classes implementing the interface `Parent` are deserialized as `ParentDefault` (due to the `JsonDeserialize(as = ParentDefault.class)` annotation on the `Parent` interface). This also applies to the `Child` class since annotations of parent interfaces are taken into consideration as well. I created a simple reproducer project here: https://github.com/gschlueter-jaconi/generator-polymorphism-demo The `ChildControllerTest` there will fail while the `ChildControllerWorkingTest` will succeed. This test contains a potential solution by adding the following annotation to the `Child` model explicitly: `@JsonDeserialize(as = Child.class)`. This could also be done by the generator (if no discriminator is used).
sschnabe commented 2025-07-15 21:48:42 +02:00 (Migrated from github.com)

Released as 4.4.3

Released as [4.4.3](https://github.com/kokuwaio/micronaut-openapi-codegen/releases/tag/4.4.3)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
kokuwaio/micronaut-openapi-codegen#447
No description provided.