Initial pass at formatting Struct as JSON.
This seems remarkably little code, but it appears to work. I can add tests for invalid structs at some point, once the general approach is approved.
This commit is contained in:
parent
115e6c735e
commit
e7caf15577
@ -368,7 +368,24 @@ namespace Google.Protobuf
|
||||
{
|
||||
var message = new TestWellKnownTypes { DurationField = new Duration() };
|
||||
AssertJson("{ 'durationField': '0s' }", JsonFormatter.Default.Format(message));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StructSample()
|
||||
{
|
||||
var message = new Struct
|
||||
{
|
||||
Fields =
|
||||
{
|
||||
{ "a", new Value { NullValue = new NullValue() } },
|
||||
{ "b", new Value { BoolValue = false } },
|
||||
{ "c", new Value { NumberValue = 10.5 } },
|
||||
{ "d", new Value { StringValue = "text" } },
|
||||
{ "e", new Value { ListValue = new ListValue { Values = { new Value { StringValue = "t1" }, new Value { NumberValue = 5 } } } } },
|
||||
{ "f", new Value { StructValue = new Struct { Fields = { { "nested", new Value { StringValue = "value" } } } } } }
|
||||
}
|
||||
};
|
||||
AssertJson("{ 'a': null, 'b': false, 'c': 10.5, 'd': 'text', 'e': [ 't1', 5 ], 'f': { 'nested': 'value' } }", message.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -380,23 +380,44 @@ namespace Google.Protobuf
|
||||
/// </summary>
|
||||
private void WriteWellKnownTypeValue(StringBuilder builder, MessageDescriptor descriptor, object value, bool inField)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
WriteNull(builder);
|
||||
return;
|
||||
}
|
||||
// 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.File == Int32Value.Descriptor.File && value != null)
|
||||
if (descriptor.File == Int32Value.Descriptor.File)
|
||||
{
|
||||
WriteSingleValue(builder, descriptor.FindFieldByNumber(1), value);
|
||||
return;
|
||||
}
|
||||
if (descriptor.FullName == Timestamp.Descriptor.FullName && value != null)
|
||||
if (descriptor.FullName == Timestamp.Descriptor.FullName)
|
||||
{
|
||||
MaybeWrapInString(builder, value, WriteTimestamp, inField);
|
||||
return;
|
||||
}
|
||||
if (descriptor.FullName == Duration.Descriptor.FullName && value != null)
|
||||
if (descriptor.FullName == Duration.Descriptor.FullName)
|
||||
{
|
||||
MaybeWrapInString(builder, value, WriteDuration, inField);
|
||||
return;
|
||||
}
|
||||
if (descriptor.FullName == Struct.Descriptor.FullName)
|
||||
{
|
||||
WriteStruct(builder, (IMessage) value);
|
||||
return;
|
||||
}
|
||||
if (descriptor.FullName == ListValue.Descriptor.FullName)
|
||||
{
|
||||
var fieldAccessor = descriptor.Fields[ListValue.ValuesFieldNumber].Accessor;
|
||||
WriteList(builder, fieldAccessor, (IList) fieldAccessor.GetValue(value));
|
||||
return;
|
||||
}
|
||||
if (descriptor.FullName == Value.Descriptor.FullName)
|
||||
{
|
||||
WriteStructFieldValue(builder, (IMessage) value);
|
||||
return;
|
||||
}
|
||||
WriteMessage(builder, (IMessage) value);
|
||||
}
|
||||
|
||||
@ -483,6 +504,63 @@ namespace Google.Protobuf
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteStruct(StringBuilder builder, IMessage message)
|
||||
{
|
||||
builder.Append("{ ");
|
||||
IDictionary fields = (IDictionary) message.Descriptor.Fields[Struct.FieldsFieldNumber].Accessor.GetValue(message);
|
||||
bool first = true;
|
||||
foreach (DictionaryEntry entry in fields)
|
||||
{
|
||||
string key = (string) entry.Key;
|
||||
IMessage value = (IMessage) entry.Value;
|
||||
if (string.IsNullOrEmpty(key) || value == null)
|
||||
{
|
||||
throw new InvalidOperationException("Struct fields cannot have an empty key or a null value.");
|
||||
}
|
||||
|
||||
if (!first)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
WriteString(builder, key);
|
||||
builder.Append(": ");
|
||||
WriteStructFieldValue(builder, value);
|
||||
first = false;
|
||||
}
|
||||
builder.Append(first ? "}" : " }");
|
||||
}
|
||||
|
||||
private void WriteStructFieldValue(StringBuilder builder, IMessage message)
|
||||
{
|
||||
var specifiedField = message.Descriptor.Oneofs[0].Accessor.GetCaseFieldDescriptor(message);
|
||||
if (specifiedField == null)
|
||||
{
|
||||
throw new InvalidOperationException("Value message must contain a value for the oneof.");
|
||||
}
|
||||
|
||||
object value = specifiedField.Accessor.GetValue(message);
|
||||
|
||||
switch (specifiedField.FieldNumber)
|
||||
{
|
||||
case Value.BoolValueFieldNumber:
|
||||
case Value.StringValueFieldNumber:
|
||||
case Value.NumberValueFieldNumber:
|
||||
WriteSingleValue(builder, specifiedField, value);
|
||||
return;
|
||||
case Value.StructValueFieldNumber:
|
||||
case Value.ListValueFieldNumber:
|
||||
// Structs and ListValues are nested messages, and already well-known types.
|
||||
var nestedMessage = (IMessage) specifiedField.Accessor.GetValue(message);
|
||||
WriteWellKnownTypeValue(builder, nestedMessage.Descriptor, nestedMessage, true);
|
||||
return;
|
||||
case Value.NullValueFieldNumber:
|
||||
WriteNull(builder);
|
||||
return;
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected case in struct field: " + specifiedField.FieldNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteList(StringBuilder builder, IFieldAccessor accessor, IList list)
|
||||
{
|
||||
builder.Append("[ ");
|
||||
|
Loading…
Reference in New Issue
Block a user