Skip to content

Commit a0d12f4

Browse files
committed
GH-5744: Fix PRETTY_PRINT silently ignored and add tests
Jackson 3 reads the PrettyPrinter from the ObjectWriteContext eagerly at generator construction time. The previous code created the generator in the constructor (before writer settings were available) with a prettyPrinterSupplier that always returned null, then updated the supplier in startDocument() — too late to have any effect. Fix: delay JsonGenerator creation to startDocument(), where the writer config is known, and capture the resolved PrettyPrinter directly in the ObjectWriteContext passed to createGenerator(). Add SPARQLJSONPrettyPrintTest to verify that PRETTY_PRINT=true produces indented output and PRETTY_PRINT=false produces compact output for both SPARQLResultsJSONWriter and SPARQLBooleanJSONWriter, and that both forms round-trip correctly through the parser.
1 parent f2ee746 commit a0d12f4

2 files changed

Lines changed: 135 additions & 16 deletions

File tree

core/queryresultio/sparqljson/src/main/java/org/eclipse/rdf4j/query/resultio/sparqljson/AbstractSPARQLJSONWriter.java

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.Iterator;
2020
import java.util.List;
2121
import java.util.Set;
22-
import java.util.function.Supplier;
2322

2423
import org.eclipse.rdf4j.common.io.CharSink;
2524
import org.eclipse.rdf4j.model.BNode;
@@ -78,10 +77,7 @@ abstract class AbstractSPARQLJSONWriter extends AbstractQueryResultWriter implem
7877

7978
protected boolean linksFound = false;
8079

81-
// supplier for a pretty printer, which is initialized in startDocument based on writer settings
82-
protected Supplier<PrettyPrinter> prettyPrinterSupplier = () -> null;
83-
84-
protected final JsonGenerator jg;
80+
protected JsonGenerator jg;
8581

8682
private final Writer writer;
8783

@@ -91,12 +87,6 @@ protected AbstractSPARQLJSONWriter(OutputStream out) {
9187

9288
protected AbstractSPARQLJSONWriter(Writer writer) {
9389
this.writer = writer;
94-
jg = JSON_FACTORY.createGenerator(new ObjectWriteContext.Base() {
95-
@Override
96-
public PrettyPrinter getPrettyPrinter() {
97-
return prettyPrinterSupplier.get();
98-
}
99-
}, writer);
10090
}
10191

10292
@Override
@@ -223,15 +213,22 @@ public void startDocument() throws QueryResultHandlerException {
223213
firstTupleWritten = false;
224214
linksFound = false;
225215

216+
// Create the generator here so the pretty printer setting (which requires the writer config)
217+
// is known at generator construction time, as Jackson 3 reads it from the ObjectWriteContext eagerly.
218+
final PrettyPrinter pp;
226219
if (getWriterConfig().get(BasicWriterSettings.PRETTY_PRINT)) {
227220
// SES-2011: Always use \n for consistency
228221
Indenter indenter = DefaultIndenter.SYSTEM_LINEFEED_INSTANCE;
229-
// By default Jackson does not pretty print, so enable this unless
230-
// PRETTY_PRINT setting is disabled
231-
final PrettyPrinter pp = new DefaultPrettyPrinter().withArrayIndenter(indenter)
232-
.withObjectIndenter(indenter);
233-
prettyPrinterSupplier = () -> pp;
222+
pp = new DefaultPrettyPrinter().withArrayIndenter(indenter).withObjectIndenter(indenter);
223+
} else {
224+
pp = null;
234225
}
226+
jg = JSON_FACTORY.createGenerator(new ObjectWriteContext.Base() {
227+
@Override
228+
public PrettyPrinter getPrettyPrinter() {
229+
return pp;
230+
}
231+
}, writer);
235232

236233
try {
237234
if (getWriterConfig().isSet(BasicQueryWriterSettings.JSONP_CALLBACK)) {
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
package org.eclipse.rdf4j.query.resultio.sparqljson;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
import java.io.ByteArrayOutputStream;
16+
import java.nio.charset.StandardCharsets;
17+
import java.util.List;
18+
19+
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
20+
import org.eclipse.rdf4j.model.vocabulary.RDF;
21+
import org.eclipse.rdf4j.query.impl.MapBindingSet;
22+
import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings;
23+
import org.junit.jupiter.api.Test;
24+
25+
/**
26+
* Tests that {@link BasicWriterSettings#PRETTY_PRINT} is honoured by the SPARQL JSON writers.
27+
*/
28+
public class SPARQLJSONPrettyPrintTest {
29+
30+
private static final SimpleValueFactory VF = SimpleValueFactory.getInstance();
31+
32+
// --- tuple writer helpers ------------------------------------------------
33+
34+
private String writeTupleResult(boolean prettyPrint) throws Exception {
35+
ByteArrayOutputStream out = new ByteArrayOutputStream();
36+
SPARQLResultsJSONWriter writer = new SPARQLResultsJSONWriter(out);
37+
writer.getWriterConfig().set(BasicWriterSettings.PRETTY_PRINT, prettyPrint);
38+
39+
MapBindingSet bs = new MapBindingSet();
40+
bs.addBinding("s", VF.createIRI("http://example.org/subject"));
41+
bs.addBinding("p", RDF.TYPE);
42+
bs.addBinding("o", VF.createIRI("http://example.org/object"));
43+
44+
writer.startQueryResult(List.of("s", "p", "o"));
45+
writer.handleSolution(bs);
46+
writer.endQueryResult();
47+
48+
return out.toString(StandardCharsets.UTF_8);
49+
}
50+
51+
@Test
52+
public void tupleResultPrettyPrintEnabled() throws Exception {
53+
String output = writeTupleResult(true);
54+
assertThat(output).contains("\n");
55+
}
56+
57+
@Test
58+
public void tupleResultPrettyPrintDisabled() throws Exception {
59+
String output = writeTupleResult(false);
60+
assertThat(output).doesNotContain("\n");
61+
}
62+
63+
@Test
64+
public void tupleResultPrettyPrintOutputIsParseable() throws Exception {
65+
// Sanity-check: pretty-printed output must still parse back to the same bindings.
66+
String prettyOutput = writeTupleResult(true);
67+
String compactOutput = writeTupleResult(false);
68+
69+
assertThat(prettyOutput).isNotEqualTo(compactOutput);
70+
71+
// Both outputs represent the same logical content: one binding with three variables.
72+
for (String output : List.of(prettyOutput, compactOutput)) {
73+
SPARQLResultsJSONParser parser = new SPARQLResultsJSONParser();
74+
org.eclipse.rdf4j.query.resultio.helpers.QueryResultCollector collector = new org.eclipse.rdf4j.query.resultio.helpers.QueryResultCollector();
75+
parser.setQueryResultHandler(collector);
76+
parser.parseQueryResult(
77+
new java.io.ByteArrayInputStream(output.getBytes(StandardCharsets.UTF_8)));
78+
assertThat(collector.getBindingSets()).hasSize(1);
79+
assertThat(collector.getBindingNames()).containsExactlyInAnyOrder("s", "p", "o");
80+
}
81+
}
82+
83+
// --- boolean writer helpers ----------------------------------------------
84+
85+
private String writeBooleanResult(boolean prettyPrint) throws Exception {
86+
ByteArrayOutputStream out = new ByteArrayOutputStream();
87+
SPARQLBooleanJSONWriter writer = new SPARQLBooleanJSONWriter(out);
88+
writer.getWriterConfig().set(BasicWriterSettings.PRETTY_PRINT, prettyPrint);
89+
writer.handleBoolean(true);
90+
return out.toString(StandardCharsets.UTF_8);
91+
}
92+
93+
@Test
94+
public void booleanResultPrettyPrintEnabled() throws Exception {
95+
String output = writeBooleanResult(true);
96+
assertThat(output).contains("\n");
97+
}
98+
99+
@Test
100+
public void booleanResultPrettyPrintDisabled() throws Exception {
101+
String output = writeBooleanResult(false);
102+
assertThat(output).doesNotContain("\n");
103+
}
104+
105+
@Test
106+
public void booleanResultPrettyPrintOutputIsParseable() throws Exception {
107+
// Sanity-check: pretty-printed output must still parse to the same boolean value.
108+
String prettyOutput = writeBooleanResult(true);
109+
String compactOutput = writeBooleanResult(false);
110+
111+
assertThat(prettyOutput).isNotEqualTo(compactOutput);
112+
113+
for (String output : List.of(prettyOutput, compactOutput)) {
114+
SPARQLBooleanJSONParser parser = new SPARQLBooleanJSONParser(VF);
115+
org.eclipse.rdf4j.query.resultio.helpers.QueryResultCollector collector = new org.eclipse.rdf4j.query.resultio.helpers.QueryResultCollector();
116+
parser.setQueryResultHandler(collector);
117+
parser.parseQueryResult(
118+
new java.io.ByteArrayInputStream(output.getBytes(StandardCharsets.UTF_8)));
119+
assertThat(collector.getBoolean()).isTrue();
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)