|
@@ -101,7 +101,7 @@ public class JsonFormat {
|
|
* Creates a {@link Printer} with default configurations.
|
|
* Creates a {@link Printer} with default configurations.
|
|
*/
|
|
*/
|
|
public static Printer printer() {
|
|
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 TypeRegistry registry;
|
|
private final boolean includingDefaultValueFields;
|
|
private final boolean includingDefaultValueFields;
|
|
private final boolean preservingProtoFieldNames;
|
|
private final boolean preservingProtoFieldNames;
|
|
|
|
+ private final boolean omittingInsignificantWhitespace;
|
|
|
|
|
|
private Printer(
|
|
private Printer(
|
|
TypeRegistry registry,
|
|
TypeRegistry registry,
|
|
boolean includingDefaultValueFields,
|
|
boolean includingDefaultValueFields,
|
|
- boolean preservingProtoFieldNames) {
|
|
|
|
|
|
+ boolean preservingProtoFieldNames, boolean omittingInsignificantWhitespace) {
|
|
this.registry = registry;
|
|
this.registry = registry;
|
|
this.includingDefaultValueFields = includingDefaultValueFields;
|
|
this.includingDefaultValueFields = includingDefaultValueFields;
|
|
this.preservingProtoFieldNames = preservingProtoFieldNames;
|
|
this.preservingProtoFieldNames = preservingProtoFieldNames;
|
|
|
|
+ this.omittingInsignificantWhitespace = omittingInsignificantWhitespace;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -131,7 +133,7 @@ public class JsonFormat {
|
|
if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
|
|
if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
|
|
throw new IllegalArgumentException("Only one registry is allowed.");
|
|
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}.
|
|
* {@link Printer}.
|
|
*/
|
|
*/
|
|
public Printer includingDefaultValueFields() {
|
|
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}.
|
|
* current {@link Printer}.
|
|
*/
|
|
*/
|
|
public Printer preservingProtoFieldNames() {
|
|
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:
|
|
|
|
+ * <pre>
|
|
|
|
+ * ws = *(
|
|
|
|
+ * %x20 / ; Space
|
|
|
|
+ * %x09 / ; Horizontal tab
|
|
|
|
+ * %x0A / ; Line feed or New line
|
|
|
|
+ * %x0D ) ; Carriage return
|
|
|
|
+ * </pre>
|
|
|
|
+ * See <a href="https://tools.ietf.org/html/rfc7159">https://tools.ietf.org/html/rfc7159</a>
|
|
|
|
+ * 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 {
|
|
public void appendTo(MessageOrBuilder message, Appendable output) throws IOException {
|
|
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
|
|
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
|
|
// mobile.
|
|
// mobile.
|
|
- new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output)
|
|
|
|
|
|
+ new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output, omittingInsignificantWhitespace)
|
|
.print(message);
|
|
.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.
|
|
* 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 Appendable output;
|
|
private final StringBuilder indent = new StringBuilder();
|
|
private final StringBuilder indent = new StringBuilder();
|
|
private boolean atStartOfLine = true;
|
|
private boolean atStartOfLine = true;
|
|
|
|
|
|
- private TextGenerator(final Appendable output) {
|
|
|
|
|
|
+ private PrettyTextGenerator(final Appendable output) {
|
|
this.output = output;
|
|
this.output = output;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -423,6 +485,8 @@ public class JsonFormat {
|
|
private final TextGenerator generator;
|
|
private final TextGenerator generator;
|
|
// We use Gson to help handle string escapes.
|
|
// We use Gson to help handle string escapes.
|
|
private final Gson gson;
|
|
private final Gson gson;
|
|
|
|
+ private final CharSequence blankOrSpace;
|
|
|
|
+ private final CharSequence blankOrNewLine;
|
|
|
|
|
|
private static class GsonHolder {
|
|
private static class GsonHolder {
|
|
private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
|
|
private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
|
|
@@ -432,12 +496,21 @@ public class JsonFormat {
|
|
TypeRegistry registry,
|
|
TypeRegistry registry,
|
|
boolean includingDefaultValueFields,
|
|
boolean includingDefaultValueFields,
|
|
boolean preservingProtoFieldNames,
|
|
boolean preservingProtoFieldNames,
|
|
- Appendable jsonOutput) {
|
|
|
|
|
|
+ Appendable jsonOutput, boolean omittingInsignificantWhitespace) {
|
|
this.registry = registry;
|
|
this.registry = registry;
|
|
this.includingDefaultValueFields = includingDefaultValueFields;
|
|
this.includingDefaultValueFields = includingDefaultValueFields;
|
|
this.preservingProtoFieldNames = preservingProtoFieldNames;
|
|
this.preservingProtoFieldNames = preservingProtoFieldNames;
|
|
- this.generator = new TextGenerator(jsonOutput);
|
|
|
|
this.gson = GsonHolder.DEFAULT_GSON;
|
|
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 {
|
|
void print(MessageOrBuilder message) throws IOException {
|
|
@@ -568,12 +641,12 @@ public class JsonFormat {
|
|
if (printer != null) {
|
|
if (printer != null) {
|
|
// If the type is one of the well-known types, we use a special
|
|
// If the type is one of the well-known types, we use a special
|
|
// formatting.
|
|
// formatting.
|
|
- generator.print("{\n");
|
|
|
|
|
|
+ generator.print("{" + blankOrNewLine);
|
|
generator.indent();
|
|
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);
|
|
printer.print(this, contentMessage);
|
|
- generator.print("\n");
|
|
|
|
|
|
+ generator.print(blankOrNewLine);
|
|
generator.outdent();
|
|
generator.outdent();
|
|
generator.print("}");
|
|
generator.print("}");
|
|
} else {
|
|
} else {
|
|
@@ -661,13 +734,15 @@ public class JsonFormat {
|
|
}
|
|
}
|
|
|
|
|
|
/** Prints a regular message with an optional type URL. */
|
|
/** 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();
|
|
generator.indent();
|
|
|
|
|
|
boolean printedField = false;
|
|
boolean printedField = false;
|
|
if (typeUrl != null) {
|
|
if (typeUrl != null) {
|
|
- generator.print("\"@type\": " + gson.toJson(typeUrl));
|
|
|
|
|
|
+ generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl));
|
|
printedField = true;
|
|
printedField = true;
|
|
}
|
|
}
|
|
Map<FieldDescriptor, Object> fieldsToPrint = null;
|
|
Map<FieldDescriptor, Object> fieldsToPrint = null;
|
|
@@ -689,7 +764,7 @@ public class JsonFormat {
|
|
for (Map.Entry<FieldDescriptor, Object> field : fieldsToPrint.entrySet()) {
|
|
for (Map.Entry<FieldDescriptor, Object> field : fieldsToPrint.entrySet()) {
|
|
if (printedField) {
|
|
if (printedField) {
|
|
// Add line-endings for the previous field.
|
|
// Add line-endings for the previous field.
|
|
- generator.print(",\n");
|
|
|
|
|
|
+ generator.print("," + blankOrNewLine);
|
|
} else {
|
|
} else {
|
|
printedField = true;
|
|
printedField = true;
|
|
}
|
|
}
|
|
@@ -698,7 +773,7 @@ public class JsonFormat {
|
|
|
|
|
|
// Add line-endings for the last field.
|
|
// Add line-endings for the last field.
|
|
if (printedField) {
|
|
if (printedField) {
|
|
- generator.print("\n");
|
|
|
|
|
|
+ generator.print(blankOrNewLine);
|
|
}
|
|
}
|
|
generator.outdent();
|
|
generator.outdent();
|
|
generator.print("}");
|
|
generator.print("}");
|
|
@@ -706,9 +781,9 @@ public class JsonFormat {
|
|
|
|
|
|
private void printField(FieldDescriptor field, Object value) throws IOException {
|
|
private void printField(FieldDescriptor field, Object value) throws IOException {
|
|
if (preservingProtoFieldNames) {
|
|
if (preservingProtoFieldNames) {
|
|
- generator.print("\"" + field.getName() + "\": ");
|
|
|
|
|
|
+ generator.print("\"" + field.getName() + "\":" + blankOrSpace);
|
|
} else {
|
|
} else {
|
|
- generator.print("\"" + field.getJsonName() + "\": ");
|
|
|
|
|
|
+ generator.print("\"" + field.getJsonName() + "\":" + blankOrSpace);
|
|
}
|
|
}
|
|
if (field.isMapField()) {
|
|
if (field.isMapField()) {
|
|
printMapFieldValue(field, value);
|
|
printMapFieldValue(field, value);
|
|
@@ -725,7 +800,7 @@ public class JsonFormat {
|
|
boolean printedElement = false;
|
|
boolean printedElement = false;
|
|
for (Object element : (List) value) {
|
|
for (Object element : (List) value) {
|
|
if (printedElement) {
|
|
if (printedElement) {
|
|
- generator.print(", ");
|
|
|
|
|
|
+ generator.print("," + blankOrSpace);
|
|
} else {
|
|
} else {
|
|
printedElement = true;
|
|
printedElement = true;
|
|
}
|
|
}
|
|
@@ -742,7 +817,7 @@ public class JsonFormat {
|
|
if (keyField == null || valueField == null) {
|
|
if (keyField == null || valueField == null) {
|
|
throw new InvalidProtocolBufferException("Invalid map field.");
|
|
throw new InvalidProtocolBufferException("Invalid map field.");
|
|
}
|
|
}
|
|
- generator.print("{\n");
|
|
|
|
|
|
+ generator.print("{" + blankOrNewLine);
|
|
generator.indent();
|
|
generator.indent();
|
|
boolean printedElement = false;
|
|
boolean printedElement = false;
|
|
for (Object element : (List) value) {
|
|
for (Object element : (List) value) {
|
|
@@ -750,17 +825,17 @@ public class JsonFormat {
|
|
Object entryKey = entry.getField(keyField);
|
|
Object entryKey = entry.getField(keyField);
|
|
Object entryValue = entry.getField(valueField);
|
|
Object entryValue = entry.getField(valueField);
|
|
if (printedElement) {
|
|
if (printedElement) {
|
|
- generator.print(",\n");
|
|
|
|
|
|
+ generator.print("," + blankOrNewLine);
|
|
} else {
|
|
} else {
|
|
printedElement = true;
|
|
printedElement = true;
|
|
}
|
|
}
|
|
// Key fields are always double-quoted.
|
|
// Key fields are always double-quoted.
|
|
printSingleFieldValue(keyField, entryKey, true);
|
|
printSingleFieldValue(keyField, entryKey, true);
|
|
- generator.print(": ");
|
|
|
|
|
|
+ generator.print(":" + blankOrSpace);
|
|
printSingleFieldValue(valueField, entryValue);
|
|
printSingleFieldValue(valueField, entryValue);
|
|
}
|
|
}
|
|
if (printedElement) {
|
|
if (printedElement) {
|
|
- generator.print("\n");
|
|
|
|
|
|
+ generator.print(blankOrNewLine);
|
|
}
|
|
}
|
|
generator.outdent();
|
|
generator.outdent();
|
|
generator.print("}");
|
|
generator.print("}");
|