aboutsummaryrefslogtreecommitdiffhomepage
path: root/csharp
diff options
context:
space:
mode:
authorGravatar Jon Skeet <jonskeet@google.com>2015-07-20 11:48:24 +0100
committerGravatar Jon Skeet <jonskeet@google.com>2015-07-20 11:48:24 +0100
commitc9fd53a3b742f2a34c527cbe0833c5bc081e6ec3 (patch)
tree410dee597653917b53ff1d7646b92deb5b15ffda /csharp
parent3f5df7a74b9d6989d0ea0cb0664f0105d80767eb (diff)
First part of JSON formatting for well-known types. I think we need a reflection API rethink before doing the rest.
Diffstat (limited to 'csharp')
-rw-r--r--csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs27
-rw-r--r--csharp/src/Google.Protobuf/JsonFormatter.cs29
-rw-r--r--csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs25
3 files changed, 80 insertions, 1 deletions
diff --git a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
index 5441bf47..a6715698 100644
--- a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
+++ b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
@@ -257,5 +257,32 @@ namespace Google.Protobuf
formatter = new JsonFormatter(new JsonFormatter.Settings(true));
Assert.AreEqual(expectedJson, formatter.Format(message));
}
+
+ [Test]
+ public void WrapperFormatting_Single()
+ {
+ // Just a few examples, handling both classes and value types, and
+ // default vs non-default values
+ var message = new TestWellKnownTypes
+ {
+ Int64Field = 10,
+ Int32Field = 0,
+ BytesField = ByteString.FromBase64("ABCD"),
+ StringField = ""
+ };
+ var expectedJson = "{ \"int64Field\": \"10\", \"int32Field\": 0, \"stringField\": \"\", \"bytesField\": \"ABCD\" }";
+ Assert.AreEqual(expectedJson, JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void WrapperFormatting_IncludeNull()
+ {
+ // The actual JSON here is very large because there are lots of fields. Just test a couple of them.
+ var message = new TestWellKnownTypes { Int32Field = 10 };
+ var formatter = new JsonFormatter(new JsonFormatter.Settings(true));
+ var actualJson = formatter.Format(message);
+ Assert.IsTrue(actualJson.Contains("\"int64Field\": null"));
+ Assert.IsFalse(actualJson.Contains("\"int32Field\": null"));
+ }
}
}
diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs
index dacc7221..a06e6545 100644
--- a/csharp/src/Google.Protobuf/JsonFormatter.cs
+++ b/csharp/src/Google.Protobuf/JsonFormatter.cs
@@ -35,6 +35,7 @@ using System.Collections;
using System.Globalization;
using System.Text;
using Google.Protobuf.Reflection;
+using Google.Protobuf.WellKnownTypes;
namespace Google.Protobuf
{
@@ -121,6 +122,9 @@ namespace Google.Protobuf
{
ThrowHelper.ThrowIfNull(message, "message");
StringBuilder builder = new StringBuilder();
+ // TODO(jonskeet): Handle well-known types here.
+ // Our reflection support needs improving so that we can get at the descriptor
+ // to find out whether *this* message is a well-known type.
WriteMessage(builder, message);
return builder.ToString();
}
@@ -375,13 +379,36 @@ namespace Google.Protobuf
break;
case FieldType.Message:
case FieldType.Group: // Never expect to get this, but...
- WriteMessage(builder, (IReflectedMessage) value);
+ if (descriptor.MessageType.IsWellKnownType)
+ {
+ WriteWellKnownTypeValue(builder, descriptor, value);
+ }
+ else
+ {
+ WriteMessage(builder, (IReflectedMessage) value);
+ }
break;
default:
throw new ArgumentException("Invalid field type: " + descriptor.FieldType);
}
}
+ /// <summary>
+ /// Central interception point for well-known type formatting. Any well-known types which
+ /// don't need special handling can fall back to WriteMessage.
+ /// </summary>
+ private void WriteWellKnownTypeValue(StringBuilder builder, FieldDescriptor descriptor, object value)
+ {
+ // For wrapper types, the value will be the (possibly boxed) "native" value,
+ // so we can write it as if we were unconditionally writing the Value field for the wrapper type.
+ if (descriptor.MessageType.File == Int32Value.Descriptor.File && value != null)
+ {
+ WriteSingleValue(builder, descriptor.MessageType.FindFieldByNumber(1), value);
+ return;
+ }
+ WriteMessage(builder, (IReflectedMessage) value);
+ }
+
private void WriteList(StringBuilder builder, IFieldAccessor accessor, IList list)
{
builder.Append("[ ");
diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
index b6351d36..1c22c460 100644
--- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
@@ -40,6 +40,20 @@ namespace Google.Protobuf.Reflection
/// </summary>
public sealed class MessageDescriptor : DescriptorBase
{
+ private static readonly HashSet<string> WellKnownTypeNames = new HashSet<string>
+ {
+ "google/protobuf/any.proto",
+ "google/protobuf/api.proto",
+ "google/protobuf/duration.proto",
+ "google/protobuf/empty.proto",
+ "google/protobuf/wrappers.proto",
+ "google/protobuf/timestamp.proto",
+ "google/protobuf/field_mask.proto",
+ "google/protobuf/source_context.proto",
+ "google/protobuf/struct.proto",
+ "google/protobuf/type.proto",
+ };
+
private readonly DescriptorProto proto;
private readonly MessageDescriptor containingType;
private readonly IList<MessageDescriptor> nestedTypes;
@@ -79,6 +93,17 @@ namespace Google.Protobuf.Reflection
internal DescriptorProto Proto { get { return proto; } }
+ /// <summary>
+ /// Returns whether this message is one of the "well known types" which may have runtime/protoc support.
+ /// </summary>
+ internal bool IsWellKnownType
+ {
+ get
+ {
+ return File.Package == "google.protobuf" && WellKnownTypeNames.Contains(File.Name);
+ }
+ }
+
/// <value>
/// If this is a nested type, get the outer descriptor, otherwise null.
/// </value>