Make sure that

"valueField": null

is parsed appropriately, i.e. that it remembers that the field is set.
pull/1145/head
Jon Skeet 9 years ago
parent 8866d6a80e
commit b1ea15f7a5
  1. 12
      csharp/src/Google.Protobuf.Test/JsonParserTest.cs
  2. 43
      csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
  3. 17
      csharp/src/Google.Protobuf/JsonParser.cs
  4. 2
      src/google/protobuf/unittest_well_known_types.proto

@ -124,7 +124,9 @@ namespace Google.Protobuf
[Test] [Test]
public void SingularWrappers_ExplicitNulls() public void SingularWrappers_ExplicitNulls()
{ {
var message = new TestWellKnownTypes(); // When we parse the "valueField": null part, we remember it... basically, it's one case
// where explicit default values don't fully roundtrip.
var message = new TestWellKnownTypes { ValueField = Value.ForNull() };
var json = new JsonFormatter(new JsonFormatter.Settings(true)).Format(message); var json = new JsonFormatter(new JsonFormatter.Settings(true)).Format(message);
var parsed = JsonParser.Default.Parse<TestWellKnownTypes>(json); var parsed = JsonParser.Default.Parse<TestWellKnownTypes>(json);
Assert.AreEqual(message, parsed); Assert.AreEqual(message, parsed);
@ -150,6 +152,14 @@ namespace Google.Protobuf
Assert.AreEqual(expected, parsed); Assert.AreEqual(expected, parsed);
} }
[Test]
public void ExplicitNullValue()
{
string json = "{\"valueField\": null}";
var message = JsonParser.Default.Parse<TestWellKnownTypes>(json);
Assert.AreEqual(new TestWellKnownTypes { ValueField = Value.ForNull() }, message);
}
[Test] [Test]
public void BytesWrapper_Standalone() public void BytesWrapper_Standalone()
{ {

@ -31,7 +31,7 @@ namespace Google.Protobuf.TestProtos {
"L3Byb3RvYnVmL3NvdXJjZV9jb250ZXh0LnByb3RvGhxnb29nbGUvcHJvdG9i", "L3Byb3RvYnVmL3NvdXJjZV9jb250ZXh0LnByb3RvGhxnb29nbGUvcHJvdG9i",
"dWYvc3RydWN0LnByb3RvGh9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnBy", "dWYvc3RydWN0LnByb3RvGh9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnBy",
"b3RvGhpnb29nbGUvcHJvdG9idWYvdHlwZS5wcm90bxoeZ29vZ2xlL3Byb3Rv", "b3RvGhpnb29nbGUvcHJvdG9idWYvdHlwZS5wcm90bxoeZ29vZ2xlL3Byb3Rv",
"YnVmL3dyYXBwZXJzLnByb3RvIpEHChJUZXN0V2VsbEtub3duVHlwZXMSJwoJ", "YnVmL3dyYXBwZXJzLnByb3RvIr4HChJUZXN0V2VsbEtub3duVHlwZXMSJwoJ",
"YW55X2ZpZWxkGAEgASgLMhQuZ29vZ2xlLnByb3RvYnVmLkFueRInCglhcGlf", "YW55X2ZpZWxkGAEgASgLMhQuZ29vZ2xlLnByb3RvYnVmLkFueRInCglhcGlf",
"ZmllbGQYAiABKAsyFC5nb29nbGUucHJvdG9idWYuQXBpEjEKDmR1cmF0aW9u", "ZmllbGQYAiABKAsyFC5nb29nbGUucHJvdG9idWYuQXBpEjEKDmR1cmF0aW9u",
"X2ZpZWxkGAMgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uEisKC2Vt", "X2ZpZWxkGAMgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uEisKC2Vt",
@ -51,7 +51,8 @@ namespace Google.Protobuf.TestProtos {
"cm90b2J1Zi5VSW50MzJWYWx1ZRIuCgpib29sX2ZpZWxkGBAgASgLMhouZ29v", "cm90b2J1Zi5VSW50MzJWYWx1ZRIuCgpib29sX2ZpZWxkGBAgASgLMhouZ29v",
"Z2xlLnByb3RvYnVmLkJvb2xWYWx1ZRIyCgxzdHJpbmdfZmllbGQYESABKAsy", "Z2xlLnByb3RvYnVmLkJvb2xWYWx1ZRIyCgxzdHJpbmdfZmllbGQYESABKAsy",
"HC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUSMAoLYnl0ZXNfZmllbGQY", "HC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUSMAoLYnl0ZXNfZmllbGQY",
"EiABKAsyGy5nb29nbGUucHJvdG9idWYuQnl0ZXNWYWx1ZSKVBwoWUmVwZWF0", "EiABKAsyGy5nb29nbGUucHJvdG9idWYuQnl0ZXNWYWx1ZRIrCgt2YWx1ZV9m",
"aWVsZBgTIAEoCzIWLmdvb2dsZS5wcm90b2J1Zi5WYWx1ZSKVBwoWUmVwZWF0",
"ZWRXZWxsS25vd25UeXBlcxInCglhbnlfZmllbGQYASADKAsyFC5nb29nbGUu", "ZWRXZWxsS25vd25UeXBlcxInCglhbnlfZmllbGQYASADKAsyFC5nb29nbGUu",
"cHJvdG9idWYuQW55EicKCWFwaV9maWVsZBgCIAMoCzIULmdvb2dsZS5wcm90", "cHJvdG9idWYuQW55EicKCWFwaV9maWVsZBgCIAMoCzIULmdvb2dsZS5wcm90",
"b2J1Zi5BcGkSMQoOZHVyYXRpb25fZmllbGQYAyADKAsyGS5nb29nbGUucHJv", "b2J1Zi5BcGkSMQoOZHVyYXRpb25fZmllbGQYAyADKAsyGS5nb29nbGUucHJv",
@ -162,7 +163,7 @@ namespace Google.Protobuf.TestProtos {
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.ApiReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.FieldMaskReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TypeReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, }, new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.ApiReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.FieldMaskReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TypeReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, },
new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestWellKnownTypes), global::Google.Protobuf.TestProtos.TestWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null), new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestWellKnownTypes), global::Google.Protobuf.TestProtos.TestWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField", "ValueField" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes), global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null), new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes), global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.OneofWellKnownTypes), global::Google.Protobuf.TestProtos.OneofWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, new[]{ "OneofField" }, null, null), new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.OneofWellKnownTypes), global::Google.Protobuf.TestProtos.OneofWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, new[]{ "OneofField" }, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.MapWellKnownTypes), global::Google.Protobuf.TestProtos.MapWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, new pbr::GeneratedCodeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }) new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.MapWellKnownTypes), global::Google.Protobuf.TestProtos.MapWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, new pbr::GeneratedCodeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, })
@ -215,6 +216,7 @@ namespace Google.Protobuf.TestProtos {
BoolField = other.BoolField; BoolField = other.BoolField;
StringField = other.StringField; StringField = other.StringField;
BytesField = other.BytesField; BytesField = other.BytesField;
ValueField = other.valueField_ != null ? other.ValueField.Clone() : null;
} }
public TestWellKnownTypes Clone() { public TestWellKnownTypes Clone() {
@ -410,6 +412,19 @@ namespace Google.Protobuf.TestProtos {
} }
} }
/// <summary>Field number for the "value_field" field.</summary>
public const int ValueFieldFieldNumber = 19;
private global::Google.Protobuf.WellKnownTypes.Value valueField_;
/// <summary>
/// Part of struct, but useful to be able to test separately
/// </summary>
public global::Google.Protobuf.WellKnownTypes.Value ValueField {
get { return valueField_; }
set {
valueField_ = value;
}
}
public override bool Equals(object other) { public override bool Equals(object other) {
return Equals(other as TestWellKnownTypes); return Equals(other as TestWellKnownTypes);
} }
@ -439,6 +454,7 @@ namespace Google.Protobuf.TestProtos {
if (BoolField != other.BoolField) return false; if (BoolField != other.BoolField) return false;
if (StringField != other.StringField) return false; if (StringField != other.StringField) return false;
if (BytesField != other.BytesField) return false; if (BytesField != other.BytesField) return false;
if (!object.Equals(ValueField, other.ValueField)) return false;
return true; return true;
} }
@ -462,6 +478,7 @@ namespace Google.Protobuf.TestProtos {
if (boolField_ != null) hash ^= BoolField.GetHashCode(); if (boolField_ != null) hash ^= BoolField.GetHashCode();
if (stringField_ != null) hash ^= StringField.GetHashCode(); if (stringField_ != null) hash ^= StringField.GetHashCode();
if (bytesField_ != null) hash ^= BytesField.GetHashCode(); if (bytesField_ != null) hash ^= BytesField.GetHashCode();
if (valueField_ != null) hash ^= ValueField.GetHashCode();
return hash; return hash;
} }
@ -533,6 +550,10 @@ namespace Google.Protobuf.TestProtos {
if (bytesField_ != null) { if (bytesField_ != null) {
_single_bytesField_codec.WriteTagAndValue(output, BytesField); _single_bytesField_codec.WriteTagAndValue(output, BytesField);
} }
if (valueField_ != null) {
output.WriteRawTag(154, 1);
output.WriteMessage(ValueField);
}
} }
public int CalculateSize() { public int CalculateSize() {
@ -591,6 +612,9 @@ namespace Google.Protobuf.TestProtos {
if (bytesField_ != null) { if (bytesField_ != null) {
size += _single_bytesField_codec.CalculateSizeWithTag(BytesField); size += _single_bytesField_codec.CalculateSizeWithTag(BytesField);
} }
if (valueField_ != null) {
size += 2 + pb::CodedOutputStream.ComputeMessageSize(ValueField);
}
return size; return size;
} }
@ -697,6 +721,12 @@ namespace Google.Protobuf.TestProtos {
BytesField = other.BytesField; BytesField = other.BytesField;
} }
} }
if (other.valueField_ != null) {
if (valueField_ == null) {
valueField_ = new global::Google.Protobuf.WellKnownTypes.Value();
}
ValueField.MergeFrom(other.ValueField);
}
} }
public void MergeFrom(pb::CodedInputStream input) { public void MergeFrom(pb::CodedInputStream input) {
@ -832,6 +862,13 @@ namespace Google.Protobuf.TestProtos {
} }
break; break;
} }
case 154: {
if (valueField_ == null) {
valueField_ = new global::Google.Protobuf.WellKnownTypes.Value();
}
input.ReadMessage(valueField_);
break;
}
} }
} }
} }

@ -215,11 +215,16 @@ namespace Google.Protobuf
var token = tokenizer.Next(); var token = tokenizer.Next();
if (token.Type == JsonToken.TokenType.Null) if (token.Type == JsonToken.TokenType.Null)
{ {
// Clear the field if we see a null token, unless it's for a singular field of type
// google.protobuf.Value.
// Note: different from Java API, which just ignores it. // Note: different from Java API, which just ignores it.
// TODO: Bring it more in line? Discuss... // TODO: Bring it more in line? Discuss...
if (field.IsMap || field.IsRepeated || !IsGoogleProtobufValueField(field))
{
field.Accessor.Clear(message); field.Accessor.Clear(message);
return; return;
} }
}
tokenizer.PushBack(token); tokenizer.PushBack(token);
if (field.IsMap) if (field.IsMap)
@ -297,14 +302,22 @@ namespace Google.Protobuf
} }
} }
private static bool IsGoogleProtobufValueField(FieldDescriptor field)
{
return field.FieldType == FieldType.Message &&
field.MessageType.FullName == Value.Descriptor.FullName;
}
private object ParseSingleValue(FieldDescriptor field, JsonTokenizer tokenizer) private object ParseSingleValue(FieldDescriptor field, JsonTokenizer tokenizer)
{ {
var token = tokenizer.Next(); var token = tokenizer.Next();
if (token.Type == JsonToken.TokenType.Null) if (token.Type == JsonToken.TokenType.Null)
{ {
if (field.FieldType == FieldType.Message && field.MessageType.FullName == Value.Descriptor.FullName) // TODO: In order to support dynamic messages, we should really build this up
// dynamically.
if (IsGoogleProtobufValueField(field))
{ {
return new Value { NullValue = NullValue.NULL_VALUE }; return Value.ForNull();
} }
return null; return null;
} }

@ -39,6 +39,8 @@ message TestWellKnownTypes {
google.protobuf.BoolValue bool_field = 16; google.protobuf.BoolValue bool_field = 16;
google.protobuf.StringValue string_field = 17; google.protobuf.StringValue string_field = 17;
google.protobuf.BytesValue bytes_field = 18; google.protobuf.BytesValue bytes_field = 18;
// Part of struct, but useful to be able to test separately
google.protobuf.Value value_field = 19;
} }
// A repeated field for each well-known type. // A repeated field for each well-known type.

Loading…
Cancel
Save