aboutsummaryrefslogtreecommitdiffhomepage
path: root/java/util
diff options
context:
space:
mode:
authorGravatar Jisi Liu <jisi.liu@gmail.com>2016-03-30 11:39:59 -0700
committerGravatar Jisi Liu <jisi.liu@gmail.com>2016-03-30 11:39:59 -0700
commit3b3c8abb9635eb3ea078a821a99c9ef29d66dff7 (patch)
tree7d2ec154f15c9f9153d890e76b6cf30e471ea488 /java/util
parent78105897a8f01c7be9cf8502b6c58d47eb1ccdd7 (diff)
Integrate google internal changes.
Diffstat (limited to 'java/util')
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java75
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/JsonFormat.java26
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java124
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java240
-rw-r--r--java/util/src/test/proto/com/google/protobuf/util/json_test.proto31
5 files changed, 294 insertions, 202 deletions
diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
index dc2f4b84..d6a2f6fa 100644
--- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
+++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
@@ -60,10 +60,9 @@ import java.util.logging.Logger;
* FieldMask in a message tree.
*/
class FieldMaskTree {
- private static final Logger logger =
- Logger.getLogger(FieldMaskTree.class.getName());
-
- private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
+ private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
+
+ private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
private static class Node {
public TreeMap<String, Node> children = new TreeMap<String, Node>();
@@ -73,12 +72,12 @@ class FieldMaskTree {
/** Creates an empty FieldMaskTree. */
public FieldMaskTree() {}
-
+
/** Creates a FieldMaskTree for a given FieldMask. */
public FieldMaskTree(FieldMask mask) {
mergeFromFieldMask(mask);
}
-
+
@Override
public String toString() {
return FieldMaskUtil.toString(toFieldMask());
@@ -121,7 +120,7 @@ class FieldMaskTree {
node.children.clear();
return this;
}
-
+
/**
* Merges all field paths in a FieldMask into this tree.
*/
@@ -149,8 +148,7 @@ class FieldMaskTree {
return;
}
for (Entry<String, Node> entry : node.children.entrySet()) {
- String childPath = path.isEmpty()
- ? entry.getKey() : path + "." + entry.getKey();
+ String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
getFieldPaths(entry.getValue(), childPath, paths);
}
}
@@ -193,11 +191,10 @@ class FieldMaskTree {
* Merges all fields specified by this FieldMaskTree from {@code source} to
* {@code destination}.
*/
- public void merge(Message source, Message.Builder destination,
- FieldMaskUtil.MergeOptions options) {
+ public void merge(
+ Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
if (source.getDescriptorForType() != destination.getDescriptorForType()) {
- throw new IllegalArgumentException(
- "Cannot merge messages of different types.");
+ throw new IllegalArgumentException("Cannot merge messages of different types.");
}
if (root.children.isEmpty()) {
return;
@@ -208,30 +205,41 @@ class FieldMaskTree {
/** Merges all fields specified by a sub-tree from {@code source} to
* {@code destination}.
*/
- private void merge(Node node, String path, Message source,
- Message.Builder destination, FieldMaskUtil.MergeOptions options) {
+ private void merge(
+ Node node,
+ String path,
+ Message source,
+ Message.Builder destination,
+ FieldMaskUtil.MergeOptions options) {
assert source.getDescriptorForType() == destination.getDescriptorForType();
-
+
Descriptor descriptor = source.getDescriptorForType();
for (Entry<String, Node> entry : node.children.entrySet()) {
- FieldDescriptor field =
- descriptor.findFieldByName(entry.getKey());
+ FieldDescriptor field = descriptor.findFieldByName(entry.getKey());
if (field == null) {
- logger.warning("Cannot find field \"" + entry.getKey()
- + "\" in message type " + descriptor.getFullName());
+ logger.warning(
+ "Cannot find field \""
+ + entry.getKey()
+ + "\" in message type "
+ + descriptor.getFullName());
continue;
}
if (!entry.getValue().children.isEmpty()) {
- if (field.isRepeated()
- || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
- logger.warning("Field \"" + field.getFullName() + "\" is not a "
- + "singluar message field and cannot have sub-fields.");
+ if (field.isRepeated() || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
+ logger.warning(
+ "Field \""
+ + field.getFullName()
+ + "\" is not a "
+ + "singluar message field and cannot have sub-fields.");
continue;
}
- String childPath = path.isEmpty()
- ? entry.getKey() : path + "." + entry.getKey();
- merge(entry.getValue(), childPath, (Message) source.getField(field),
- destination.getFieldBuilder(field), options);
+ String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
+ merge(
+ entry.getValue(),
+ childPath,
+ (Message) source.getField(field),
+ destination.getFieldBuilder(field),
+ options);
continue;
}
if (field.isRepeated()) {
@@ -245,10 +253,15 @@ class FieldMaskTree {
} else {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (options.replaceMessageFields()) {
- destination.setField(field, source.getField(field));
+ if (!source.hasField(field)) {
+ destination.clearField(field);
+ } else {
+ destination.setField(field, source.getField(field));
+ }
} else {
- destination.getFieldBuilder(field).mergeFrom(
- (Message) source.getField(field));
+ if (source.hasField(field)) {
+ destination.getFieldBuilder(field).mergeFrom((Message) source.getField(field));
+ }
}
} else {
destination.setField(field, source.getField(field));
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 d13ff0ed..d7bf34a4 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
@@ -32,6 +32,7 @@ package com.google.protobuf.util;
import com.google.common.io.BaseEncoding;
import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
@@ -433,7 +434,7 @@ public class JsonFormat {
private final Gson gson;
private static class GsonHolder {
- private static final Gson DEFAULT_GSON = new Gson();
+ private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
}
PrinterImpl(
@@ -1066,6 +1067,15 @@ public class JsonFormat {
parser.mergeStruct(json, builder);
}
});
+ // Special-case ListValue.
+ parsers.put(ListValue.getDescriptor().getFullName(),
+ new WellKnownTypeParser() {
+ @Override
+ public void merge(ParserImpl parser, JsonElement json,
+ Message.Builder builder) throws InvalidProtocolBufferException {
+ parser.mergeListValue(json, builder);
+ }
+ });
// Special-case Value.
parsers.put(Value.getDescriptor().getFullName(),
new WellKnownTypeParser() {
@@ -1213,6 +1223,16 @@ public class JsonFormat {
}
mergeMapField(field, json, builder);
}
+
+ private void mergeListValue(JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ Descriptor descriptor = builder.getDescriptorForType();
+ FieldDescriptor field = descriptor.findFieldByName("values");
+ if (field == null) {
+ throw new InvalidProtocolBufferException("Invalid ListValue type.");
+ }
+ mergeRepeatedField(field, json, builder);
+ }
private void mergeValue(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
@@ -1237,9 +1257,7 @@ public class JsonFormat {
} else if (json instanceof JsonArray) {
FieldDescriptor field = type.findFieldByName("list_value");
Message.Builder listBuilder = builder.newBuilderForField(field);
- FieldDescriptor listField =
- listBuilder.getDescriptorForType().findFieldByName("values");
- mergeRepeatedField(listField, json, listBuilder);
+ merge(json, listBuilder);
builder.setField(field, listBuilder.build());
} else {
throw new IllegalStateException("Unexpected json data: " + json);
diff --git a/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java b/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java
index 3391f239..618e0072 100644
--- a/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java
@@ -61,19 +61,16 @@ public class FieldMaskTreeTest extends TestCase {
tree.addFieldPath("bar");
assertEquals("bar,foo", tree.toString());
}
-
+
public void testMergeFromFieldMask() throws Exception {
- FieldMaskTree tree = new FieldMaskTree(
- FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
+ FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
assertEquals("bar.baz,bar.quz,foo", tree.toString());
- tree.mergeFromFieldMask(
- FieldMaskUtil.fromString("foo.bar,bar"));
+ tree.mergeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar"));
assertEquals("bar,foo", tree.toString());
}
-
+
public void testIntersectFieldPath() throws Exception {
- FieldMaskTree tree = new FieldMaskTree(
- FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
+ FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
FieldMaskTree result = new FieldMaskTree();
// Empty path.
tree.intersectFieldPath("", result);
@@ -96,16 +93,18 @@ public class FieldMaskTreeTest extends TestCase {
}
public void testMerge() throws Exception {
- TestAllTypes value = TestAllTypes.newBuilder()
- .setOptionalInt32(1234)
- .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678))
- .addRepeatedInt32(4321)
- .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765))
- .build();
- NestedTestAllTypes source = NestedTestAllTypes.newBuilder()
- .setPayload(value)
- .setChild(NestedTestAllTypes.newBuilder().setPayload(value))
- .build();
+ TestAllTypes value =
+ TestAllTypes.newBuilder()
+ .setOptionalInt32(1234)
+ .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678))
+ .addRepeatedInt32(4321)
+ .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765))
+ .build();
+ NestedTestAllTypes source =
+ NestedTestAllTypes.newBuilder()
+ .setPayload(value)
+ .setChild(NestedTestAllTypes.newBuilder().setPayload(value))
+ .build();
// Now we have a message source with the following structure:
// [root] -+- payload -+- optional_int32
// | +- optional_nested_message
@@ -116,114 +115,127 @@ public class FieldMaskTreeTest extends TestCase {
// +- optional_nested_message
// +- repeated_int32
// +- repeated_nested_message
-
+
FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
-
+
// Test merging each individual field.
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("payload.optional_int32")
- .merge(source, builder, options);
+ new FieldMaskTree().addFieldPath("payload.optional_int32").merge(source, builder, options);
NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalInt32(1234);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("payload.optional_nested_message")
+ new FieldMaskTree()
+ .addFieldPath("payload.optional_nested_message")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
- expected.getPayloadBuilder().setOptionalNestedMessage(
- NestedMessage.newBuilder().setBb(5678));
+ expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
assertEquals(expected.build(), builder.build());
-
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("payload.repeated_int32")
- .merge(source, builder, options);
+ new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().addRepeatedInt32(4321);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("payload.repeated_nested_message")
+ new FieldMaskTree()
+ .addFieldPath("payload.repeated_nested_message")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
- expected.getPayloadBuilder().addRepeatedNestedMessage(
- NestedMessage.newBuilder().setBb(8765));
+ expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("child.payload.optional_int32")
+ new FieldMaskTree()
+ .addFieldPath("child.payload.optional_int32")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("child.payload.optional_nested_message")
+ new FieldMaskTree()
+ .addFieldPath("child.payload.optional_nested_message")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
- expected.getChildBuilder().getPayloadBuilder().setOptionalNestedMessage(
- NestedMessage.newBuilder().setBb(5678));
+ expected
+ .getChildBuilder()
+ .getPayloadBuilder()
+ .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
assertEquals(expected.build(), builder.build());
-
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("child.payload.repeated_int32")
+ new FieldMaskTree()
+ .addFieldPath("child.payload.repeated_int32")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321);
assertEquals(expected.build(), builder.build());
-
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message")
+ new FieldMaskTree()
+ .addFieldPath("child.payload.repeated_nested_message")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
- expected.getChildBuilder().getPayloadBuilder().addRepeatedNestedMessage(
- NestedMessage.newBuilder().setBb(8765));
+ expected
+ .getChildBuilder()
+ .getPayloadBuilder()
+ .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
assertEquals(expected.build(), builder.build());
-
+
// Test merging all fields.
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("child").addFieldPath("payload")
- .merge(source, builder, options);
+ new FieldMaskTree()
+ .addFieldPath("child")
+ .addFieldPath("payload")
+ .merge(source, builder, options);
assertEquals(source, builder.build());
-
+
// Test repeated options.
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().addRepeatedInt32(1000);
- new FieldMaskTree().addFieldPath("payload.repeated_int32")
- .merge(source, builder, options);
+ new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
// Default behavior is to append repeated fields.
assertEquals(2, builder.getPayload().getRepeatedInt32Count());
assertEquals(1000, builder.getPayload().getRepeatedInt32(0));
assertEquals(4321, builder.getPayload().getRepeatedInt32(1));
// Change to replace repeated fields.
options.setReplaceRepeatedFields(true);
- new FieldMaskTree().addFieldPath("payload.repeated_int32")
- .merge(source, builder, options);
+ new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
assertEquals(1, builder.getPayload().getRepeatedInt32Count());
assertEquals(4321, builder.getPayload().getRepeatedInt32(0));
-
+
// Test message options.
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
- new FieldMaskTree().addFieldPath("payload")
- .merge(source, builder, options);
+ new FieldMaskTree().addFieldPath("payload").merge(source, builder, options);
// Default behavior is to merge message fields.
assertEquals(1234, builder.getPayload().getOptionalInt32());
assertEquals(2000, builder.getPayload().getOptionalUint32());
-
+
+ // Test merging unset message fields.
+ NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build();
+ builder = NestedTestAllTypes.newBuilder();
+ new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options);
+ assertEquals(false, builder.hasPayload());
+
// Change to replace message fields.
options.setReplaceMessageFields(true);
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
- new FieldMaskTree().addFieldPath("payload")
- .merge(source, builder, options);
+ new FieldMaskTree().addFieldPath("payload").merge(source, builder, options);
assertEquals(1234, builder.getPayload().getOptionalInt32());
assertEquals(0, builder.getPayload().getOptionalUint32());
+
+ // Test merging unset message fields.
+ builder = NestedTestAllTypes.newBuilder();
+ builder.getPayloadBuilder().setOptionalInt32(1000);
+ builder.getPayloadBuilder().setOptionalUint32(2000);
+ new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options);
+ assertEquals(false, builder.hasPayload());
}
}
-
diff --git a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
index c0eb0330..d95b626c 100644
--- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
@@ -122,11 +122,11 @@ public class JsonFormatTest extends TestCase {
builder.addRepeatedNestedEnum(NestedEnum.BAZ);
builder.addRepeatedNestedMessageBuilder().setValue(200);
}
-
+
private void assertRoundTripEquals(Message message) throws Exception {
assertRoundTripEquals(message, TypeRegistry.getEmptyTypeRegistry());
}
-
+
private void assertRoundTripEquals(Message message, TypeRegistry registry) throws Exception {
JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(registry);
@@ -135,68 +135,68 @@ public class JsonFormatTest extends TestCase {
Message parsedMessage = builder.build();
assertEquals(message.toString(), parsedMessage.toString());
}
-
+
private String toJsonString(Message message) throws IOException {
return JsonFormat.printer().print(message);
}
-
+
private void mergeFromJson(String json, Message.Builder builder) throws IOException {
JsonFormat.parser().merge(json, builder);
}
-
+
public void testAllFields() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
setAllFields(builder);
TestAllTypes message = builder.build();
-
- assertEquals(
+
+ assertEquals(
"{\n"
- + " \"optionalInt32\": 1234,\n"
- + " \"optionalInt64\": \"1234567890123456789\",\n"
- + " \"optionalUint32\": 5678,\n"
- + " \"optionalUint64\": \"2345678901234567890\",\n"
- + " \"optionalSint32\": 9012,\n"
- + " \"optionalSint64\": \"3456789012345678901\",\n"
- + " \"optionalFixed32\": 3456,\n"
- + " \"optionalFixed64\": \"4567890123456789012\",\n"
- + " \"optionalSfixed32\": 7890,\n"
- + " \"optionalSfixed64\": \"5678901234567890123\",\n"
- + " \"optionalFloat\": 1.5,\n"
- + " \"optionalDouble\": 1.25,\n"
- + " \"optionalBool\": true,\n"
- + " \"optionalString\": \"Hello world!\",\n"
- + " \"optionalBytes\": \"AAEC\",\n"
- + " \"optionalNestedMessage\": {\n"
- + " \"value\": 100\n"
- + " },\n"
- + " \"optionalNestedEnum\": \"BAR\",\n"
- + " \"repeatedInt32\": [1234, 234],\n"
- + " \"repeatedInt64\": [\"1234567890123456789\", \"234567890123456789\"],\n"
- + " \"repeatedUint32\": [5678, 678],\n"
- + " \"repeatedUint64\": [\"2345678901234567890\", \"345678901234567890\"],\n"
- + " \"repeatedSint32\": [9012, 10],\n"
- + " \"repeatedSint64\": [\"3456789012345678901\", \"456789012345678901\"],\n"
- + " \"repeatedFixed32\": [3456, 456],\n"
- + " \"repeatedFixed64\": [\"4567890123456789012\", \"567890123456789012\"],\n"
- + " \"repeatedSfixed32\": [7890, 890],\n"
- + " \"repeatedSfixed64\": [\"5678901234567890123\", \"678901234567890123\"],\n"
- + " \"repeatedFloat\": [1.5, 11.5],\n"
- + " \"repeatedDouble\": [1.25, 11.25],\n"
- + " \"repeatedBool\": [true, true],\n"
- + " \"repeatedString\": [\"Hello world!\", \"ello world!\"],\n"
- + " \"repeatedBytes\": [\"AAEC\", \"AQI=\"],\n"
- + " \"repeatedNestedMessage\": [{\n"
- + " \"value\": 100\n"
- + " }, {\n"
- + " \"value\": 200\n"
- + " }],\n"
- + " \"repeatedNestedEnum\": [\"BAR\", \"BAZ\"]\n"
- + "}",
+ + " \"optionalInt32\": 1234,\n"
+ + " \"optionalInt64\": \"1234567890123456789\",\n"
+ + " \"optionalUint32\": 5678,\n"
+ + " \"optionalUint64\": \"2345678901234567890\",\n"
+ + " \"optionalSint32\": 9012,\n"
+ + " \"optionalSint64\": \"3456789012345678901\",\n"
+ + " \"optionalFixed32\": 3456,\n"
+ + " \"optionalFixed64\": \"4567890123456789012\",\n"
+ + " \"optionalSfixed32\": 7890,\n"
+ + " \"optionalSfixed64\": \"5678901234567890123\",\n"
+ + " \"optionalFloat\": 1.5,\n"
+ + " \"optionalDouble\": 1.25,\n"
+ + " \"optionalBool\": true,\n"
+ + " \"optionalString\": \"Hello world!\",\n"
+ + " \"optionalBytes\": \"AAEC\",\n"
+ + " \"optionalNestedMessage\": {\n"
+ + " \"value\": 100\n"
+ + " },\n"
+ + " \"optionalNestedEnum\": \"BAR\",\n"
+ + " \"repeatedInt32\": [1234, 234],\n"
+ + " \"repeatedInt64\": [\"1234567890123456789\", \"234567890123456789\"],\n"
+ + " \"repeatedUint32\": [5678, 678],\n"
+ + " \"repeatedUint64\": [\"2345678901234567890\", \"345678901234567890\"],\n"
+ + " \"repeatedSint32\": [9012, 10],\n"
+ + " \"repeatedSint64\": [\"3456789012345678901\", \"456789012345678901\"],\n"
+ + " \"repeatedFixed32\": [3456, 456],\n"
+ + " \"repeatedFixed64\": [\"4567890123456789012\", \"567890123456789012\"],\n"
+ + " \"repeatedSfixed32\": [7890, 890],\n"
+ + " \"repeatedSfixed64\": [\"5678901234567890123\", \"678901234567890123\"],\n"
+ + " \"repeatedFloat\": [1.5, 11.5],\n"
+ + " \"repeatedDouble\": [1.25, 11.25],\n"
+ + " \"repeatedBool\": [true, true],\n"
+ + " \"repeatedString\": [\"Hello world!\", \"ello world!\"],\n"
+ + " \"repeatedBytes\": [\"AAEC\", \"AQI=\"],\n"
+ + " \"repeatedNestedMessage\": [{\n"
+ + " \"value\": 100\n"
+ + " }, {\n"
+ + " \"value\": 200\n"
+ + " }],\n"
+ + " \"repeatedNestedEnum\": [\"BAR\", \"BAZ\"]\n"
+ + "}",
toJsonString(message));
-
+
assertRoundTripEquals(message);
}
-
+
public void testUnknownEnumValues() throws Exception {
TestAllTypes message = TestAllTypes.newBuilder()
.setOptionalNestedEnumValue(12345)
@@ -209,21 +209,22 @@ public class JsonFormatTest extends TestCase {
+ " \"repeatedNestedEnum\": [12345, \"FOO\"]\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
-
+
TestMap.Builder mapBuilder = TestMap.newBuilder();
mapBuilder.getMutableInt32ToEnumMapValue().put(1, 0);
mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345);
TestMap mapMessage = mapBuilder.build();
assertEquals(
- "{\n"
- + " \"int32ToEnumMap\": {\n"
- + " \"1\": \"FOO\",\n"
- + " \"2\": 12345\n"
- + " }\n"
- + "}", toJsonString(mapMessage));
+ "{\n"
+ + " \"int32ToEnumMap\": {\n"
+ + " \"1\": \"FOO\",\n"
+ + " \"2\": 12345\n"
+ + " }\n"
+ + "}",
+ toJsonString(mapMessage));
assertRoundTripEquals(mapMessage);
}
-
+
public void testSpecialFloatValues() throws Exception {
TestAllTypes message = TestAllTypes.newBuilder()
.addRepeatedFloat(Float.NaN)
@@ -238,10 +239,10 @@ public class JsonFormatTest extends TestCase {
+ " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n"
+ " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n"
+ "}", toJsonString(message));
-
+
assertRoundTripEquals(message);
}
-
+
public void testParserAcceptStringForNumbericField() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
@@ -265,7 +266,7 @@ public class JsonFormatTest extends TestCase {
assertEquals(1.25, message.getOptionalDouble());
assertEquals(true, message.getOptionalBool());
}
-
+
public void testParserAcceptFloatingPointValueForIntegerField() throws Exception {
// Test that numeric values like "1.000", "1e5" will also be accepted.
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@@ -287,14 +288,14 @@ public class JsonFormatTest extends TestCase {
assertEquals(expectedValues[i], builder.getRepeatedInt64(i));
assertEquals(expectedValues[i], builder.getRepeatedUint64(i));
}
-
+
// Non-integers will still be rejected.
assertRejects("optionalInt32", "1.5");
assertRejects("optionalUint32", "1.5");
assertRejects("optionalInt64", "1.5");
assertRejects("optionalUint64", "1.5");
}
-
+
private void assertRejects(String name, String value) {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
try {
@@ -312,7 +313,7 @@ public class JsonFormatTest extends TestCase {
// Expected.
}
}
-
+
private void assertAccepts(String name, String value) throws IOException {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
// Both numeric form and string form are accepted.
@@ -320,17 +321,17 @@ public class JsonFormatTest extends TestCase {
builder.clear();
mergeFromJson("{\"" + name + "\":\"" + value + "\"}", builder);
}
-
+
public void testParserRejectOutOfRangeNumericValues() throws Exception {
assertAccepts("optionalInt32", String.valueOf(Integer.MAX_VALUE));
assertAccepts("optionalInt32", String.valueOf(Integer.MIN_VALUE));
assertRejects("optionalInt32", String.valueOf(Integer.MAX_VALUE + 1L));
assertRejects("optionalInt32", String.valueOf(Integer.MIN_VALUE - 1L));
-
+
assertAccepts("optionalUint32", String.valueOf(Integer.MAX_VALUE + 1L));
assertRejects("optionalUint32", "123456789012345");
assertRejects("optionalUint32", "-1");
-
+
BigInteger one = new BigInteger("1");
BigInteger maxLong = new BigInteger(String.valueOf(Long.MAX_VALUE));
BigInteger minLong = new BigInteger(String.valueOf(Long.MIN_VALUE));
@@ -351,7 +352,7 @@ public class JsonFormatTest extends TestCase {
assertAccepts("optionalFloat", String.valueOf(-Float.MAX_VALUE));
assertRejects("optionalFloat", String.valueOf(Double.MAX_VALUE));
assertRejects("optionalFloat", String.valueOf(-Double.MAX_VALUE));
-
+
BigDecimal moreThanOne = new BigDecimal("1.000001");
BigDecimal maxDouble = new BigDecimal(Double.MAX_VALUE);
BigDecimal minDouble = new BigDecimal(-Double.MAX_VALUE);
@@ -360,7 +361,7 @@ public class JsonFormatTest extends TestCase {
assertRejects("optionalDouble", maxDouble.multiply(moreThanOne).toString());
assertRejects("optionalDouble", minDouble.multiply(moreThanOne).toString());
}
-
+
public void testParserAcceptNull() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
@@ -402,7 +403,7 @@ public class JsonFormatTest extends TestCase {
+ "}", builder);
TestAllTypes message = builder.build();
assertEquals(TestAllTypes.getDefaultInstance(), message);
-
+
// Repeated field elements cannot be null.
try {
builder = TestAllTypes.newBuilder();
@@ -414,7 +415,7 @@ public class JsonFormatTest extends TestCase {
} catch (InvalidProtocolBufferException e) {
// Exception expected.
}
-
+
try {
builder = TestAllTypes.newBuilder();
mergeFromJson(
@@ -426,14 +427,14 @@ public class JsonFormatTest extends TestCase {
// Exception expected.
}
}
-
+
public void testParserRejectDuplicatedFields() throws Exception {
// TODO(xiaofeng): The parser we are currently using (GSON) will accept and keep the last
// one if multiple entries have the same name. This is not the desired behavior but it can
// only be fixed by using our own parser. Here we only test the cases where the names are
// different but still referring to the same field.
-
- // Duplicated optional fields.
+
+ // Duplicated optional fields.
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
@@ -445,7 +446,7 @@ public class JsonFormatTest extends TestCase {
} catch (InvalidProtocolBufferException e) {
// Exception expected.
}
-
+
// Duplicated repeated fields.
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@@ -458,7 +459,7 @@ public class JsonFormatTest extends TestCase {
} catch (InvalidProtocolBufferException e) {
// Exception expected.
}
-
+
// Duplicated oneof fields.
try {
TestOneof.Builder builder = TestOneof.newBuilder();
@@ -472,7 +473,7 @@ public class JsonFormatTest extends TestCase {
// Exception expected.
}
}
-
+
public void testMapFields() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
builder.getMutableInt32ToInt32Map().put(1, 10);
@@ -507,7 +508,7 @@ public class JsonFormatTest extends TestCase {
8, NestedMessage.newBuilder().setValue(1234).build());
builder.getMutableInt32ToEnumMap().put(9, NestedEnum.BAR);
TestMap message = builder.build();
-
+
assertEquals(
"{\n"
+ " \"int32ToInt32Map\": {\n"
@@ -598,13 +599,13 @@ public class JsonFormatTest extends TestCase {
+ " }\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
-
+
// Test multiple entries.
builder = TestMap.newBuilder();
builder.getMutableInt32ToInt32Map().put(1, 2);
builder.getMutableInt32ToInt32Map().put(3, 4);
message = builder.build();
-
+
assertEquals(
"{\n"
+ " \"int32ToInt32Map\": {\n"
@@ -614,7 +615,7 @@ public class JsonFormatTest extends TestCase {
+ "}", toJsonString(message));
assertRoundTripEquals(message);
}
-
+
public void testMapNullValueIsRejected() throws Exception {
try {
TestMap.Builder builder = TestMap.newBuilder();
@@ -627,7 +628,7 @@ public class JsonFormatTest extends TestCase {
} catch (InvalidProtocolBufferException e) {
// Exception expected.
}
-
+
try {
TestMap.Builder builder = TestMap.newBuilder();
mergeFromJson(
@@ -640,7 +641,7 @@ public class JsonFormatTest extends TestCase {
// Exception expected.
}
}
-
+
public void testParserAcceptNonQuotedObjectKey() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
mergeFromJson(
@@ -652,7 +653,7 @@ public class JsonFormatTest extends TestCase {
assertEquals(2, message.getInt32ToInt32Map().get(1).intValue());
assertEquals(3, message.getStringToInt32Map().get("hello").intValue());
}
-
+
public void testWrappers() throws Exception {
TestWrappers.Builder builder = TestWrappers.newBuilder();
builder.getBoolValueBuilder().setValue(false);
@@ -665,7 +666,7 @@ public class JsonFormatTest extends TestCase {
builder.getStringValueBuilder().setValue("");
builder.getBytesValueBuilder().setValue(ByteString.EMPTY);
TestWrappers message = builder.build();
-
+
assertEquals(
"{\n"
+ " \"int32Value\": 0,\n"
@@ -691,7 +692,7 @@ public class JsonFormatTest extends TestCase {
builder.getStringValueBuilder().setValue("7");
builder.getBytesValueBuilder().setValue(ByteString.copyFrom(new byte[]{8}));
message = builder.build();
-
+
assertEquals(
"{\n"
+ " \"int32Value\": 1,\n"
@@ -706,43 +707,43 @@ public class JsonFormatTest extends TestCase {
+ "}", toJsonString(message));
assertRoundTripEquals(message);
}
-
+
public void testTimestamp() throws Exception {
TestTimestamp message = TestTimestamp.newBuilder()
.setTimestampValue(TimeUtil.parseTimestamp("1970-01-01T00:00:00Z"))
.build();
-
+
assertEquals(
"{\n"
+ " \"timestampValue\": \"1970-01-01T00:00:00Z\"\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
}
-
+
public void testDuration() throws Exception {
TestDuration message = TestDuration.newBuilder()
.setDurationValue(TimeUtil.parseDuration("12345s"))
.build();
-
+
assertEquals(
"{\n"
+ " \"durationValue\": \"12345s\"\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
}
-
+
public void testFieldMask() throws Exception {
TestFieldMask message = TestFieldMask.newBuilder()
.setFieldMaskValue(FieldMaskUtil.fromString("foo.bar,baz"))
.build();
-
+
assertEquals(
"{\n"
+ " \"fieldMaskValue\": \"foo.bar,baz\"\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
}
-
+
public void testStruct() throws Exception {
// Build a struct with all possible values.
TestStruct.Builder builder = TestStruct.newBuilder();
@@ -764,7 +765,7 @@ public class JsonFormatTest extends TestCase {
structBuilder.getMutableFields().put(
"list_value", Value.newBuilder().setListValue(listBuilder.build()).build());
TestStruct message = builder.build();
-
+
assertEquals(
"{\n"
+ " \"structValue\": {\n"
@@ -778,7 +779,7 @@ public class JsonFormatTest extends TestCase {
+ " }\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
-
+
builder = TestStruct.newBuilder();
builder.setValue(Value.newBuilder().setNullValueValue(0).build());
message = builder.build();
@@ -787,12 +788,23 @@ public class JsonFormatTest extends TestCase {
+ " \"value\": null\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
+
+ builder = TestStruct.newBuilder();
+ listBuilder = builder.getListValueBuilder();
+ listBuilder.addValues(Value.newBuilder().setNumberValue(31831.125).build());
+ listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build());
+ message = builder.build();
+ assertEquals(
+ "{\n"
+ + " \"listValue\": [31831.125, null]\n"
+ + "}", toJsonString(message));
+ assertRoundTripEquals(message);
}
-
+
public void testAnyFields() throws Exception {
TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build();
TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build();
-
+
// A TypeRegistry must be provided in order to convert Any types.
try {
toJsonString(message);
@@ -800,11 +812,11 @@ public class JsonFormatTest extends TestCase {
} catch (IOException e) {
// Expected.
}
-
+
JsonFormat.TypeRegistry registry = JsonFormat.TypeRegistry.newBuilder()
.add(TestAllTypes.getDescriptor()).build();
JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
-
+
assertEquals(
"{\n"
+ " \"anyValue\": {\n"
@@ -813,8 +825,8 @@ public class JsonFormatTest extends TestCase {
+ " }\n"
+ "}" , printer.print(message));
assertRoundTripEquals(message, registry);
-
-
+
+
// Well-known types have a special formatting when embedded in Any.
//
// 1. Any in Any.
@@ -828,7 +840,7 @@ public class JsonFormatTest extends TestCase {
+ " }\n"
+ "}", printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
-
+
// 2. Wrappers in Any.
anyMessage = Any.pack(Int32Value.newBuilder().setValue(12345).build());
assertEquals(
@@ -894,7 +906,7 @@ public class JsonFormatTest extends TestCase {
+ " \"value\": \"AQI=\"\n"
+ "}", printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
-
+
// 3. Timestamp in Any.
anyMessage = Any.pack(TimeUtil.parseTimestamp("1969-12-31T23:59:59Z"));
assertEquals(
@@ -903,7 +915,7 @@ public class JsonFormatTest extends TestCase {
+ " \"value\": \"1969-12-31T23:59:59Z\"\n"
+ "}", printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
-
+
// 4. Duration in Any
anyMessage = Any.pack(TimeUtil.parseDuration("12345.10s"));
assertEquals(
@@ -945,7 +957,7 @@ public class JsonFormatTest extends TestCase {
+ "}", printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
}
-
+
public void testParserMissingTypeUrl() throws Exception {
try {
Any.Builder builder = Any.newBuilder();
@@ -958,7 +970,7 @@ public class JsonFormatTest extends TestCase {
// Expected.
}
}
-
+
public void testParserUnexpectedTypeUrl() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@@ -970,9 +982,9 @@ public class JsonFormatTest extends TestCase {
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
- }
+ }
}
-
+
public void testParserRejectTrailingComma() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@@ -1000,13 +1012,13 @@ public class JsonFormatTest extends TestCase {
// // Expected.
// }
}
-
+
public void testParserRejectInvalidBase64() throws Exception {
assertRejects("optionalBytes", "!@#$");
// We use standard BASE64 with paddings.
assertRejects("optionalBytes", "AQI");
}
-
+
public void testParserRejectInvalidEnumValue() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@@ -1017,7 +1029,7 @@ public class JsonFormatTest extends TestCase {
fail("Exception is expected.");
} catch (InvalidProtocolBufferException e) {
// Expected.
- }
+ }
}
public void testCustomJsonName() throws Exception {
@@ -1026,6 +1038,12 @@ public class JsonFormatTest extends TestCase {
assertRoundTripEquals(message);
}
+ public void testDefaultGsonDoesNotHtmlEscape() throws Exception {
+ TestAllTypes message = TestAllTypes.newBuilder().setOptionalString("=").build();
+ assertEquals(
+ "{\n" + " \"optionalString\": \"=\"" + "\n}", JsonFormat.printer().print(message));
+ }
+
public void testIncludingDefaultValueFields() throws Exception {
TestAllTypes message = TestAllTypes.getDefaultInstance();
assertEquals("{\n}", JsonFormat.printer().print(message));
diff --git a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
index 509c1d69..686edc42 100644
--- a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
+++ b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
@@ -28,6 +28,36 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package json_test;
@@ -159,6 +189,7 @@ message TestFieldMask {
message TestStruct {
google.protobuf.Struct struct_value = 1;
google.protobuf.Value value = 2;
+ google.protobuf.ListValue list_value = 3;
}
message TestAny {