From 0d32ab37fa94134427478d6776108e7cdb3e5e15 Mon Sep 17 00:00:00 2001 From: alien Date: Thu, 17 Mar 2016 15:19:04 +0300 Subject: csharp: add support for the json_name option Conflicts: csharp/src/Google.Protobuf/JsonFormatter.cs --- csharp/protos/unittest_issues.proto | 9 +- csharp/src/Google.Protobuf.Test/IssuesTest.cs | 18 +++ .../TestProtos/UnittestIssues.cs | 173 ++++++++++++++++++++- csharp/src/Google.Protobuf/JsonFormatter.cs | 7 +- .../Google.Protobuf/Reflection/FieldDescriptor.cs | 6 + .../Reflection/MessageDescriptor.cs | 2 + 6 files changed, 207 insertions(+), 8 deletions(-) (limited to 'csharp') diff --git a/csharp/protos/unittest_issues.proto b/csharp/protos/unittest_issues.proto index 989b3dc4..f120516c 100644 --- a/csharp/protos/unittest_issues.proto +++ b/csharp/protos/unittest_issues.proto @@ -116,4 +116,11 @@ message TestJsonFieldOrdering { string o2_string = 3; } -} \ No newline at end of file +} + +message TestJsonName { + // json_name field options are not properly handled during deserialization + string name = 1; + string description = 2 [json_name = "desc"]; + string guid = 3 [json_name = "exid"]; +} diff --git a/csharp/src/Google.Protobuf.Test/IssuesTest.cs b/csharp/src/Google.Protobuf.Test/IssuesTest.cs index a0350035..5b432edf 100644 --- a/csharp/src/Google.Protobuf.Test/IssuesTest.cs +++ b/csharp/src/Google.Protobuf.Test/IssuesTest.cs @@ -59,5 +59,23 @@ namespace Google.Protobuf // Underscores aren't reflected in the JSON. Assert.AreEqual("{ \"types\": 10, \"descriptor\": 20 }", message.ToString()); } + + [Test] + public void JsonNameParseTest() + { + var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor)); + var parser = new JsonParser(settings); + + Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" }, + parser.Parse("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }")); + } + + [Test] + public void JsonNameFormatTest() + { + var message = new TestJsonName { Name = "test", Description = "test2", Guid = "test3" }; + Assert.AreEqual("{ \"name\": \"test\", \"desc\": \"test2\", \"exid\": \"test3\" }", + JsonFormatter.Default.Format(message)); + } } } diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs index 16176a33..586f01c8 100644 --- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs +++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs @@ -42,10 +42,12 @@ namespace UnitTest.Issues.TestProtos { "CgtwbGFpbl9pbnQzMhgEIAEoBRITCglvMV9zdHJpbmcYAiABKAlIABISCghv", "MV9pbnQzMhgFIAEoBUgAEhQKDHBsYWluX3N0cmluZxgBIAEoCRISCghvMl9p", "bnQzMhgGIAEoBUgBEhMKCW8yX3N0cmluZxgDIAEoCUgBQgQKAm8xQgQKAm8y", - "KlUKDE5lZ2F0aXZlRW51bRIWChJORUdBVElWRV9FTlVNX1pFUk8QABIWCglG", - "aXZlQmVsb3cQ+///////////ARIVCghNaW51c09uZRD///////////8BKi4K", - "DkRlcHJlY2F0ZWRFbnVtEhMKD0RFUFJFQ0FURURfWkVSTxAAEgcKA29uZRAB", - "Qh9IAaoCGlVuaXRUZXN0Lklzc3Vlcy5UZXN0UHJvdG9zYgZwcm90bzM=")); + "IksKDFRlc3RKc29uTmFtZRIMCgRuYW1lGAEgASgJEhkKC2Rlc2NyaXB0aW9u", + "GAIgASgJUgRkZXNjEhIKBGd1aWQYAyABKAlSBGV4aWQqVQoMTmVnYXRpdmVF", + "bnVtEhYKEk5FR0FUSVZFX0VOVU1fWkVSTxAAEhYKCUZpdmVCZWxvdxD7////", + "//////8BEhUKCE1pbnVzT25lEP///////////wEqLgoORGVwcmVjYXRlZEVu", + "dW0SEwoPREVQUkVDQVRFRF9aRVJPEAASBwoDb25lEAFCH0gBqgIaVW5pdFRl", + "c3QuSXNzdWVzLlRlc3RQcm90b3NiBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, new pbr::GeneratedClrTypeInfo[] { @@ -55,7 +57,8 @@ namespace UnitTest.Issues.TestProtos { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage.Parser, new[]{ "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ItemField), global::UnitTest.Issues.TestProtos.ItemField.Parser, new[]{ "Item" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames), global::UnitTest.Issues.TestProtos.ReservedNames.Parser, new[]{ "Types_", "Descriptor_" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType), global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType.Parser, null, null, null, null)}), - new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonName), global::UnitTest.Issues.TestProtos.TestJsonName.Parser, new[]{ "Name", "Description", "Guid" }, null, null, null) })); } #endregion @@ -1399,6 +1402,166 @@ namespace UnitTest.Issues.TestProtos { } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class TestJsonName : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new TestJsonName()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::UnitTest.Issues.TestProtos.UnittestIssuesReflection.Descriptor.MessageTypes[7]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public TestJsonName() { + OnConstruction(); + } + + partial void OnConstruction(); + + public TestJsonName(TestJsonName other) : this() { + name_ = other.name_; + description_ = other.description_; + guid_ = other.guid_; + } + + public TestJsonName Clone() { + return new TestJsonName(this); + } + + /// Field number for the "name" field. + public const int NameFieldNumber = 1; + private string name_ = ""; + /// + /// json_name field options are not properly handled during deserialization + /// + public string Name { + get { return name_; } + set { + name_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "description" field. + public const int DescriptionFieldNumber = 2; + private string description_ = ""; + public string Description { + get { return description_; } + set { + description_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "guid" field. + public const int GuidFieldNumber = 3; + private string guid_ = ""; + public string Guid { + get { return guid_; } + set { + guid_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as TestJsonName); + } + + public bool Equals(TestJsonName other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Name != other.Name) return false; + if (Description != other.Description) return false; + if (Guid != other.Guid) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Name.Length != 0) hash ^= Name.GetHashCode(); + if (Description.Length != 0) hash ^= Description.GetHashCode(); + if (Guid.Length != 0) hash ^= Guid.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Name.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Name); + } + if (Description.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Description); + } + if (Guid.Length != 0) { + output.WriteRawTag(26); + output.WriteString(Guid); + } + } + + public int CalculateSize() { + int size = 0; + if (Name.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); + } + if (Description.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Description); + } + if (Guid.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Guid); + } + return size; + } + + public void MergeFrom(TestJsonName other) { + if (other == null) { + return; + } + if (other.Name.Length != 0) { + Name = other.Name; + } + if (other.Description.Length != 0) { + Description = other.Description; + } + if (other.Guid.Length != 0) { + Guid = other.Guid; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Name = input.ReadString(); + break; + } + case 18: { + Description = input.ReadString(); + break; + } + case 26: { + Guid = input.ReadString(); + break; + } + } + } + } + + } + #endregion } diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs index eb959c9f..1b5349e9 100644 --- a/csharp/src/Google.Protobuf/JsonFormatter.cs +++ b/csharp/src/Google.Protobuf/JsonFormatter.cs @@ -237,11 +237,14 @@ namespace Google.Protobuf { writer.Write(PropertySeparator); } - WriteString(writer, ToCamelCase(accessor.Descriptor.Name)); + + WriteString(writer, string.IsNullOrEmpty(accessor.Descriptor.JsonName) ? + ToCamelCase(accessor.Descriptor.Name) : accessor.Descriptor.JsonName); writer.Write(NameValueSeparator); WriteValue(writer, value); + first = false; - } + } return !first; } diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs index c6caaec6..8cfdb779 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs @@ -90,6 +90,12 @@ namespace Google.Protobuf.Reflection /// public override string Name { get { return proto.Name; } } + + /// + /// The json_name option of the descriptor's target. + /// + public string JsonName { get { return proto.JsonName; } } + internal FieldDescriptorProto Proto { get { return proto; } } /// diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index f5798d1e..63f168ca 100644 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -104,6 +104,8 @@ namespace Google.Protobuf.Reflection { map[JsonFormatter.ToCamelCase(field.Name)] = field; map[field.Name] = field; + if (!string.IsNullOrEmpty(field.JsonName)) + map[field.JsonName] = field; } return new ReadOnlyDictionary(map); } -- cgit v1.2.3 From 6f8dd2115b3ed07819a48dfa78b15770dfa2c450 Mon Sep 17 00:00:00 2001 From: alien Date: Tue, 29 Mar 2016 20:56:32 +0300 Subject: Code review fixes --- csharp/protos/unittest_issues.proto | 2 +- csharp/src/Google.Protobuf.Test/IssuesTest.cs | 1 + csharp/src/Google.Protobuf/JsonFormatter.cs | 3 +-- csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs | 2 +- csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs | 4 +--- 5 files changed, 5 insertions(+), 7 deletions(-) (limited to 'csharp') diff --git a/csharp/protos/unittest_issues.proto b/csharp/protos/unittest_issues.proto index f120516c..6c9f7634 100644 --- a/csharp/protos/unittest_issues.proto +++ b/csharp/protos/unittest_issues.proto @@ -119,7 +119,7 @@ message TestJsonFieldOrdering { } message TestJsonName { - // json_name field options are not properly handled during deserialization + // Message for testing the effects for of the json_name option string name = 1; string description = 2 [json_name = "desc"]; string guid = 3 [json_name = "exid"]; diff --git a/csharp/src/Google.Protobuf.Test/IssuesTest.cs b/csharp/src/Google.Protobuf.Test/IssuesTest.cs index 5b432edf..a38d6b08 100644 --- a/csharp/src/Google.Protobuf.Test/IssuesTest.cs +++ b/csharp/src/Google.Protobuf.Test/IssuesTest.cs @@ -66,6 +66,7 @@ namespace Google.Protobuf var settings = new JsonParser.Settings(10, TypeRegistry.FromFiles(UnittestIssuesReflection.Descriptor)); var parser = new JsonParser(settings); + // It is safe to use either original field name or explicitly specified json_name Assert.AreEqual(new TestJsonName { Name = "test", Description = "test2", Guid = "test3" }, parser.Parse("{ \"name\": \"test\", \"desc\": \"test2\", \"guid\": \"test3\" }")); } diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs index 1b5349e9..cbd9366c 100644 --- a/csharp/src/Google.Protobuf/JsonFormatter.cs +++ b/csharp/src/Google.Protobuf/JsonFormatter.cs @@ -238,8 +238,7 @@ namespace Google.Protobuf writer.Write(PropertySeparator); } - WriteString(writer, string.IsNullOrEmpty(accessor.Descriptor.JsonName) ? - ToCamelCase(accessor.Descriptor.Name) : accessor.Descriptor.JsonName); + WriteString(writer, accessor.Descriptor.JsonName); writer.Write(NameValueSeparator); WriteValue(writer, value); diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs index 8cfdb779..6083f171 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs @@ -94,7 +94,7 @@ namespace Google.Protobuf.Reflection /// /// The json_name option of the descriptor's target. /// - public string JsonName { get { return proto.JsonName; } } + public string JsonName { get { return proto.JsonName == "" ? JsonFormatter.ToCamelCase(proto.Name) : proto.JsonName; } } internal FieldDescriptorProto Proto { get { return proto; } } diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index 63f168ca..f5a835e5 100644 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -102,10 +102,8 @@ namespace Google.Protobuf.Reflection var map = new Dictionary(); foreach (var field in fields) { - map[JsonFormatter.ToCamelCase(field.Name)] = field; map[field.Name] = field; - if (!string.IsNullOrEmpty(field.JsonName)) - map[field.JsonName] = field; + map[field.JsonName] = field; } return new ReadOnlyDictionary(map); } -- cgit v1.2.3