Skip to content

Commit ba83447

Browse files
committed
Use property naming policy
1 parent 7c16cbc commit ba83447

8 files changed

Lines changed: 145 additions & 5 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace System.Text.Json.Protobuf;
2+
3+
public class JsonProtobufSerializerOptions
4+
{
5+
/// <summary>
6+
/// Defines how property names should be resolved for protobuf contracts.
7+
/// When set to true PropertyNamingPolicy will be ignored and property name will be derived from protobuf contract.
8+
/// This is usually the lower-camel-cased form of the field name, but can be overridden using the <c>json_name</c>
9+
/// option in the .proto file.
10+
/// </summary>
11+
public bool UseProtobufJsonNames { get; set; }
12+
}

src/System.Text.Json.Protobuf/JsonSerializerOptionsExtensions.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,25 @@ namespace System.Text.Json;
55

66
public static class JsonSerializerOptionsExtensions
77
{
8-
public static void AddProtobufSupport(this JsonSerializerOptions options)
8+
/// <summary>
9+
/// Adds support for Protobuf contracts to JsonSerializer.
10+
/// </summary>
11+
/// <param name="options">The <see cref="T:System.Text.Json.JsonSerializerOptions" />.</param>
12+
/// <param name="configure">An <see cref="T:System.Action" /> to configure the <see cref="T:System.Text.Json.Protobuf.JsonProtobufSerializerOptions" />.</param>
13+
public static void AddProtobufSupport(this JsonSerializerOptions options, Action<JsonProtobufSerializerOptions> configure)
914
{
15+
var jsonProtobufSerializerOptions = new JsonProtobufSerializerOptions();
16+
configure.Invoke(jsonProtobufSerializerOptions);
1017
options.Converters.Add(new TimestampConverter());
11-
options.Converters.Add(new ProtobufJsonConverterFactory());
18+
options.Converters.Add(new ProtobufJsonConverterFactory(jsonProtobufSerializerOptions));
19+
}
20+
21+
/// <summary>
22+
/// Adds support for Protobuf contracts to JsonSerializer.
23+
/// </summary>
24+
/// <param name="options">The <see cref="T:System.Text.Json.JsonSerializerOptions" />.</param>
25+
public static void AddProtobufSupport(this JsonSerializerOptions options)
26+
{
27+
options.AddProtobufSupport(_ => {});
1228
}
1329
}

src/System.Text.Json.Protobuf/ProtobufConverter.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,40 @@ namespace System.Text.Json.Protobuf;
1111
private readonly FieldInfo[] _fields;
1212
private readonly Dictionary<string, FieldInfo> _fieldsLookup;
1313

14-
public ProtobufConverter()
14+
public ProtobufConverter(JsonNamingPolicy? namingPolicy, JsonProtobufSerializerOptions options)
1515
{
1616
var propertyInfo = typeof(T).GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static);
1717
var messageDescriptor = (MessageDescriptor) propertyInfo?.GetValue(null, null)!;
1818

19+
var convertNameFunc = GetConvertNameFunc(namingPolicy, options.UseProtobufJsonNames);
20+
1921
_fields = messageDescriptor.Fields.InDeclarationOrder().Select(fieldDescriptor => new FieldInfo
2022
{
2123
Accessor = fieldDescriptor.Accessor,
2224
IsRepeated = fieldDescriptor.IsRepeated,
2325
FieldType = GetFieldType(fieldDescriptor),
24-
JsonName = fieldDescriptor.JsonName,
26+
JsonName = convertNameFunc(fieldDescriptor),
2527
IsOneOf = fieldDescriptor.ContainingOneof != null
2628
}).ToArray();
2729

2830
_fieldsLookup = _fields.ToDictionary(x => x.JsonName, x => x);
2931
}
3032

33+
private static Func<FieldDescriptor, string> GetConvertNameFunc(JsonNamingPolicy? jsonNamingPolicy, bool useProtobufJsonNames)
34+
{
35+
if (useProtobufJsonNames)
36+
{
37+
return descriptor => descriptor.JsonName;
38+
}
39+
40+
if (jsonNamingPolicy != null)
41+
{
42+
return descriptor => jsonNamingPolicy.ConvertName(descriptor.PropertyName);
43+
}
44+
45+
return descriptor => descriptor.PropertyName;
46+
}
47+
3148
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
3249
{
3350
var obj = new T();

src/System.Text.Json.Protobuf/ProtobufJsonConverterFactory.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ namespace System.Text.Json.Protobuf;
55

66
internal class ProtobufJsonConverterFactory : JsonConverterFactory
77
{
8+
private readonly JsonProtobufSerializerOptions _options;
9+
10+
public ProtobufJsonConverterFactory(JsonProtobufSerializerOptions options)
11+
{
12+
_options = options;
13+
}
14+
815
public override bool CanConvert(Type typeToConvert)
916
{
1017
return typeToConvert.IsAssignableTo(typeof(IMessage));
@@ -13,6 +20,6 @@ public override bool CanConvert(Type typeToConvert)
1320
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
1421
{
1522
var converterType = typeof(ProtobufConverter<>).MakeGenericType(typeToConvert);
16-
return (JsonConverter) Activator.CreateInstance(converterType)!;
23+
return (JsonConverter) Activator.CreateInstance(converterType, new object[]{options.PropertyNamingPolicy, _options})!;
1724
}
1825
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"doubleProperty": 2.5,
3+
"floatProperty": 0,
4+
"int32Property": 0,
5+
"int64Property": 0,
6+
"uint32Property": 0,
7+
"uint64Property": 0,
8+
"sint32Property": 0,
9+
"sint64Property": 0,
10+
"fixed32Property": 0,
11+
"fixed64Property": 0,
12+
"sfixed32Property": 0,
13+
"sfixed64Property": 0,
14+
"boolProperty": false,
15+
"stringProperty": ""
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"doubleproperty": 2.5,
3+
"floatproperty": 0,
4+
"int32property": 0,
5+
"int64property": 0,
6+
"uint32property": 0,
7+
"uint64property": 0,
8+
"sint32property": 0,
9+
"sint64property": 0,
10+
"fixed32property": 0,
11+
"fixed64property": 0,
12+
"sfixed32property": 0,
13+
"sfixed64property": 0,
14+
"boolproperty": false,
15+
"stringproperty": ""
16+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Text.Json.Protobuf.Tests.Utils;
2+
using SmartAnalyzers.ApprovalTestsExtensions;
3+
using Xunit;
4+
5+
namespace System.Text.Json.Protobuf.Tests;
6+
7+
public class JsonNamingPolicyTests
8+
{
9+
[Fact]
10+
public void Should_serialize_message_with_primitive_types()
11+
{
12+
// Arrange
13+
var msg = new SimpleMessage
14+
{
15+
DoubleProperty = 2.5d
16+
};
17+
var jsonSerializerOptions = TestHelper.CreateJsonSerializerOptions();
18+
jsonSerializerOptions.PropertyNamingPolicy = new JsonLowerCaseNamingPolicy();
19+
20+
// Act
21+
var serialized = JsonSerializer.Serialize(msg, jsonSerializerOptions);
22+
23+
// Assert
24+
var approver = new ExplicitApprover();
25+
approver.VerifyJson(serialized);
26+
}
27+
28+
[Fact]
29+
public void Should_ignore_PropertyNamingPolicy_when_UseProtobufJsonNames_set_to_true()
30+
{
31+
// Arrange
32+
var msg = new SimpleMessage
33+
{
34+
DoubleProperty = 2.5d
35+
};
36+
var jsonSerializerOptions = new JsonSerializerOptions();
37+
jsonSerializerOptions.PropertyNamingPolicy = new JsonLowerCaseNamingPolicy();
38+
jsonSerializerOptions.AddProtobufSupport(options => options.UseProtobufJsonNames = true);
39+
40+
// Act
41+
var serialized = JsonSerializer.Serialize(msg, jsonSerializerOptions);
42+
43+
// Assert
44+
var approver = new ExplicitApprover();
45+
approver.VerifyJson(serialized);
46+
}
47+
48+
private class JsonLowerCaseNamingPolicy : JsonNamingPolicy
49+
{
50+
public override string ConvertName(string name)
51+
{
52+
return name.ToLower();
53+
}
54+
}
55+
}

test/System.Text.Json.Protobuf.Tests/Utils/TestHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ public class TestHelper
55
public static JsonSerializerOptions CreateJsonSerializerOptions()
66
{
77
var jsonSerializerOptions = new JsonSerializerOptions();
8+
jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
89
jsonSerializerOptions.AddProtobufSupport();
910
return jsonSerializerOptions;
1011
}

0 commit comments

Comments
 (0)