micronaut-projects / micronaut-projects/micronaut-problem-json
Deserialization Does not work with Jackson and Kotlin
- Dominant language
- Java
- Stars
- 14
- Forks
- 9
- Avg merge
- 7h 59m
- Merged PRs (30d)
- 5
Description
I noticed [all tests cast the body as a map.](https://github.com/micronaut-projects/micronaut-problem-json/blob/3.3.x/test-suite-groovy/src/test/groovy/io/micronaut/problem/OutOfStockSpec.groovy#L37) So maybe this is a new feature request?
Possible issues, but curious if the databind should be included by micronaut problem at least
https://github.com/zalando/problem/issues/505
https://github.com/zalando/problem/issues/485
https://github.com/zalando/problem/issues/145
### Expected Behavior
Without any additional dependencies, I'd expect http client calls with Problem type deserialization to work without Micronaut serde (I'm not sure if micronaut serde works either because switching to it broke too many things, will need to isolate)
Default Problem response
```json
{"type":"about:blank","parameters":{},"status":404,"title":"Test Action does not exist"}
```
Constraint Violation Problem Response
```json
{"violations":[{"field":"createScheduledAction.scheduledAction.scheduledTime","message":"must be a date in the present or in the future"}],"type":"https://zalando.github.io/problem/constraint-violation","status":400,"title":"Constraint Violation"}
```
Code
```kotlin
httpClient.toBlocking().exchange(HttpRequest.GET("v1/scheduled-action/${UUID.randomUUID()}"),
argumentOf(), argumentOf()
```
```kotlin
httpClient.toBlocking().exchange(HttpRequest.GET("v1/scheduled-action/${UUID.randomUUID()}"),
argumentOf(), argumentOf()
```
> [!NOTE]
> Spring has [ConstraintViolationProblemModule](https://github.com/zalando/problem-spring-web/blob/main/problem-violations/src/main/java/org/zalando/problem/violations/ConstraintViolationProblemModule.java) in addition to the `ProblemModule()`
### Actual Behaviour
There are 2 Actual Behaviors based on an extra dependency added.
The Micronaut Object Mapper does not have `ProblemModule()` Registered. I had to include https://mvnrepository.com/artifact/org.zalando/jackson-datatype-problem/0.27.1
```kotlin
implementation("org.zalando:jackson-datatype-problem:0.27.1")
```
Default Problem response
```json
{"type":"about:blank","parameters":{},"status":404,"title":"Test Action does not exist"}
```
Constraint Violation Problem Response
```json
{"violations":[{"field":"createScheduledAction.scheduledAction.scheduledTime","message":"must be a date in the present or in the future"}],"type":"https://zalando.github.io/problem/constraint-violation","status":400,"title":"Constraint Violation"}
```
## Without the Zalando Jackson DataTyp Dependency
### Default Problem
```
08:40:06.404 [default-nioEventLoopGroup-1-3] DEBUG io.micronaut.http.client.netty.DefaultHttpClient - Error decoding HTTP error response body: Error decoding JSON stream for type [defaultProblem]: Cannot construct instance of `org.zalando.problem.StatusType` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 48]
io.micronaut.http.codec.CodecException: Error decoding JSON stream for type [defaultProblem]: Cannot construct instance of `org.zalando.problem.StatusType` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 48]
```
### ConstraintViolationThrowableProblem
```
07:16:24.457 [default-nioEventLoopGroup-1-3] DEBUG io.micronaut.http.client.netty.DefaultHttpClient - Error decoding HTTP error response body: Error decoding JSON stream for type [constraintViolationThrowableProblem]: Cannot construct instance of `org.zalando.problem.StatusType` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 4195] (through reference chain: io.micronaut.problem.violations.ConstraintViolationThrowableProblem["status"])
io.micronaut.http.codec.CodecException: Error decoding JSON stream for type [constraintViolationThrowableProblem]: Cannot construct instance of `org.zalando.problem.StatusType` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 4195] (through reference chain: io.micronaut.problem.violations.ConstraintViolationThrowableProblem["status"])
```
## With the Zalando Jackson Datatype Dependency
### Default Problem
```
08:41:06.237 [io-executor-thread-1] ERROR io.micronaut.http.server.RouteExecutor - Unexpected error occurred: Error encoding object [io.micronaut.problem.ProblemErrorResponseProcessor$IntrospectedThrowableProblemWithoutStacktrace@1278937c] to JSON: Problem with definition of [AnnotedClass io.micronaut.problem.ProblemErrorResponseProcessor$IntrospectedThrowableProblemWithoutStacktrace]: Multiple 'any-getter' methods defined ([method io.micronaut.problem.ProblemErrorResponseProcessor$IntrospectedThrowableProblemWithoutStacktrace#getProperties()] vs [method io.micronaut.problem.ProblemErrorResponseProcessor$IntrospectedThrowableProblemWithoutStacktrace#getParameters()])
io.micronaut.http.codec.CodecException: Error encoding object [io.micronaut.problem.ProblemErrorResponseProcessor$IntrospectedThrowableProblemWithoutStacktrace@1278937c] to JSON: Problem with definition of [AnnotedClass io.micronaut.problem.ProblemErrorResponseProcessor$IntrospectedThrowableProblemWithoutStacktrace]: Multiple 'any-getter' methods defined ([method io.micronaut.problem.ProblemErrorResponseProcessor$IntrospectedThrowableProblemWithoutStacktrace#getProperties()] vs [method io.micronaut.problem.ProblemErrorResponseProcessor$IntrospectedThrowableProblemWithoutStacktrace#getParameters()])
```
### ConstraintViolationThrowableProblem
```
08:41:52.372 [default-nioEventLoopGroup-1-3] DEBUG io.micronaut.http.client.netty.DefaultHttpClient - Error decoding HTTP error response body: Error decoding JSON stream for type [constraintViolationThrowableProblem]: Could not resolve type id 'https://zalando.github.io/problem/constraint-violation' as a subtype of `io.micronaut.problem.violations.ConstraintViolationThrowableProblem`: known type ids = [ConstraintViolationThrowableProblem]
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 147]
io.micronaut.http.codec.CodecException: Error decoding JSON stream for type [constraintViolationThrowableProblem]: Could not resolve type id 'https://zalando.github.io/problem/constraint-violation' as a subtype of `io.micronaut.problem.violations.ConstraintViolationThrowableProblem`: known type ids = [ConstraintViolationThrowableProblem]
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 147]
```
### Steps To Reproduce
Just so I don't have to parse out the body from the exception, I did this with `application-test.yaml`. But it does not affect the behavior, I get the same problem either way, this just seems nicer for tests as it feels more like REST assured.
```yaml
micronaut:
http:
client:
exception-on-error-status: false
```
### Environment Information
- Java 17
- Kotlin 1.9.25
```kotlin
implementation(mn.micronaut.kotlin.extension)
implementation(mn.micronaut.jackson.databind)
implementation(mn.micronaut.problem.json)
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
```
### Example Application
_No response_
### Version
4.7.2
Contributor guide
Research direction
Start by reproducing the Kotlin HTTP client calls using application-test.yaml and the listed Micronaut and Zalando dependencies. Inspect ProblemErrorResponseProcessor and the object-mapper integration around DefaultProblem and ConstraintViolationThrowableProblem. Done means both problem responses deserialize without the extra Jackson datatype dependency and server encoding no longer reports the multiple any-getter conflict.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, kotlin
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100