From 0db5ae8c5e002465320ff075d5d08b22bc41c448 Mon Sep 17 00:00:00 2001 From: Aaron Digulla Date: Tue, 6 Jun 2023 22:51:47 +0200 Subject: [PATCH 1/4] Allow to configure the space before the colon in DefaultPrettyPrinter. --- .../core/util/DefaultPrettyPrinter.java | 2 +- .../jackson/core/util/Separators.java | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java b/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java index 98f650a4a1..85b709a7b5 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java +++ b/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java @@ -255,7 +255,7 @@ protected DefaultPrettyPrinter _withSpaces(boolean state) */ public DefaultPrettyPrinter withSeparators(Separators separators) { _separators = separators; - _objectFieldValueSeparatorWithSpaces = " " + separators.getObjectFieldValueSeparator() + " "; + _objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator(); return this; } diff --git a/src/main/java/com/fasterxml/jackson/core/util/Separators.java b/src/main/java/com/fasterxml/jackson/core/util/Separators.java index 9d63ab3d8b..6c9731db0e 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/Separators.java +++ b/src/main/java/com/fasterxml/jackson/core/util/Separators.java @@ -15,7 +15,9 @@ public class Separators implements Serializable { private static final long serialVersionUID = 1; - private final char objectFieldValueSeparator; + private static final String DEFAULT_OBJECT_FIELD_VALUE_SEPARATOR = " : "; + + private final String objectFieldValueSeparator; private final char objectEntrySeparator; private final char arrayValueSeparator; @@ -24,20 +26,46 @@ public static Separators createDefaultInstance() { } public Separators() { - this(':', ',', ','); + this(DEFAULT_OBJECT_FIELD_VALUE_SEPARATOR, ',', ','); } public Separators(char objectFieldValueSeparator, char objectEntrySeparator, char arrayValueSeparator) { + this(migrate(objectFieldValueSeparator), objectEntrySeparator, arrayValueSeparator); + } + + public Separators(String objectFieldValueSeparator, + char objectEntrySeparator, char arrayValueSeparator) { + if (objectFieldValueSeparator == null) { + objectFieldValueSeparator = DEFAULT_OBJECT_FIELD_VALUE_SEPARATOR; + } else if (objectFieldValueSeparator.trim().isEmpty()) { + throw new IllegalArgumentException("objectFieldValueSeparator can't be blank"); + } this.objectFieldValueSeparator = objectFieldValueSeparator; this.objectEntrySeparator = objectEntrySeparator; this.arrayValueSeparator = arrayValueSeparator; } + private static String migrate(char objectFieldValueSeparator) { + return " " + objectFieldValueSeparator + " "; + } + public Separators withObjectFieldValueSeparator(char sep) { - return (objectFieldValueSeparator == sep) ? this + return (objectFieldValueSeparator == migrate(sep)) ? this : new Separators(sep, objectEntrySeparator, arrayValueSeparator); } + + public Separators withObjectFieldValueSeparator(String sep) { + if (sep == null) { + sep = DEFAULT_OBJECT_FIELD_VALUE_SEPARATOR; + } + return (objectFieldValueSeparator.equals(sep)) ? this + : new Separators(sep, objectEntrySeparator, arrayValueSeparator); + } + + public Separators withCompactObjectFieldValueSeparator() { + return withObjectFieldValueSeparator(": "); + } public Separators withObjectEntrySeparator(char sep) { return (objectEntrySeparator == sep) ? this @@ -49,7 +77,7 @@ public Separators withArrayValueSeparator(char sep) { : new Separators(objectFieldValueSeparator, objectEntrySeparator, sep); } - public char getObjectFieldValueSeparator() { + public String getObjectFieldValueSeparator() { return objectFieldValueSeparator; } From 4450b574a3f95a0b0b97bd4e9e9c55cc1fe793bd Mon Sep 17 00:00:00 2001 From: Aaron Digulla Date: Wed, 7 Jun 2023 22:24:05 +0200 Subject: [PATCH 2/4] Second attempt to move the spacing config to Separators. This one is much cleaner than the first attempt but we have to decide which PrettyPrinter config to break: Either the default Separators is with spaces around the colon, then MinimalPrettyPrinter has to modify the default. Or the default is without spaces, then DefaultPrettyPrinter has to modify it. Of course, this affects anyone who tries to build their own PrettyPrinter using the default Separators. --- .../core/util/DefaultPrettyPrinter.java | 7 +- .../core/util/MinimalPrettyPrinter.java | 3 +- .../jackson/core/util/Separators.java | 114 +++++++++++++----- .../core/util/TestDefaultPrettyPrinter.java | 31 +++++ 4 files changed, 118 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java b/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java index 85b709a7b5..b081d99d35 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java +++ b/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java @@ -255,7 +255,8 @@ protected DefaultPrettyPrinter _withSpaces(boolean state) */ public DefaultPrettyPrinter withSeparators(Separators separators) { _separators = separators; - _objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator(); + _objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSpacing().apply( + separators.getObjectFieldValueSeparator()); return this; } @@ -334,7 +335,7 @@ public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException @Override public void writeObjectEntrySeparator(JsonGenerator g) throws IOException { - g.writeRaw(_separators.getObjectEntrySeparator()); + g.writeRaw(_separators.getObjectEntrySpacing().apply(_separators.getObjectEntrySeparator())); _objectIndenter.writeIndentation(g, _nesting); } @@ -378,7 +379,7 @@ public void beforeArrayValues(JsonGenerator g) throws IOException { @Override public void writeArrayValueSeparator(JsonGenerator g) throws IOException { - g.writeRaw(_separators.getArrayValueSeparator()); + g.writeRaw(_separators.getArrayValueSpacing().apply(_separators.getArrayValueSeparator())); _arrayIndenter.writeIndentation(g, _nesting); } diff --git a/src/main/java/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java b/src/main/java/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java index 72b9c99f3a..868cc68f72 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java +++ b/src/main/java/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.core.util.Separators.Spacing; /** * {@link PrettyPrinter} implementation that adds no indentation, @@ -46,7 +47,7 @@ public MinimalPrettyPrinter() { public MinimalPrettyPrinter(String rootValueSeparator) { _rootValueSeparator = rootValueSeparator; - _separators = DEFAULT_SEPARATORS; + _separators = DEFAULT_SEPARATORS.withObjectFieldValueSpacing(Spacing.NONE); } public void setRootValueSeparator(String sep) { diff --git a/src/main/java/com/fasterxml/jackson/core/util/Separators.java b/src/main/java/com/fasterxml/jackson/core/util/Separators.java index 6c9731db0e..389bc03095 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/Separators.java +++ b/src/main/java/com/fasterxml/jackson/core/util/Separators.java @@ -15,77 +15,125 @@ public class Separators implements Serializable { private static final long serialVersionUID = 1; - private static final String DEFAULT_OBJECT_FIELD_VALUE_SEPARATOR = " : "; + public enum Spacing { + NONE("", ""), + BEFORE(" ", ""), + AFTER("", " "), + BOTH(" ", " "); + + private final String spacesBefore; + private final String spacesAfter; + + private Spacing(String spacesBefore, String spacesAfter) { + this.spacesBefore = spacesBefore; + this.spacesAfter = spacesAfter; + } + + public String spacesBefore() { + return spacesBefore; + } + + public String spacesAfter() { + return spacesAfter; + } + + public String apply(char separator) { + return spacesBefore + separator + spacesAfter; + } + } - private final String objectFieldValueSeparator; + private final char objectFieldValueSeparator; + private final Spacing objectFieldValueSpacing; private final char objectEntrySeparator; + private final Spacing objectEntrySpacing; private final char arrayValueSeparator; + private final Spacing arrayValueSpacing; public static Separators createDefaultInstance() { return new Separators(); } public Separators() { - this(DEFAULT_OBJECT_FIELD_VALUE_SEPARATOR, ',', ','); + this(':', ',', ','); } - public Separators(char objectFieldValueSeparator, - char objectEntrySeparator, char arrayValueSeparator) { - this(migrate(objectFieldValueSeparator), objectEntrySeparator, arrayValueSeparator); + public Separators( + char objectFieldValueSeparator, + char objectEntrySeparator, + char arrayValueSeparator + ) { + this(objectFieldValueSeparator, Spacing.BOTH, + objectEntrySeparator, Spacing.NONE, + arrayValueSeparator, Spacing.NONE); } - public Separators(String objectFieldValueSeparator, - char objectEntrySeparator, char arrayValueSeparator) { - if (objectFieldValueSeparator == null) { - objectFieldValueSeparator = DEFAULT_OBJECT_FIELD_VALUE_SEPARATOR; - } else if (objectFieldValueSeparator.trim().isEmpty()) { - throw new IllegalArgumentException("objectFieldValueSeparator can't be blank"); - } + public Separators( + char objectFieldValueSeparator, + Spacing objectFieldValueSpacing, + char objectEntrySeparator, + Spacing objectEntrySpacing, + char arrayValueSeparator, + Spacing arrayValueSpacing + ) { this.objectFieldValueSeparator = objectFieldValueSeparator; + this.objectFieldValueSpacing = objectFieldValueSpacing; this.objectEntrySeparator = objectEntrySeparator; + this.objectEntrySpacing = objectEntrySpacing; this.arrayValueSeparator = arrayValueSeparator; - } - - private static String migrate(char objectFieldValueSeparator) { - return " " + objectFieldValueSeparator + " "; + this.arrayValueSpacing = arrayValueSpacing; } public Separators withObjectFieldValueSeparator(char sep) { - return (objectFieldValueSeparator == migrate(sep)) ? this - : new Separators(sep, objectEntrySeparator, arrayValueSeparator); + return (objectFieldValueSeparator == sep) ? this + : new Separators(sep, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); } - - public Separators withObjectFieldValueSeparator(String sep) { - if (sep == null) { - sep = DEFAULT_OBJECT_FIELD_VALUE_SEPARATOR; - } - return (objectFieldValueSeparator.equals(sep)) ? this - : new Separators(sep, objectEntrySeparator, arrayValueSeparator); + + public Separators withObjectFieldValueSpacing(Spacing spacing) { + return (objectFieldValueSpacing == spacing) ? this + : new Separators(objectFieldValueSeparator, spacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); } - public Separators withCompactObjectFieldValueSeparator() { - return withObjectFieldValueSeparator(": "); - } - public Separators withObjectEntrySeparator(char sep) { return (objectEntrySeparator == sep) ? this - : new Separators(objectFieldValueSeparator, sep, arrayValueSeparator); + : new Separators(objectFieldValueSeparator, objectFieldValueSpacing, sep, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); + } + + public Separators withObjectEntrySpacing(Spacing spacing) { + return (objectEntrySpacing == spacing) ? this + : new Separators(objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, spacing, arrayValueSeparator, arrayValueSpacing); } public Separators withArrayValueSeparator(char sep) { return (arrayValueSeparator == sep) ? this - : new Separators(objectFieldValueSeparator, objectEntrySeparator, sep); + : new Separators(objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, sep, arrayValueSpacing); + } + + public Separators withArrayValueSpacing(Spacing spacing) { + return (arrayValueSpacing == spacing) ? this + : new Separators(objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, spacing); } - public String getObjectFieldValueSeparator() { + public char getObjectFieldValueSeparator() { return objectFieldValueSeparator; } + public Spacing getObjectFieldValueSpacing() { + return objectFieldValueSpacing; + } + public char getObjectEntrySeparator() { return objectEntrySeparator; } + public Spacing getObjectEntrySpacing() { + return objectEntrySpacing; + } + public char getArrayValueSeparator() { return arrayValueSeparator; } + + public Spacing getArrayValueSpacing() { + return arrayValueSpacing; + } } diff --git a/src/test/java/com/fasterxml/jackson/core/util/TestDefaultPrettyPrinter.java b/src/test/java/com/fasterxml/jackson/core/util/TestDefaultPrettyPrinter.java index a65c011aeb..887fadca70 100644 --- a/src/test/java/com/fasterxml/jackson/core/util/TestDefaultPrettyPrinter.java +++ b/src/test/java/com/fasterxml/jackson/core/util/TestDefaultPrettyPrinter.java @@ -5,6 +5,7 @@ import java.io.StringWriter; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.util.Separators.Spacing; public class TestDefaultPrettyPrinter extends BaseTest { @@ -81,6 +82,36 @@ public void testTabIndent() throws IOException assertEquals(EXP, _printTestData(pp, false)); assertEquals(EXP, _printTestData(pp, true)); } + + public void testObjectFieldValueSpacingAfter() throws IOException + { + Separators separators = new Separators() + .withObjectFieldValueSpacing(Spacing.AFTER); + PrettyPrinter pp = new DefaultPrettyPrinter() + .withObjectIndenter(new DefaultIndenter(" ", "\n")) + .withSeparators(separators); + String EXP = "{\n" + + " \"name\": \"John Doe\",\n" + + " \"age\": 3.14\n" + + "}"; + assertEquals(EXP, _printTestData(pp, false)); + assertEquals(EXP, _printTestData(pp, true)); + } + + public void testObjectFieldValueSpacingNone() throws IOException + { + Separators separators = new Separators() + .withObjectFieldValueSpacing(Spacing.NONE); + PrettyPrinter pp = new DefaultPrettyPrinter() + .withObjectIndenter(new DefaultIndenter(" ", "\n")) + .withSeparators(separators); + String EXP = "{\n" + + " \"name\":\"John Doe\",\n" + + " \"age\":3.14\n" + + "}"; + assertEquals(EXP, _printTestData(pp, false)); + assertEquals(EXP, _printTestData(pp, true)); + } public void testRootSeparator() throws IOException { From eb46c9d187927b7ce02c3ed18d0e8c5ba4c3baf9 Mon Sep 17 00:00:00 2001 From: Aaron Digulla Date: Tue, 13 Jun 2023 19:40:19 +0200 Subject: [PATCH 3/4] Moved rootSeparator to Separators. Deprecated old API. Tests for old and new API to make sure the changes don't break existing code and the new API can do what the old could. --- .../core/util/DefaultPrettyPrinter.java | 115 ++++++++++--- .../jackson/core/util/Separators.java | 56 +++++- .../core/util/TestDefaultPrettyPrinter.java | 161 +++++++++++++----- 3 files changed, 262 insertions(+), 70 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java b/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java index b081d99d35..779a2de98f 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java +++ b/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java @@ -33,7 +33,9 @@ public class DefaultPrettyPrinter * root values: a single space character. * * @since 2.1 + * @deprecated in 2.16. Use the Separators API instead. */ + @Deprecated public final static SerializedString DEFAULT_ROOT_VALUE_SEPARATOR = new SerializedString(" "); /** @@ -70,8 +72,10 @@ public interface Indenter /** * String printed between root-level values, if any. + * + * @deprecated in 2.16. Use Separators API instead. */ - protected final SerializableString _rootSeparator; + protected SerializableString _rootSeparator; // // // Config, other white space configuration @@ -79,7 +83,10 @@ public interface Indenter * By default we will add spaces around colons used to * separate object fields and values. * If disabled, will not use spaces around colon. + * + * @deprecated in 2.16. Use Separators API instead. */ + @Deprecated protected boolean _spacesInObjectEntries = true; // // // State: @@ -99,7 +106,17 @@ public interface Indenter * @since 2.9 */ protected String _objectFieldValueSeparatorWithSpaces; + + /** + * @since 2.16 + */ + protected String _objectEntrySeparator; + /** + * @since 2.16 + */ + protected String _arrayValueSeparator; + /* /********************************************************** /* Life-cycle (construct, configure) @@ -107,7 +124,7 @@ public interface Indenter */ public DefaultPrettyPrinter() { - this(DEFAULT_ROOT_VALUE_SEPARATOR); + this(DEFAULT_SEPARATORS); } /** @@ -118,7 +135,9 @@ public DefaultPrettyPrinter() { * calls {@link #DefaultPrettyPrinter(SerializableString)} * * @param rootSeparator String to use as root value separator + * @deprecated in 2.16. Use the Separators API instead. */ + @Deprecated public DefaultPrettyPrinter(String rootSeparator) { this((rootSeparator == null) ? null : new SerializedString(rootSeparator)); } @@ -128,16 +147,17 @@ public DefaultPrettyPrinter(String rootSeparator) { * if null, no separator is printed. * * @param rootSeparator String to use as root value separator + * @deprecated in 2.16. Use the Separators API instead. */ + @Deprecated public DefaultPrettyPrinter(SerializableString rootSeparator) { - _rootSeparator = rootSeparator; - withSeparators(DEFAULT_SEPARATORS); - } - - public DefaultPrettyPrinter(DefaultPrettyPrinter base) { - this(base, base._rootSeparator); + this(DEFAULT_SEPARATORS.withRootSeparator(rootSeparator.getValue())); } + /** + * @deprecated in 2.16. Use the Separators API instead. + */ + @Deprecated public DefaultPrettyPrinter(DefaultPrettyPrinter base, SerializableString rootSeparator) { @@ -148,17 +168,58 @@ public DefaultPrettyPrinter(DefaultPrettyPrinter base, _separators = base._separators; _objectFieldValueSeparatorWithSpaces = base._objectFieldValueSeparatorWithSpaces; + _objectEntrySeparator = base._objectEntrySeparator; + _arrayValueSeparator = base._arrayValueSeparator; _rootSeparator = rootSeparator; } + /** + * @since 2.16 + */ + public DefaultPrettyPrinter(Separators separators) + { + _separators = separators; + + _rootSeparator = separators.getRootSeparator() == null ? null : new SerializedString(separators.getRootSeparator()); + _objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSpacing().apply( + separators.getObjectFieldValueSeparator()); + _objectEntrySeparator = separators.getObjectEntrySpacing().apply(separators.getObjectEntrySeparator()); + _arrayValueSeparator = separators.getArrayValueSpacing().apply(separators.getArrayValueSeparator()); + } + + /** + * Copy constructor + * + * @since 2.16 + */ + public DefaultPrettyPrinter(DefaultPrettyPrinter base) { + _rootSeparator = base._rootSeparator; + + _arrayIndenter = base._arrayIndenter; + _objectIndenter = base._objectIndenter; + _spacesInObjectEntries = base._spacesInObjectEntries; + _nesting = base._nesting; + + _separators = base._separators; + _objectFieldValueSeparatorWithSpaces = base._objectFieldValueSeparatorWithSpaces; + _objectEntrySeparator = base._objectEntrySeparator; + _arrayValueSeparator = base._arrayValueSeparator; + } + + /** + * @deprecated in 2.16. Use the Separators API instead. + */ + @Deprecated public DefaultPrettyPrinter withRootSeparator(SerializableString rootSeparator) { if (_rootSeparator == rootSeparator || (rootSeparator != null && rootSeparator.equals(_rootSeparator))) { return this; } - return new DefaultPrettyPrinter(this, rootSeparator); + Separators separators = _separators.withRootSeparator(rootSeparator == null ? null : rootSeparator.getValue()); + return new DefaultPrettyPrinter(this) + .withSeparators(separators); } /** @@ -167,7 +228,9 @@ public DefaultPrettyPrinter withRootSeparator(SerializableString rootSeparator) * @return This pretty-printer instance (for call chaining) * * @since 2.6 + * @deprecated in 2.16. Use the Separators API instead. */ + @Deprecated public DefaultPrettyPrinter withRootSeparator(String rootSeparator) { return withRootSeparator((rootSeparator == null) ? null : new SerializedString(rootSeparator)); } @@ -180,7 +243,7 @@ public void indentObjectsWith(Indenter i) { _objectIndenter = (i == null) ? NopIndenter.instance : i; } - // @since 2.3 + /** @since 2.3 */ public DefaultPrettyPrinter withArrayIndenter(Indenter i) { if (i == null) { i = NopIndenter.instance; @@ -193,7 +256,7 @@ public DefaultPrettyPrinter withArrayIndenter(Indenter i) { return pp; } - // @since 2.3 + /** @since 2.3 */ public DefaultPrettyPrinter withObjectIndenter(Indenter i) { if (i == null) { i = NopIndenter.instance; @@ -215,7 +278,9 @@ public DefaultPrettyPrinter withObjectIndenter(Indenter i) { * @return This pretty-printer instance (for call chaining) * * @since 2.3 + * @deprecated in 2.16. Use the Separators API instead. */ + @Deprecated public DefaultPrettyPrinter withSpacesInObjectEntries() { return _withSpaces(true); } @@ -229,7 +294,9 @@ public DefaultPrettyPrinter withSpacesInObjectEntries() { * @return This pretty-printer instance (for call chaining) * * @since 2.3 + * @deprecated in 2.16. Use the Separators API instead. */ + @Deprecated public DefaultPrettyPrinter withoutSpacesInObjectEntries() { return _withSpaces(false); } @@ -239,7 +306,9 @@ protected DefaultPrettyPrinter _withSpaces(boolean state) if (_spacesInObjectEntries == state) { return this; } - DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this); + + Separators copy = _separators.withObjectFieldValueSpacing(state ? Separators.Spacing.BOTH : Separators.Spacing.NONE); + DefaultPrettyPrinter pp = withSeparators(copy); pp._spacesInObjectEntries = state; return pp; } @@ -254,10 +323,16 @@ protected DefaultPrettyPrinter _withSpaces(boolean state) * @since 2.9 */ public DefaultPrettyPrinter withSeparators(Separators separators) { - _separators = separators; - _objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSpacing().apply( + DefaultPrettyPrinter result = new DefaultPrettyPrinter(this); + result._separators = separators; + + result._rootSeparator = separators.getRootSeparator() == null ? null : new SerializedString(separators.getRootSeparator()); + result._objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSpacing().apply( separators.getObjectFieldValueSeparator()); - return this; + result._objectEntrySeparator = separators.getObjectEntrySpacing().apply(separators.getObjectEntrySeparator()); + result._arrayValueSeparator = separators.getArrayValueSpacing().apply(separators.getArrayValueSeparator()); + + return result; } /* @@ -316,11 +391,7 @@ public void beforeObjectEntries(JsonGenerator g) throws IOException @Override public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException { - if (_spacesInObjectEntries) { - g.writeRaw(_objectFieldValueSeparatorWithSpaces); - } else { - g.writeRaw(_separators.getObjectFieldValueSeparator()); - } + g.writeRaw(_objectFieldValueSeparatorWithSpaces); } /** @@ -335,7 +406,7 @@ public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException @Override public void writeObjectEntrySeparator(JsonGenerator g) throws IOException { - g.writeRaw(_separators.getObjectEntrySpacing().apply(_separators.getObjectEntrySeparator())); + g.writeRaw(_objectEntrySeparator); _objectIndenter.writeIndentation(g, _nesting); } @@ -379,7 +450,7 @@ public void beforeArrayValues(JsonGenerator g) throws IOException { @Override public void writeArrayValueSeparator(JsonGenerator g) throws IOException { - g.writeRaw(_separators.getArrayValueSpacing().apply(_separators.getArrayValueSeparator())); + g.writeRaw(_arrayValueSeparator); _arrayIndenter.writeIndentation(g, _nesting); } diff --git a/src/main/java/com/fasterxml/jackson/core/util/Separators.java b/src/main/java/com/fasterxml/jackson/core/util/Separators.java index 389bc03095..5837d7825a 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/Separators.java +++ b/src/main/java/com/fasterxml/jackson/core/util/Separators.java @@ -15,6 +15,19 @@ public class Separators implements Serializable { private static final long serialVersionUID = 1; + /** + * Constant that specifies default "root-level" separator to use between + * root values: a single space character. + * + * @since 2.16 + */ + public final static String DEFAULT_ROOT_VALUE_SEPARATOR = " "; + + /** + * Define the spacing around elements like commas and colons. + * + * @since 2.16 + */ public enum Spacing { NONE("", ""), BEFORE(" ", ""), @@ -48,6 +61,7 @@ public String apply(char separator) { private final Spacing objectEntrySpacing; private final char arrayValueSeparator; private final Spacing arrayValueSpacing; + private final String rootSeparator; public static Separators createDefaultInstance() { return new Separators(); @@ -57,17 +71,28 @@ public Separators() { this(':', ',', ','); } + /** + * Create an instance with the specified separator characters. There will be spaces before and + * after the objectFieldValueSeparator and none around the other two. + */ public Separators( char objectFieldValueSeparator, char objectEntrySeparator, char arrayValueSeparator ) { - this(objectFieldValueSeparator, Spacing.BOTH, + this(DEFAULT_ROOT_VALUE_SEPARATOR, + objectFieldValueSeparator, Spacing.BOTH, objectEntrySeparator, Spacing.NONE, arrayValueSeparator, Spacing.NONE); } + /** + * Create an instance with the specified separator characters and spaces around those characters. + * + * @since 2.16 + */ public Separators( + String rootSeperator, char objectFieldValueSeparator, Spacing objectFieldValueSpacing, char objectEntrySeparator, @@ -75,6 +100,7 @@ public Separators( char arrayValueSeparator, Spacing arrayValueSpacing ) { + this.rootSeparator = rootSeperator; this.objectFieldValueSeparator = objectFieldValueSeparator; this.objectFieldValueSpacing = objectFieldValueSpacing; this.objectEntrySeparator = objectEntrySeparator; @@ -83,40 +109,54 @@ public Separators( this.arrayValueSpacing = arrayValueSpacing; } + public Separators withRootSeparator(String sep) { + return (rootSeparator.equals(sep)) ? this + : new Separators(sep, objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); + } + public Separators withObjectFieldValueSeparator(char sep) { return (objectFieldValueSeparator == sep) ? this - : new Separators(sep, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); + : new Separators(rootSeparator, sep, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); } + /** @since 2.16 */ public Separators withObjectFieldValueSpacing(Spacing spacing) { return (objectFieldValueSpacing == spacing) ? this - : new Separators(objectFieldValueSeparator, spacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); + : new Separators(rootSeparator, objectFieldValueSeparator, spacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); } public Separators withObjectEntrySeparator(char sep) { return (objectEntrySeparator == sep) ? this - : new Separators(objectFieldValueSeparator, objectFieldValueSpacing, sep, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); + : new Separators(rootSeparator, objectFieldValueSeparator, objectFieldValueSpacing, sep, objectEntrySpacing, arrayValueSeparator, arrayValueSpacing); } + /** @since 2.16 */ public Separators withObjectEntrySpacing(Spacing spacing) { return (objectEntrySpacing == spacing) ? this - : new Separators(objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, spacing, arrayValueSeparator, arrayValueSpacing); + : new Separators(rootSeparator, objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, spacing, arrayValueSeparator, arrayValueSpacing); } public Separators withArrayValueSeparator(char sep) { return (arrayValueSeparator == sep) ? this - : new Separators(objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, sep, arrayValueSpacing); + : new Separators(rootSeparator, objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, sep, arrayValueSpacing); } + /** @since 2.16 */ public Separators withArrayValueSpacing(Spacing spacing) { return (arrayValueSpacing == spacing) ? this - : new Separators(objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, spacing); + : new Separators(rootSeparator, objectFieldValueSeparator, objectFieldValueSpacing, objectEntrySeparator, objectEntrySpacing, arrayValueSeparator, spacing); + } + + /** @since 2.16 */ + public String getRootSeparator() { + return rootSeparator; } public char getObjectFieldValueSeparator() { return objectFieldValueSeparator; } + /** @since 2.16 */ public Spacing getObjectFieldValueSpacing() { return objectFieldValueSpacing; } @@ -125,6 +165,7 @@ public char getObjectEntrySeparator() { return objectEntrySeparator; } + /** @since 2.16 */ public Spacing getObjectEntrySpacing() { return objectEntrySpacing; } @@ -133,6 +174,7 @@ public char getArrayValueSeparator() { return arrayValueSeparator; } + /** @since 2.16 */ public Spacing getArrayValueSpacing() { return arrayValueSpacing; } diff --git a/src/test/java/com/fasterxml/jackson/core/util/TestDefaultPrettyPrinter.java b/src/test/java/com/fasterxml/jackson/core/util/TestDefaultPrettyPrinter.java index 887fadca70..123a0bfca9 100644 --- a/src/test/java/com/fasterxml/jackson/core/util/TestDefaultPrettyPrinter.java +++ b/src/test/java/com/fasterxml/jackson/core/util/TestDefaultPrettyPrinter.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.io.StringWriter; +import org.assertj.core.api.ThrowingConsumer; + import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.util.Separators.Spacing; @@ -112,61 +114,143 @@ public void testObjectFieldValueSpacingNone() throws IOException assertEquals(EXP, _printTestData(pp, false)); assertEquals(EXP, _printTestData(pp, true)); } + + public void testCopyConfigOld() throws IOException + { + Separators separators = new Separators() + .withObjectFieldValueSpacing(Spacing.AFTER) + .withObjectEntrySpacing(Spacing.AFTER) + .withArrayValueSpacing(Spacing.AFTER); + DefaultPrettyPrinter pp = new DefaultPrettyPrinter() + .withObjectIndenter(new DefaultIndenter(" ", "\n")) + .withSeparators(separators); + String expected = _printTestData(pp, false); + assertEquals(expected, _printTestData(pp, true)); + + @SuppressWarnings("deprecation") // Testing the old API + DefaultPrettyPrinter copy = pp.withRootSeparator(DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR); + assertEquals(expected, _printTestData(copy, false)); + assertEquals(expected, _printTestData(copy, true)); + } + + public void testCopyConfigNew() throws IOException + { + Separators separators = new Separators() + .withObjectFieldValueSpacing(Spacing.AFTER) + .withObjectEntrySpacing(Spacing.AFTER) + .withArrayValueSpacing(Spacing.AFTER); + DefaultPrettyPrinter pp = new DefaultPrettyPrinter() + .withObjectIndenter(new DefaultIndenter(" ", "\n")) + .withSeparators(separators); + String expected = _printTestData(pp, false); + assertEquals(expected, _printTestData(pp, true)); + + DefaultPrettyPrinter copy = new DefaultPrettyPrinter(pp); + assertEquals(expected, _printTestData(copy, false)); + assertEquals(expected, _printTestData(copy, true)); + } - public void testRootSeparator() throws IOException + public void testRootSeparatorOld() throws IOException { + @SuppressWarnings("deprecation") // Testing the old API DefaultPrettyPrinter pp = new DefaultPrettyPrinter() .withRootSeparator("|"); final String EXP = "1|2|3"; - StringWriter sw = new StringWriter(); - JsonGenerator gen = JSON_F.createGenerator(sw); - gen.setPrettyPrinter(pp); - - gen.writeNumber(1); - gen.writeNumber(2); - gen.writeNumber(3); - gen.close(); - assertEquals(EXP, sw.toString()); - - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - gen = JSON_F.createGenerator(bytes); - gen.setPrettyPrinter(pp); + ThrowingConsumer writeTestData = gen -> { + gen.writeNumber(1); + gen.writeNumber(2); + gen.writeNumber(3); + }; - gen.writeNumber(1); - gen.writeNumber(2); - gen.writeNumber(3); - gen.close(); - assertEquals(EXP, bytes.toString("UTF-8")); + assertEquals(EXP, _printTestData(pp, false, writeTestData)); + assertEquals(EXP, _printTestData(pp, true, writeTestData)); + } + + public void testRootSeparator() throws IOException + { + Separators separators = new Separators() + .withRootSeparator("|"); + DefaultPrettyPrinter pp = new DefaultPrettyPrinter() + .withSeparators(separators); + final String EXP = "1|2|3"; + + ThrowingConsumer writeTestData = gen -> { + gen.writeNumber(1); + gen.writeNumber(2); + gen.writeNumber(3); + }; + + assertEquals(EXP, _printTestData(pp, false, writeTestData)); + assertEquals(EXP, _printTestData(pp, true, writeTestData)); + } + public void testWithoutSeparatorsOld() throws IOException + { // Also: let's try removing separator altogether - pp = pp.withRootSeparator((String) null) + @SuppressWarnings("deprecation") // Testing the old API + DefaultPrettyPrinter pp = new DefaultPrettyPrinter() + .withRootSeparator((String) null) .withArrayIndenter(null) .withObjectIndenter(null) .withoutSpacesInObjectEntries(); - sw = new StringWriter(); - gen = JSON_F.createGenerator(sw); - gen.setPrettyPrinter(pp); - gen.writeNumber(1); - gen.writeStartArray(); - gen.writeNumber(2); - gen.writeEndArray(); - gen.writeStartObject(); - gen.writeFieldName("a"); - gen.writeNumber(3); - gen.writeEndObject(); - gen.close(); + ThrowingConsumer writeTestData = gen -> { + gen.writeNumber(1); + gen.writeStartArray(); + gen.writeNumber(2); + gen.writeEndArray(); + gen.writeStartObject(); + gen.writeFieldName("a"); + gen.writeNumber(3); + gen.writeEndObject(); + }; // no root separator, nor array, object - assertEquals("1[2]{\"a\":3}", sw.toString()); + assertEquals("1[2]{\"a\":3}", _printTestData(pp, false, writeTestData)); + } + + public void testWithoutSeparatorsNew() throws IOException + { + Separators separators = new Separators() + .withRootSeparator(null) + .withObjectFieldValueSpacing(Spacing.NONE); + DefaultPrettyPrinter pp = new DefaultPrettyPrinter() + .withSeparators(separators) + .withArrayIndenter(null) + .withObjectIndenter(null); + + ThrowingConsumer writeTestData = gen -> { + gen.writeNumber(1); + gen.writeStartArray(); + gen.writeNumber(2); + gen.writeEndArray(); + gen.writeStartObject(); + gen.writeFieldName("a"); + gen.writeNumber(3); + gen.writeEndObject(); + }; + // no root separator, nor array, object + assertEquals("1[2]{\"a\":3}", _printTestData(pp, false, writeTestData)); } private String _printTestData(PrettyPrinter pp, boolean useBytes) throws IOException + { + return _printTestData(pp, useBytes, gen -> { + gen.writeStartObject(); + gen.writeFieldName("name"); + gen.writeString("John Doe"); + gen.writeFieldName("age"); + gen.writeNumber(3.14); + gen.writeEndObject(); + }); + } + + private String _printTestData(PrettyPrinter pp, boolean useBytes, ThrowingConsumer writeTestData) throws IOException { JsonGenerator gen; StringWriter sw; ByteArrayOutputStream bytes; - + if (useBytes) { sw = null; bytes = new ByteArrayOutputStream(); @@ -177,14 +261,9 @@ private String _printTestData(PrettyPrinter pp, boolean useBytes) throws IOExcep gen = JSON_F.createGenerator(sw); } gen.setPrettyPrinter(pp); - gen.writeStartObject(); - gen.writeFieldName("name"); - gen.writeString("John Doe"); - gen.writeFieldName("age"); - gen.writeNumber(3.14); - gen.writeEndObject(); + writeTestData.accept(gen); gen.close(); - + if (useBytes) { return bytes.toString("UTF-8"); } From feaec2fe125b4615f8e99af4e7c412460506c685 Mon Sep 17 00:00:00 2001 From: Aaron Digulla Date: Tue, 13 Jun 2023 21:49:27 +0200 Subject: [PATCH 4/4] Fixed deprecations in existing code. Converted all tests that used the old API. --- .../fasterxml/jackson/core/JsonFactory.java | 4 +- .../jackson/core/json/JsonGeneratorImpl.java | 3 +- .../jackson/core/write/PrettyPrinterTest.java | 71 +++++++++++++++---- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index 1427ba5021..a754660b25 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -19,8 +19,8 @@ import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.BufferRecyclers; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.JacksonFeature; +import com.fasterxml.jackson.core.util.Separators; /** * The main factory class of Jackson package, used to configure and @@ -197,7 +197,7 @@ public static int collectDefaults() { */ protected final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults(); - public final static SerializableString DEFAULT_ROOT_VALUE_SEPARATOR = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; + public final static SerializableString DEFAULT_ROOT_VALUE_SEPARATOR = new SerializedString(Separators.DEFAULT_ROOT_VALUE_SEPARATOR); /** * @since 2.10 diff --git a/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java b/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java index 6aef2e857e..807a92fda8 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java +++ b/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.core.io.CharTypes; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.IOContext; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.JacksonFeatureSet; import com.fasterxml.jackson.core.util.VersionUtil; @@ -92,7 +91,7 @@ public abstract class JsonGeneratorImpl extends GeneratorBase * @since 2.1 */ protected SerializableString _rootValueSeparator - = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; + = JsonFactory.DEFAULT_ROOT_VALUE_SEPARATOR; /** * Flag that is set if quoting is not to be added around diff --git a/src/test/java/com/fasterxml/jackson/core/write/PrettyPrinterTest.java b/src/test/java/com/fasterxml/jackson/core/write/PrettyPrinterTest.java index f8eabb70fe..05c91a4bac 100644 --- a/src/test/java/com/fasterxml/jackson/core/write/PrettyPrinterTest.java +++ b/src/test/java/com/fasterxml/jackson/core/write/PrettyPrinterTest.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.core.util.Separators; +import com.fasterxml.jackson.core.util.Separators.Spacing; import java.io.*; @@ -131,14 +132,33 @@ public void beforeArrayValues(JsonGenerator jg) throws IOException, JsonGenerati } // [core#26] - public void testCustomRootSeparatorWithPP() throws Exception + public void testRootSeparatorWithoutPP() throws Exception { - // first, no pretty-printing (will still separate root values with a space!) + // no pretty-printing (will still separate root values with a space!) assertEquals("{} {} []", _generateRoot(JSON_F, null)); - // First with default pretty printer, default configs: + } + + // [core#26] + public void testDefaultRootSeparatorWithPP() throws Exception + { assertEquals("{ } { } [ ]", _generateRoot(JSON_F, new DefaultPrettyPrinter())); - // then custom: - assertEquals("{ }|{ }|[ ]", _generateRoot(JSON_F, new DefaultPrettyPrinter("|"))); + } + + // [core#26] + public void testCustomRootSeparatorWithPPOld() throws Exception + { + @SuppressWarnings("deprecation") + DefaultPrettyPrinter pp = new DefaultPrettyPrinter("|"); + assertEquals("{ }|{ }|[ ]", _generateRoot(JSON_F, pp)); + } + + // [core#26] + public void testCustomRootSeparatorWithPPNew() throws Exception + { + Separators separators = Separators.createDefaultInstance() + .withRootSeparator("|"); + DefaultPrettyPrinter pp = new DefaultPrettyPrinter(separators); + assertEquals("{ }|{ }|[ ]", _generateRoot(JSON_F, pp)); } // Alternative solution for [jackson-core#26] @@ -202,25 +222,50 @@ public void testCustomSeparatorsWithPP() throws Exception "} ]", sw.toString()); } - public void testCustomSeparatorsWithPPWithoutSpaces() throws Exception + private static final String EXPECTED_CUSTOM_SEPARATORS_WITH_PP_WITHOUT_SPACES = + "[ 3| \"abc\"| [ true ]| {" + DefaultIndenter.SYS_LF + + " \"f\"=null;" + DefaultIndenter.SYS_LF + + " \"f2\"=null" + DefaultIndenter.SYS_LF + + "} ]"; + + public void testCustomSeparatorsWithPPWithoutSpacesOld() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); - gen.setPrettyPrinter(new DefaultPrettyPrinter().withSeparators(Separators.createDefaultInstance() + Separators separators = Separators.createDefaultInstance() .withObjectFieldValueSeparator('=') .withObjectEntrySeparator(';') - .withArrayValueSeparator('|')) - .withoutSpacesInObjectEntries()); + .withArrayValueSeparator('|'); + @SuppressWarnings("deprecation") + DefaultPrettyPrinter pp = new DefaultPrettyPrinter() + .withSeparators(separators) + .withoutSpacesInObjectEntries(); + gen.setPrettyPrinter(pp); _writeTestDocument(gen); gen.close(); - assertEquals("[ 3| \"abc\"| [ true ]| {" + DefaultIndenter.SYS_LF + - " \"f\"=null;" + DefaultIndenter.SYS_LF + - " \"f2\"=null" + DefaultIndenter.SYS_LF + - "} ]", sw.toString()); + assertEquals(EXPECTED_CUSTOM_SEPARATORS_WITH_PP_WITHOUT_SPACES, sw.toString()); } + public void testCustomSeparatorsWithPPWithoutSpacesNew() throws Exception + { + StringWriter sw = new StringWriter(); + JsonGenerator gen = new JsonFactory().createGenerator(sw); + Separators separators = Separators.createDefaultInstance() + .withObjectFieldValueSeparator('=') + .withObjectFieldValueSpacing(Spacing.NONE) + .withObjectEntrySeparator(';') + .withArrayValueSeparator('|'); + DefaultPrettyPrinter pp = new DefaultPrettyPrinter(separators); + gen.setPrettyPrinter(pp); + + _writeTestDocument(gen); + gen.close(); + + assertEquals(EXPECTED_CUSTOM_SEPARATORS_WITH_PP_WITHOUT_SPACES, sw.toString()); + } + /* /********************************************************** /* Helper methods