Jackson 3 Regression: `writeValue` With A `JsonGenerator` Does Not Take In Account The `PrettyPrinter`
Introduction
In this article, we will discuss a regression issue in Jackson 3, specifically with the writeValue
method when using a JsonGenerator
and a PrettyPrinter
. We will explore the issue, provide a reproduction, and discuss the expected behavior.
Search before asking
Before diving into the issue, it's essential to check if similar problems have been reported in the Jackson issues repository. In this case, we have searched the issues and found nothing similar.
Describe the bug
When calling mapper.writer().with(prettyPrinter).writeValue(generator, bean)
, the pretty printer is not taken into account. This is in contrast to passing an OutputStream
instead of a JsonGenerator
, where the pretty printer is respected.
Version Information
The issue is observed in version 3.0.0-rc3 of Jackson.
Reproduction
To reproduce the issue, we will create a test class with a PrettyPrintBean
and a test method prettyPrintWithSse
. We will use the MockHttpOutputMessage
class to simulate an HTTP output message.
public static class PrettyPrintBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
void prettyPrintWithSse() throws IOException {
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
ObjectMapper mapper = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
PrettyPrintBean bean = new PrettyPrintBean();
bean.setName("Jason");
JsonGenerator generator = mapper.createGenerator(outputMessage.getBody(), JsonEncoding.UTF8);
mapper.writer().with(prettyPrinter).writeValue(generator, bean);
String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8);
assertThat(result).isEqualTo("{\ndata: \"name\" : \"Jason\"\ndata:}");
}
However, when we use an OutputStream
instead of a JsonGenerator
, the test succeeds:
@Test
void prettyPrintWithSse() throws IOException {
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
ObjectMapper mapper = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
PrettyPrintBean bean = new PrettyPrintBean();
bean.setName("Jason");
mapper.writer().with(prettyPrinter).writeValue(outputMessage.getBody(), bean);
String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8);
assertThat(result).isEqualTo("{\ndata: \"name\" : \"Jason\"\ndata:}");
}
Expected behavior
The expected behavior is that mapper.writer().with(prettyPrinter).writeValue(generator, bean)
should take into account the PrettyPrinter
configured the ObjectWriter
level, just like in Jackson 2.x.
Additional context
This issue was working with Jackson 2.x and is used by Spring Framework SSE support. The regression in Jackson 3 is causing issues in applications that rely on this functionality.
Conclusion
Q: What is the issue with Jackson 3's writeValue
method?
A: The issue is that the writeValue
method with a JsonGenerator
does not take into account the PrettyPrinter
configured at the ObjectWriter
level, unlike in Jackson 2.x.
Q: What is the expected behavior?
A: The expected behavior is that mapper.writer().with(prettyPrinter).writeValue(generator, bean)
should take into account the PrettyPrinter
configured at the ObjectWriter
level, just like in Jackson 2.x.
Q: Why is this issue important?
A: This issue is important because it affects applications that rely on the PrettyPrinter
functionality, particularly those using Spring Framework SSE support. The regression in Jackson 3 is causing problems in these applications.
Q: How can I reproduce the issue?
A: To reproduce the issue, you can create a test class with a PrettyPrintBean
and a test method prettyPrintWithSse
. You can use the MockHttpOutputMessage
class to simulate an HTTP output message.
public static class PrettyPrintBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
void prettyPrintWithSse() throws IOException {
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
ObjectMapper mapper = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
PrettyPrintBean bean = new PrettyPrintBean();
bean.setName("Jason");
JsonGenerator generator = mapper.createGenerator(outputMessage.getBody(), JsonEncoding.UTF8);
mapper.writer().with(prettyPrinter).writeValue(generator, bean);
String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8);
assertThat(result).isEqualTo("{\ndata: \"name\" : \"Jason\"\ndata:}");
}
Q: What is the difference between using a JsonGenerator
and an OutputStream
?
A: When using a JsonGenerator
, the PrettyPrinter
is not taken into account, whereas when using an OutputStream
, the PrettyPrinter
is respected.
Q: How can I work around this issue?
A: One way to work around this issue is to use an OutputStream
instead of a JsonGenerator
. However, this may not be feasible in all cases, particularly when working with streaming APIs.
Q: Is this issue fixed in Jackson 3?
A: Unfortunately, this issue is not fixed in Jackson 3. However, the Jackson team is aware of the issue and may address it in future releases.
Q: What can I do to help resolve this issue?
A: If you are experiencing this issue, you can report it to the Jackson team and provide a case. Additionally, you can contribute to the Jackson project by providing a patch or helping to fix the issue.