micronaut-projects / micronaut-projects/micronaut-serialization

Getting error in serializing Stream<T> return type from http controller's method

Open
#770 2 comments 0 reactions 0 assignees View on GitHub
type: bug
Dominant language
Java
Stars
36
Forks
29
Avg merge
1d 21h
Merged PRs (30d)
29

Description

### Expected Behavior

For the following record,
```
@Introspected
@Serdeable
public record Employee(String firstName, String lastName) {
}
```
and controller
```
@Controller("/api/employees")
public class EmployeeController {

private static final List data = List.of(new Employee("First","Last"));

@Get
public Stream getAll(){
return data.stream();
}
}
```
The getAll method (called on HTTP GET /api/employees) should return a single element array of following,

```
[
{"firstName": "First", "lastName":"Last"}
]

```

### Actual Behaviour

Starting 4.3.0, the same method is failing with following exception,
```
Caused by: io.micronaut.serde.exceptions.SerdeException: Cannot serialize raw stream
at io.micronaut.serde.support.serializers.StreamSerializer.createSpecific(StreamSerializer.java:37)
at io.micronaut.serde.jackson.JacksonJsonMapper.writeValue(JacksonJsonMapper.java:174)
at io.micronaut.serde.jackson.JacksonJsonMapper.writeValue0(JacksonJsonMapper.java:167)
at io.micronaut.serde.jackson.JacksonJsonMapper.writeValue0(JacksonJsonMapper.java:162)
at io.micronaut.serde.jackson.JacksonJsonMapper.writeValue(JacksonJsonMapper.java:248)
at io.micronaut.http.netty.body.NettyJsonHandler.writeTo(NettyJsonHandler.java:161)
```
and on client side following is received,
```
{
"_links": {
"self": [
{
"href": "/api/employees",
"templated": false
}
]
},
"_embedded": {
"errors": [
{
"message": "Internal Server Error: Error encoding object [java.util.stream.ReferencePipeline$Head@70693eaf] to JSON: Cannot serialize raw stream"
}
]
},
"message": "Internal Server Error"
}
```

After some debugging, I concluded that this has probably got introduced due to commit : ([Make NettyJsonHandler implement NettyBodyWriter](https://github.com/micronaut-projects/micronaut-core/commit/34bf0eae167142a90df775a06dda8f10a2ab624d))

Due to this change, the wrap method of RoutingInBoundHandler ,
```
NettyBodyWriter wrap(MessageBodyWriter closure) {
if (closure instanceof NettyBodyWriter nettyClosure) {
return nettyClosure;
} else {
return new CompatNettyWriteClosure<>(closure);
}
}
```
started returning the same instance (as it now inherits from NettyBodyWriter) instead of CompatNettyWriteClosure.

I debugged this flow in 4.2.4 and 4.3.2 and in case of 4.2.4, since it creates CompatNettyWriteClosure, it ultimately results in to correct interpretation of Argument type in StreamSerializer's createSpecific method.

So in case of 4.2.4, Argument instance comes pointing to Stream,
![image](https://github.com/micronaut-projects/micronaut-core/assets/22400303/f57f735b-080a-44eb-8861-ab026fcac8ba)

whereas in case of 4.3.2, it points to Head,

![image](https://github.com/micronaut-projects/micronaut-core/assets/22400303/10c5af97-a918-4302-8f07-bb183ed37a71)

Looks like unintentional effect.

If my understanding is incorrect, please let me know.

### Steps To Reproduce

_No response_

### Environment Information

Micronaut version 4.3.2
Oracle JDK 21
Windows 11

### Example Application

_No response_

### Version

4.3.2

Contributor guide

Open the contributing guide

Research direction

Start with StreamSerializer.createSpecific, JacksonJsonMapper.writeValue, NettyJsonHandler, and RoutingInBoundHandler.wrap, comparing the 4.2.4 and 4.3.2 behavior described in the issue. Reproduce the Employee controller case and verify that an HTTP GET returning Stream produces the expected JSON array without the raw-stream error.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.