Jackson 3 Regression: `writeValue` With A `JsonGenerator` Does Not Take In Account The `PrettyPrinter`

by ADMIN 103 views

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.