diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java index bf70834a6b..297545e52a 100644 --- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java +++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java @@ -101,7 +101,7 @@ public class JsonFormat { * Creates a {@link Printer} with default configurations. */ public static Printer printer() { - return new Printer(TypeRegistry.getEmptyTypeRegistry(), false, false); + return new Printer(TypeRegistry.getEmptyTypeRegistry(), false, false, false); } /** @@ -111,14 +111,16 @@ public class JsonFormat { private final TypeRegistry registry; private final boolean includingDefaultValueFields; private final boolean preservingProtoFieldNames; + private final boolean omittingInsignificantWhitespace; private Printer( TypeRegistry registry, boolean includingDefaultValueFields, - boolean preservingProtoFieldNames) { + boolean preservingProtoFieldNames, boolean omittingInsignificantWhitespace) { this.registry = registry; this.includingDefaultValueFields = includingDefaultValueFields; this.preservingProtoFieldNames = preservingProtoFieldNames; + this.omittingInsignificantWhitespace = omittingInsignificantWhitespace; } /** @@ -131,7 +133,7 @@ public class JsonFormat { if (this.registry != TypeRegistry.getEmptyTypeRegistry()) { throw new IllegalArgumentException("Only one registry is allowed."); } - return new Printer(registry, includingDefaultValueFields, preservingProtoFieldNames); + return new Printer(registry, includingDefaultValueFields, preservingProtoFieldNames, omittingInsignificantWhitespace); } /** @@ -141,7 +143,7 @@ public class JsonFormat { * {@link Printer}. */ public Printer includingDefaultValueFields() { - return new Printer(registry, true, preservingProtoFieldNames); + return new Printer(registry, true, preservingProtoFieldNames, omittingInsignificantWhitespace); } /** @@ -151,7 +153,27 @@ public class JsonFormat { * current {@link Printer}. */ public Printer preservingProtoFieldNames() { - return new Printer(registry, includingDefaultValueFields, true); + return new Printer(registry, includingDefaultValueFields, true, omittingInsignificantWhitespace); + } + + + /** + * Create a new {@link Printer} that will omit all insignificant whitespace + * in the JSON output. This new Printer clones all other configurations from the + * current Printer. Insignificant whitespace is defined by the JSON spec as whitespace + * that appear between JSON structural elements: + *
+ * ws = *( + * %x20 / ; Space + * %x09 / ; Horizontal tab + * %x0A / ; Line feed or New line + * %x0D ) ; Carriage return + *+ * See https://tools.ietf.org/html/rfc7159 + * current {@link Printer}. + */ + public Printer omittingInsignificantWhitespace(){ + return new Printer(registry, includingDefaultValueFields, preservingProtoFieldNames, true); } /** @@ -164,7 +186,7 @@ public class JsonFormat { public void appendTo(MessageOrBuilder message, Appendable output) throws IOException { // TODO(xiaofeng): Investigate the allocation overhead and optimize for // mobile. - new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output) + new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output, omittingInsignificantWhitespace) .print(message); } @@ -351,15 +373,55 @@ public class JsonFormat { } } + /** + * An interface for json formatting that can be used in + * combination with the omittingInsignificantWhitespace() method + */ + interface TextGenerator { + void indent(); + void outdent(); + void print(final CharSequence text) throws IOException; + } + + + /** + * Format the json without indentation + */ + private static final class CompactTextGenerator implements TextGenerator{ + private final Appendable output; + + + private CompactTextGenerator(final Appendable output) { + this.output = output; + } + + /** + * ignored by compact printer + */ + public void indent() {} + + /** + * ignored by compact printer + */ + public void outdent() {} + + /** + * Print text to the output stream. + */ + public void print(final CharSequence text) throws IOException { + output.append(text); + } + + } /** * A TextGenerator adds indentation when writing formatted text. */ - private static final class TextGenerator { + private static final class PrettyTextGenerator implements TextGenerator{ private final Appendable output; private final StringBuilder indent = new StringBuilder(); private boolean atStartOfLine = true; - private TextGenerator(final Appendable output) { + private PrettyTextGenerator(final Appendable output) { this.output = output; } @@ -423,6 +485,8 @@ public class JsonFormat { private final TextGenerator generator; // We use Gson to help handle string escapes. private final Gson gson; + private final CharSequence blankOrSpace; + private final CharSequence blankOrNewLine; private static class GsonHolder { private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create(); @@ -432,12 +496,21 @@ public class JsonFormat { TypeRegistry registry, boolean includingDefaultValueFields, boolean preservingProtoFieldNames, - Appendable jsonOutput) { + Appendable jsonOutput, boolean omittingInsignificantWhitespace) { this.registry = registry; this.includingDefaultValueFields = includingDefaultValueFields; this.preservingProtoFieldNames = preservingProtoFieldNames; - this.generator = new TextGenerator(jsonOutput); this.gson = GsonHolder.DEFAULT_GSON; + // json format related properties, determined by printerType + if (omittingInsignificantWhitespace) { + this.generator = new CompactTextGenerator(jsonOutput); + this.blankOrSpace = ""; + this.blankOrNewLine = ""; + } else { + this.generator = new PrettyTextGenerator(jsonOutput); + this.blankOrSpace = " "; + this.blankOrNewLine = "\n"; + } } void print(MessageOrBuilder message) throws IOException { @@ -568,12 +641,12 @@ public class JsonFormat { if (printer != null) { // If the type is one of the well-known types, we use a special // formatting. - generator.print("{\n"); + generator.print("{" + blankOrNewLine); generator.indent(); - generator.print("\"@type\": " + gson.toJson(typeUrl) + ",\n"); - generator.print("\"value\": "); + generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl) + "," + blankOrNewLine); + generator.print("\"value\":" + blankOrSpace); printer.print(this, contentMessage); - generator.print("\n"); + generator.print(blankOrNewLine); generator.outdent(); generator.print("}"); } else { @@ -661,13 +734,15 @@ public class JsonFormat { } /** Prints a regular message with an optional type URL. */ - private void print(MessageOrBuilder message, String typeUrl) throws IOException { - generator.print("{\n"); + + private void print(MessageOrBuilder message, String typeUrl) + throws IOException { + generator.print("{" + blankOrNewLine); generator.indent(); boolean printedField = false; if (typeUrl != null) { - generator.print("\"@type\": " + gson.toJson(typeUrl)); + generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl)); printedField = true; } Map