First part of JSON formatting for well-known types. I think we need a reflection API rethink before doing the rest.
This commit is contained in:
parent
3f5df7a74b
commit
c9fd53a3b7
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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("[ ");
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user