From 1cc939f003390c1332572f819f9e05073b226aa8 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 23 Jul 2025 10:10:48 +0800 Subject: [PATCH 1/5] Don't error on Protobuf messages that expose wrapper types --- .../Internal/Json/MessageTypeInfoResolver.cs | 43 +- .../UnaryTests.cs | 23 + .../ConverterTests/JsonConverterReadTests.cs | 20 + .../ConverterTests/JsonConverterWriteTests.cs | 20 + .../MessageTypeInfoResolverTests.cs | 23 + .../ProtobutMessages/WrappersMessage.cs | 731 ++++++++++++++++++ .../Protos/greet.proto | 10 + 7 files changed, 863 insertions(+), 7 deletions(-) create mode 100644 src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/TestObjects/ProtobutMessages/WrappersMessage.cs diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs index 66e249194e0c..431d9c186069 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text.Json; @@ -94,6 +93,22 @@ private JsonPropertyInfo CreatePropertyInfo(JsonTypeInfo typeInfo, string name, JsonConverterHelper.GetFieldType(field), name); + // A propery with a wrapper type is usually the underlying type on the DTO. + // For example, a field of type google.protobuf.StringValue will have a property of type string. + // Sometimes the wrapper type is exposed so we need to do some extra work to translate the value. + FieldDescriptor? wrapperTypeValueField = null; + if (field.FieldType == FieldType.Message && ServiceDescriptorHelpers.IsWrapperType(field.MessageType)) + { + var property = field.ContainingType.ClrType.GetProperty(field.PropertyName); + + // Check if the property type is the same as the field type. This means the property is StringValue, et al, + // and additional conversion is required. + if (property != null && property.PropertyType == field.MessageType.ClrType) + { + wrapperTypeValueField = field.MessageType.FindFieldByName("value"); + } + } + propertyInfo.ShouldSerialize = (o, v) => { // Properties that don't have this flag set are only used to deserialize incoming JSON. @@ -105,7 +120,13 @@ private JsonPropertyInfo CreatePropertyInfo(JsonTypeInfo typeInfo, string name, }; propertyInfo.Get = (o) => { - return field.Accessor.GetValue((IMessage)o); + var value = field.Accessor.GetValue((IMessage)o); + if (wrapperTypeValueField != null && value is IMessage wrapperMessage) + { + return wrapperTypeValueField.Accessor.GetValue(wrapperMessage); + } + + return value; }; if (field.IsMap || field.IsRepeated) @@ -115,13 +136,13 @@ private JsonPropertyInfo CreatePropertyInfo(JsonTypeInfo typeInfo, string name, } else { - propertyInfo.Set = GetSetMethod(field); + propertyInfo.Set = GetSetMethod(field, wrapperTypeValueField); } return propertyInfo; } - private static Action GetSetMethod(FieldDescriptor field) + private static Action GetSetMethod(FieldDescriptor field, FieldDescriptor? wrapperTypeValueField) { Debug.Assert(!field.IsRepeated && !field.IsMap, "Collections shouldn't have a setter."); @@ -135,19 +156,27 @@ private JsonPropertyInfo CreatePropertyInfo(JsonTypeInfo typeInfo, string name, throw new InvalidOperationException($"Multiple values specified for oneof {field.RealContainingOneof.Name}."); } - SetFieldValue(field, (IMessage)o, v); + SetFieldValue(field, wrapperTypeValueField, (IMessage)o, v); }; } return (o, v) => { - SetFieldValue(field, (IMessage)o, v); + SetFieldValue(field, wrapperTypeValueField, (IMessage)o, v); }; - static void SetFieldValue(FieldDescriptor field, IMessage m, object? v) + static void SetFieldValue(FieldDescriptor field, FieldDescriptor? wrapperTypeValueField, IMessage m, object? v) { if (v != null) { + // This field exposes a wrapper type. Need to create a wrapper instance and set the value on it. + if (wrapperTypeValueField != null && v is not IMessage) + { + var wrapper = (IMessage)Activator.CreateInstance(field.MessageType.ClrType)!; + wrapperTypeValueField.Accessor.SetValue(wrapper, v); + v = wrapper; + } + field.Accessor.SetValue(m, v); } else diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/UnaryTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/UnaryTests.cs index 7a9a7603289a..d1a298e831a6 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/UnaryTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/UnaryTests.cs @@ -251,4 +251,27 @@ Task UnaryMethod(EnumHelloRequest request, ServerCallContext context Assert.Equal("utf-8", response.Content.Headers.ContentType!.CharSet); Assert.Equal("Hello Jane!", result.RootElement.GetProperty("message").GetString()); } + + [Fact] + public async Task Request_sdfsdf_Success() + { + // Arrange + Task UnaryMethod(HelloRequest request, ServerCallContext context) + { + return Task.FromResult(new HelloReplyStringValue { Message = $"Hello {request.Name}!" }); + } + var method = Fixture.DynamicGrpc.AddUnaryMethod( + UnaryMethod, + Greeter.Descriptor.FindMethodByName("SayHelloStringValue")); + + var client = new HttpClient(Fixture.Handler) { BaseAddress = new Uri("http://localhost") }; + + // Act + var response = await client.GetAsync("/v1/greeter_stringvalue").DefaultTimeout(); + var responseStream = await response.Content.ReadAsStreamAsync(); + using var result = await JsonDocument.ParseAsync(responseStream); + + // Assert + Assert.Equal("Hello test!", result.RootElement.GetProperty("message").GetString()); + } } diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterReadTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterReadTests.cs index 519f40221216..ff1155dac5b8 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterReadTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterReadTests.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.Infrastructure; +using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages; using Transcoding; using Xunit.Abstractions; @@ -531,6 +532,25 @@ public void NullableWrappers() AssertReadJson(json); } + [Fact] + public void NullableWrappers_Type() + { + var json = @"{ + ""stringValue"": ""A string"", + ""int32Value"": 1, + ""int64Value"": ""2"", + ""floatValue"": 1.2, + ""doubleValue"": 1.1, + ""boolValue"": true, + ""uint32Value"": 3, + ""uint64Value"": ""4"", + ""bytesValue"": ""SGVsbG8gd29ybGQ="" +}"; + + var result = AssertReadJson(json, serializeOld: false); + Assert.Equal("A string", result.StringValue.Value); + } + [Fact] public void NullValue_Default_Null() { diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterWriteTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterWriteTests.cs index 32d1df26d8e4..16b876123a49 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterWriteTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterWriteTests.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.Infrastructure; +using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages; using Transcoding; using Xunit.Abstractions; using Type = System.Type; @@ -201,6 +202,25 @@ public void NullableWrappers() AssertWrittenJson(wrappers); } + [Fact] + public void NullableWrappers_Types() + { + var wrappers = new WrappersMessage + { + BoolValue = new BoolValue { Value = true }, + BytesValue = new BytesValue { Value = ByteString.CopyFrom(Encoding.UTF8.GetBytes("Hello world")) }, + DoubleValue = new DoubleValue { Value = 1.1 }, + FloatValue = new FloatValue { Value = 1.2f }, + Int32Value = new Int32Value { Value = 1 }, + Int64Value = new Int64Value { Value = 2L }, + StringValue = new StringValue { Value = "A string" }, + Uint32Value = new UInt32Value { Value = 3U }, + Uint64Value = new UInt64Value { Value = 4UL } + }; + + AssertWrittenJson(wrappers); + } + [Fact] public void NullableWrapper_Root_Int32() { diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs index 09718876a4d4..1fef842671d6 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs @@ -4,8 +4,10 @@ using System.Text.Json; using Google.Protobuf; using Google.Protobuf.Reflection; +using Google.Protobuf.WellKnownTypes; using Grpc.Shared; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json; +using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages; using Transcoding; namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.ConverterTests; @@ -50,6 +52,27 @@ public void GetTypeInfo_HelloRequest_Success() Assert.NotEmpty(typeInfo.Properties); } + //[Fact] + //public void GetTypeInfo_StringValuesMessage_Success() + //{ + // var descriptorRegistry = new DescriptorRegistry(); + // descriptorRegistry.RegisterFileDescriptor(StringValuesMessage.Descriptor.File); + + // var resolver = CreateResolver(descriptorRegistry); + + // var typeInfo = resolver.GetTypeInfo(typeof(StringValuesMessage), new JsonSerializerOptions()); + // Assert.NotNull(typeInfo); + + // Assert.NotEmpty(typeInfo.Properties); + + // var message = new StringValuesMessage(); + // message.Message = new StringValue { Value = "test" }; + + // var prop = Assert.Single(typeInfo.Properties); + + // var value = prop.Get(message); + //} + private static MessageTypeInfoResolver CreateResolver(DescriptorRegistry? descriptorRegistry = null) { var context = new JsonContext(new GrpcJsonSettings(), TypeRegistry.Empty, descriptorRegistry ?? new DescriptorRegistry()); diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/TestObjects/ProtobutMessages/WrappersMessage.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/TestObjects/ProtobutMessages/WrappersMessage.cs new file mode 100644 index 000000000000..48dce5832515 --- /dev/null +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/TestObjects/ProtobutMessages/WrappersMessage.cs @@ -0,0 +1,731 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Protos/extra.proto +// +#pragma warning disable 1591, 0612, 3021, 8981 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages +{ + + /// Holder for reflection information generated from Protos/extra.proto + public static partial class ExtraReflection + { + + #region Descriptor + /// File descriptor for Protos/extra.proto + public static pbr::FileDescriptor Descriptor + { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static ExtraReflection() + { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChJQcm90b3MvZXh0cmEucHJvdG8SBHRlc3QaHkdvb2dsZS9wcm90b2J1Zi93", + "cmFwcGVycy5wcm90bxocR29vZ2xlL2FwaS9hbm5vdGF0aW9ucy5wcm90byLZ", + "AwoPV3JhcHBlcnNNZXNzYWdlEjIKDHN0cmluZ192YWx1ZRgBIAEoCzIcLmdv", + "b2dsZS5wcm90b2J1Zi5TdHJpbmdWYWx1ZRIwCgtpbnQzMl92YWx1ZRgCIAEo", + "CzIbLmdvb2dsZS5wcm90b2J1Zi5JbnQzMlZhbHVlEjAKC2ludDY0X3ZhbHVl", + "GAMgASgLMhsuZ29vZ2xlLnByb3RvYnVmLkludDY0VmFsdWUSMAoLZmxvYXRf", + "dmFsdWUYBCABKAsyGy5nb29nbGUucHJvdG9idWYuRmxvYXRWYWx1ZRIyCgxk", + "b3VibGVfdmFsdWUYBSABKAsyHC5nb29nbGUucHJvdG9idWYuRG91YmxlVmFs", + "dWUSLgoKYm9vbF92YWx1ZRgGIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5Cb29s", + "VmFsdWUSMgoMdWludDMyX3ZhbHVlGAcgASgLMhwuZ29vZ2xlLnByb3RvYnVm", + "LlVJbnQzMlZhbHVlEjIKDHVpbnQ2NF92YWx1ZRgIIAEoCzIcLmdvb2dsZS5w", + "cm90b2J1Zi5VSW50NjRWYWx1ZRIwCgtieXRlc192YWx1ZRgJIAEoCzIbLmdv", + "b2dsZS5wcm90b2J1Zi5CeXRlc1ZhbHVlQk+qAkxNaWNyb3NvZnQuQXNwTmV0", + "Q29yZS5HcnBjLkpzb25UcmFuc2NvZGluZy5UZXN0cy5UZXN0T2JqZWN0cy5Q", + "cm90b2J1dE1lc3NhZ2VzYgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, global::Google.Api.AnnotationsReflection.Descriptor, }, + new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages.WrappersMessage), global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages.WrappersMessage.Parser, new[]{ "StringValue", "Int32Value", "Int64Value", "FloatValue", "DoubleValue", "BoolValue", "Uint32Value", "Uint64Value", "BytesValue" }, null, null, null, null) + })); + } + #endregion + + } + #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class WrappersMessage : pb::IMessage +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage +#endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new WrappersMessage()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor + { + get { return global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages.ExtraReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor + { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public WrappersMessage() + { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public WrappersMessage(WrappersMessage other) : this() + { + stringValue_ = other.stringValue_ != null ? other.stringValue_.Clone() : null; + int32Value_ = other.int32Value_ != null ? other.int32Value_.Clone() : null; + int64Value_ = other.int64Value_ != null ? other.int64Value_.Clone() : null; + floatValue_ = other.floatValue_ != null ? other.floatValue_.Clone() : null; + doubleValue_ = other.doubleValue_ != null ? other.doubleValue_.Clone() : null; + boolValue_ = other.boolValue_ != null ? other.boolValue_.Clone() : null; + uint32Value_ = other.uint32Value_ != null ? other.uint32Value_.Clone() : null; + uint64Value_ = other.uint64Value_ != null ? other.uint64Value_.Clone() : null; + bytesValue_ = other.bytesValue_ != null ? other.bytesValue_.Clone() : null; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public WrappersMessage Clone() + { + return new WrappersMessage(this); + } + + /// Field number for the "string_value" field. + public const int StringValueFieldNumber = 1; + private global::Google.Protobuf.WellKnownTypes.StringValue stringValue_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.StringValue StringValue + { + get { return stringValue_; } + set + { + stringValue_ = value; + } + } + + /// Field number for the "int32_value" field. + public const int Int32ValueFieldNumber = 2; + private global::Google.Protobuf.WellKnownTypes.Int32Value int32Value_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.Int32Value Int32Value + { + get { return int32Value_; } + set + { + int32Value_ = value; + } + } + + /// Field number for the "int64_value" field. + public const int Int64ValueFieldNumber = 3; + private global::Google.Protobuf.WellKnownTypes.Int64Value int64Value_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.Int64Value Int64Value + { + get { return int64Value_; } + set + { + int64Value_ = value; + } + } + + /// Field number for the "float_value" field. + public const int FloatValueFieldNumber = 4; + private global::Google.Protobuf.WellKnownTypes.FloatValue floatValue_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.FloatValue FloatValue + { + get { return floatValue_; } + set + { + floatValue_ = value; + } + } + + /// Field number for the "double_value" field. + public const int DoubleValueFieldNumber = 5; + private global::Google.Protobuf.WellKnownTypes.DoubleValue doubleValue_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.DoubleValue DoubleValue + { + get { return doubleValue_; } + set + { + doubleValue_ = value; + } + } + + /// Field number for the "bool_value" field. + public const int BoolValueFieldNumber = 6; + private global::Google.Protobuf.WellKnownTypes.BoolValue boolValue_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.BoolValue BoolValue + { + get { return boolValue_; } + set + { + boolValue_ = value; + } + } + + /// Field number for the "uint32_value" field. + public const int Uint32ValueFieldNumber = 7; + private global::Google.Protobuf.WellKnownTypes.UInt32Value uint32Value_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.UInt32Value Uint32Value + { + get { return uint32Value_; } + set + { + uint32Value_ = value; + } + } + + /// Field number for the "uint64_value" field. + public const int Uint64ValueFieldNumber = 8; + private global::Google.Protobuf.WellKnownTypes.UInt64Value uint64Value_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.UInt64Value Uint64Value + { + get { return uint64Value_; } + set + { + uint64Value_ = value; + } + } + + /// Field number for the "bytes_value" field. + public const int BytesValueFieldNumber = 9; + private global::Google.Protobuf.WellKnownTypes.BytesValue bytesValue_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Google.Protobuf.WellKnownTypes.BytesValue BytesValue + { + get { return bytesValue_; } + set + { + bytesValue_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) + { + return Equals(other as WrappersMessage); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(WrappersMessage other) + { + if (ReferenceEquals(other, null)) + { + return false; + } + if (ReferenceEquals(other, this)) + { + return true; + } + if (!object.Equals(StringValue, other.StringValue)) return false; + if (!object.Equals(Int32Value, other.Int32Value)) return false; + if (!object.Equals(Int64Value, other.Int64Value)) return false; + if (!object.Equals(FloatValue, other.FloatValue)) return false; + if (!object.Equals(DoubleValue, other.DoubleValue)) return false; + if (!object.Equals(BoolValue, other.BoolValue)) return false; + if (!object.Equals(Uint32Value, other.Uint32Value)) return false; + if (!object.Equals(Uint64Value, other.Uint64Value)) return false; + if (!object.Equals(BytesValue, other.BytesValue)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() + { + int hash = 1; + if (stringValue_ != null) hash ^= StringValue.GetHashCode(); + if (int32Value_ != null) hash ^= Int32Value.GetHashCode(); + if (int64Value_ != null) hash ^= Int64Value.GetHashCode(); + if (floatValue_ != null) hash ^= FloatValue.GetHashCode(); + if (doubleValue_ != null) hash ^= DoubleValue.GetHashCode(); + if (boolValue_ != null) hash ^= BoolValue.GetHashCode(); + if (uint32Value_ != null) hash ^= Uint32Value.GetHashCode(); + if (uint64Value_ != null) hash ^= Uint64Value.GetHashCode(); + if (bytesValue_ != null) hash ^= BytesValue.GetHashCode(); + if (_unknownFields != null) + { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() + { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) + { +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); +#else + if (stringValue_ != null) { + output.WriteRawTag(10); + output.WriteMessage(StringValue); + } + if (int32Value_ != null) { + output.WriteRawTag(18); + output.WriteMessage(Int32Value); + } + if (int64Value_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Int64Value); + } + if (floatValue_ != null) { + output.WriteRawTag(34); + output.WriteMessage(FloatValue); + } + if (doubleValue_ != null) { + output.WriteRawTag(42); + output.WriteMessage(DoubleValue); + } + if (boolValue_ != null) { + output.WriteRawTag(50); + output.WriteMessage(BoolValue); + } + if (uint32Value_ != null) { + output.WriteRawTag(58); + output.WriteMessage(Uint32Value); + } + if (uint64Value_ != null) { + output.WriteRawTag(66); + output.WriteMessage(Uint64Value); + } + if (bytesValue_ != null) { + output.WriteRawTag(74); + output.WriteMessage(BytesValue); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } +#endif + } + +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) + { + if (stringValue_ != null) + { + output.WriteRawTag(10); + output.WriteMessage(StringValue); + } + if (int32Value_ != null) + { + output.WriteRawTag(18); + output.WriteMessage(Int32Value); + } + if (int64Value_ != null) + { + output.WriteRawTag(26); + output.WriteMessage(Int64Value); + } + if (floatValue_ != null) + { + output.WriteRawTag(34); + output.WriteMessage(FloatValue); + } + if (doubleValue_ != null) + { + output.WriteRawTag(42); + output.WriteMessage(DoubleValue); + } + if (boolValue_ != null) + { + output.WriteRawTag(50); + output.WriteMessage(BoolValue); + } + if (uint32Value_ != null) + { + output.WriteRawTag(58); + output.WriteMessage(Uint32Value); + } + if (uint64Value_ != null) + { + output.WriteRawTag(66); + output.WriteMessage(Uint64Value); + } + if (bytesValue_ != null) + { + output.WriteRawTag(74); + output.WriteMessage(BytesValue); + } + if (_unknownFields != null) + { + _unknownFields.WriteTo(ref output); + } + } +#endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() + { + int size = 0; + if (stringValue_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(StringValue); + } + if (int32Value_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Int32Value); + } + if (int64Value_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Int64Value); + } + if (floatValue_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(FloatValue); + } + if (doubleValue_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(DoubleValue); + } + if (boolValue_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(BoolValue); + } + if (uint32Value_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Uint32Value); + } + if (uint64Value_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Uint64Value); + } + if (bytesValue_ != null) + { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(BytesValue); + } + if (_unknownFields != null) + { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(WrappersMessage other) + { + if (other == null) + { + return; + } + if (other.stringValue_ != null) + { + if (stringValue_ == null) + { + StringValue = new global::Google.Protobuf.WellKnownTypes.StringValue(); + } + StringValue.MergeFrom(other.StringValue); + } + if (other.int32Value_ != null) + { + if (int32Value_ == null) + { + Int32Value = new global::Google.Protobuf.WellKnownTypes.Int32Value(); + } + Int32Value.MergeFrom(other.Int32Value); + } + if (other.int64Value_ != null) + { + if (int64Value_ == null) + { + Int64Value = new global::Google.Protobuf.WellKnownTypes.Int64Value(); + } + Int64Value.MergeFrom(other.Int64Value); + } + if (other.floatValue_ != null) + { + if (floatValue_ == null) + { + FloatValue = new global::Google.Protobuf.WellKnownTypes.FloatValue(); + } + FloatValue.MergeFrom(other.FloatValue); + } + if (other.doubleValue_ != null) + { + if (doubleValue_ == null) + { + DoubleValue = new global::Google.Protobuf.WellKnownTypes.DoubleValue(); + } + DoubleValue.MergeFrom(other.DoubleValue); + } + if (other.boolValue_ != null) + { + if (boolValue_ == null) + { + BoolValue = new global::Google.Protobuf.WellKnownTypes.BoolValue(); + } + BoolValue.MergeFrom(other.BoolValue); + } + if (other.uint32Value_ != null) + { + if (uint32Value_ == null) + { + Uint32Value = new global::Google.Protobuf.WellKnownTypes.UInt32Value(); + } + Uint32Value.MergeFrom(other.Uint32Value); + } + if (other.uint64Value_ != null) + { + if (uint64Value_ == null) + { + Uint64Value = new global::Google.Protobuf.WellKnownTypes.UInt64Value(); + } + Uint64Value.MergeFrom(other.Uint64Value); + } + if (other.bytesValue_ != null) + { + if (bytesValue_ == null) + { + BytesValue = new global::Google.Protobuf.WellKnownTypes.BytesValue(); + } + BytesValue.MergeFrom(other.BytesValue); + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) + { +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); +#else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + if (stringValue_ == null) { + StringValue = new global::Google.Protobuf.WellKnownTypes.StringValue(); + } + input.ReadMessage(StringValue); + break; + } + case 18: { + if (int32Value_ == null) { + Int32Value = new global::Google.Protobuf.WellKnownTypes.Int32Value(); + } + input.ReadMessage(Int32Value); + break; + } + case 26: { + if (int64Value_ == null) { + Int64Value = new global::Google.Protobuf.WellKnownTypes.Int64Value(); + } + input.ReadMessage(Int64Value); + break; + } + case 34: { + if (floatValue_ == null) { + FloatValue = new global::Google.Protobuf.WellKnownTypes.FloatValue(); + } + input.ReadMessage(FloatValue); + break; + } + case 42: { + if (doubleValue_ == null) { + DoubleValue = new global::Google.Protobuf.WellKnownTypes.DoubleValue(); + } + input.ReadMessage(DoubleValue); + break; + } + case 50: { + if (boolValue_ == null) { + BoolValue = new global::Google.Protobuf.WellKnownTypes.BoolValue(); + } + input.ReadMessage(BoolValue); + break; + } + case 58: { + if (uint32Value_ == null) { + Uint32Value = new global::Google.Protobuf.WellKnownTypes.UInt32Value(); + } + input.ReadMessage(Uint32Value); + break; + } + case 66: { + if (uint64Value_ == null) { + Uint64Value = new global::Google.Protobuf.WellKnownTypes.UInt64Value(); + } + input.ReadMessage(Uint64Value); + break; + } + case 74: { + if (bytesValue_ == null) { + BytesValue = new global::Google.Protobuf.WellKnownTypes.BytesValue(); + } + input.ReadMessage(BytesValue); + break; + } + } + } +#endif + } + +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) + { + uint tag; + while ((tag = input.ReadTag()) != 0) + { + if ((tag & 7) == 4) + { + // Abort on any end group tag. + return; + } + switch (tag) + { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: + { + if (stringValue_ == null) + { + StringValue = new global::Google.Protobuf.WellKnownTypes.StringValue(); + } + input.ReadMessage(StringValue); + break; + } + case 18: + { + if (int32Value_ == null) + { + Int32Value = new global::Google.Protobuf.WellKnownTypes.Int32Value(); + } + input.ReadMessage(Int32Value); + break; + } + case 26: + { + if (int64Value_ == null) + { + Int64Value = new global::Google.Protobuf.WellKnownTypes.Int64Value(); + } + input.ReadMessage(Int64Value); + break; + } + case 34: + { + if (floatValue_ == null) + { + FloatValue = new global::Google.Protobuf.WellKnownTypes.FloatValue(); + } + input.ReadMessage(FloatValue); + break; + } + case 42: + { + if (doubleValue_ == null) + { + DoubleValue = new global::Google.Protobuf.WellKnownTypes.DoubleValue(); + } + input.ReadMessage(DoubleValue); + break; + } + case 50: + { + if (boolValue_ == null) + { + BoolValue = new global::Google.Protobuf.WellKnownTypes.BoolValue(); + } + input.ReadMessage(BoolValue); + break; + } + case 58: + { + if (uint32Value_ == null) + { + Uint32Value = new global::Google.Protobuf.WellKnownTypes.UInt32Value(); + } + input.ReadMessage(Uint32Value); + break; + } + case 66: + { + if (uint64Value_ == null) + { + Uint64Value = new global::Google.Protobuf.WellKnownTypes.UInt64Value(); + } + input.ReadMessage(Uint64Value); + break; + } + case 74: + { + if (bytesValue_ == null) + { + BytesValue = new global::Google.Protobuf.WellKnownTypes.BytesValue(); + } + input.ReadMessage(BytesValue); + break; + } + } + } + } +#endif + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto b/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto index 890132f0e54c..e4ce6e427f47 100644 --- a/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto +++ b/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto @@ -3,6 +3,7 @@ syntax = "proto3"; option csharp_namespace = "IntegrationTestsWebsite"; import "google/api/annotations.proto"; +import "google/protobuf/wrappers.proto"; package greet; @@ -83,6 +84,11 @@ service Greeter { body: "*" }; } + rpc SayHelloStringValue (HelloRequest) returns (HelloReplyStringValue) { + option (google.api.http) = { + get: "/v1/greeter_stringvalue" + }; + } } // The request message containing the user's name. @@ -113,3 +119,7 @@ enum NameOptions { message HelloReply { string message = 1; } + +message HelloReplyStringValue { + google.protobuf.StringValue message = 1; +} From f18319c920cb69422063dbaa7f7d400571106b28 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 23 Jul 2025 10:12:16 +0800 Subject: [PATCH 2/5] Update --- .../UnaryTests.cs | 23 ------------------- .../MessageTypeInfoResolverTests.cs | 21 ----------------- .../Protos/greet.proto | 10 -------- 3 files changed, 54 deletions(-) diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/UnaryTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/UnaryTests.cs index d1a298e831a6..7a9a7603289a 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/UnaryTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/UnaryTests.cs @@ -251,27 +251,4 @@ Task UnaryMethod(EnumHelloRequest request, ServerCallContext context Assert.Equal("utf-8", response.Content.Headers.ContentType!.CharSet); Assert.Equal("Hello Jane!", result.RootElement.GetProperty("message").GetString()); } - - [Fact] - public async Task Request_sdfsdf_Success() - { - // Arrange - Task UnaryMethod(HelloRequest request, ServerCallContext context) - { - return Task.FromResult(new HelloReplyStringValue { Message = $"Hello {request.Name}!" }); - } - var method = Fixture.DynamicGrpc.AddUnaryMethod( - UnaryMethod, - Greeter.Descriptor.FindMethodByName("SayHelloStringValue")); - - var client = new HttpClient(Fixture.Handler) { BaseAddress = new Uri("http://localhost") }; - - // Act - var response = await client.GetAsync("/v1/greeter_stringvalue").DefaultTimeout(); - var responseStream = await response.Content.ReadAsStreamAsync(); - using var result = await JsonDocument.ParseAsync(responseStream); - - // Assert - Assert.Equal("Hello test!", result.RootElement.GetProperty("message").GetString()); - } } diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs index 1fef842671d6..4ba721d2025f 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs @@ -52,27 +52,6 @@ public void GetTypeInfo_HelloRequest_Success() Assert.NotEmpty(typeInfo.Properties); } - //[Fact] - //public void GetTypeInfo_StringValuesMessage_Success() - //{ - // var descriptorRegistry = new DescriptorRegistry(); - // descriptorRegistry.RegisterFileDescriptor(StringValuesMessage.Descriptor.File); - - // var resolver = CreateResolver(descriptorRegistry); - - // var typeInfo = resolver.GetTypeInfo(typeof(StringValuesMessage), new JsonSerializerOptions()); - // Assert.NotNull(typeInfo); - - // Assert.NotEmpty(typeInfo.Properties); - - // var message = new StringValuesMessage(); - // message.Message = new StringValue { Value = "test" }; - - // var prop = Assert.Single(typeInfo.Properties); - - // var value = prop.Get(message); - //} - private static MessageTypeInfoResolver CreateResolver(DescriptorRegistry? descriptorRegistry = null) { var context = new JsonContext(new GrpcJsonSettings(), TypeRegistry.Empty, descriptorRegistry ?? new DescriptorRegistry()); diff --git a/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto b/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto index e4ce6e427f47..890132f0e54c 100644 --- a/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto +++ b/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto @@ -3,7 +3,6 @@ syntax = "proto3"; option csharp_namespace = "IntegrationTestsWebsite"; import "google/api/annotations.proto"; -import "google/protobuf/wrappers.proto"; package greet; @@ -84,11 +83,6 @@ service Greeter { body: "*" }; } - rpc SayHelloStringValue (HelloRequest) returns (HelloReplyStringValue) { - option (google.api.http) = { - get: "/v1/greeter_stringvalue" - }; - } } // The request message containing the user's name. @@ -119,7 +113,3 @@ enum NameOptions { message HelloReply { string message = 1; } - -message HelloReplyStringValue { - google.protobuf.StringValue message = 1; -} From e60c0e9392f4336be656a25dcc715a72948a44d8 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 23 Jul 2025 10:12:47 +0800 Subject: [PATCH 3/5] Update --- .../ConverterTests/MessageTypeInfoResolverTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs index 4ba721d2025f..09718876a4d4 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/MessageTypeInfoResolverTests.cs @@ -4,10 +4,8 @@ using System.Text.Json; using Google.Protobuf; using Google.Protobuf.Reflection; -using Google.Protobuf.WellKnownTypes; using Grpc.Shared; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json; -using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages; using Transcoding; namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.ConverterTests; From 77b40a711066ac706f4dd0aec7a694947ff9c96b Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 23 Jul 2025 14:16:25 +0800 Subject: [PATCH 4/5] Update --- .../Internal/Json/MessageTypeInfoResolver.cs | 2 +- .../ConverterTests/JsonConverterReadTests.cs | 2 +- .../ConverterTests/JsonConverterWriteTests.cs | 2 +- .../TestObjects/ProtobutMessages/WrappersMessage.cs | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs index 431d9c186069..0556df30a6c6 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs @@ -93,7 +93,7 @@ private JsonPropertyInfo CreatePropertyInfo(JsonTypeInfo typeInfo, string name, JsonConverterHelper.GetFieldType(field), name); - // A propery with a wrapper type is usually the underlying type on the DTO. + // A property with a wrapper type is usually the underlying type on the DTO. // For example, a field of type google.protobuf.StringValue will have a property of type string. // Sometimes the wrapper type is exposed so we need to do some extra work to translate the value. FieldDescriptor? wrapperTypeValueField = null; diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterReadTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterReadTests.cs index ff1155dac5b8..95c51908ba3b 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterReadTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterReadTests.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.Infrastructure; -using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages; +using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobufMessages; using Transcoding; using Xunit.Abstractions; diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterWriteTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterWriteTests.cs index 16b876123a49..7dfe0c38095e 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterWriteTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/ConverterTests/JsonConverterWriteTests.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json; using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.Infrastructure; -using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages; +using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobufMessages; using Transcoding; using Xunit.Abstractions; using Type = System.Type; diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/TestObjects/ProtobutMessages/WrappersMessage.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/TestObjects/ProtobutMessages/WrappersMessage.cs index 48dce5832515..d1ae86daa8c7 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/TestObjects/ProtobutMessages/WrappersMessage.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/TestObjects/ProtobutMessages/WrappersMessage.cs @@ -9,7 +9,7 @@ using pbc = global::Google.Protobuf.Collections; using pbr = global::Google.Protobuf.Reflection; using scg = global::System.Collections.Generic; -namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages +namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobufMessages { /// Holder for reflection information generated from Protos/extra.proto @@ -46,7 +46,7 @@ static ExtraReflection() descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, global::Google.Api.AnnotationsReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages.WrappersMessage), global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages.WrappersMessage.Parser, new[]{ "StringValue", "Int32Value", "Int64Value", "FloatValue", "DoubleValue", "BoolValue", "Uint32Value", "Uint64Value", "BytesValue" }, null, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobufMessages.WrappersMessage), global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobufMessages.WrappersMessage.Parser, new[]{ "StringValue", "Int32Value", "Int64Value", "FloatValue", "DoubleValue", "BoolValue", "Uint32Value", "Uint64Value", "BytesValue" }, null, null, null, null) })); } #endregion @@ -69,7 +69,7 @@ public sealed partial class WrappersMessage : pb::IMessage [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobutMessages.ExtraReflection.Descriptor.MessageTypes[0]; } + get { return global::Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.TestObjects.ProtobufMessages.ExtraReflection.Descriptor.MessageTypes[0]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] From f7f2f2a9e135d448870788a3ca625d16c25cbab4 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 24 Jul 2025 07:21:36 +0800 Subject: [PATCH 5/5] Comment --- .../Internal/Json/MessageTypeInfoResolver.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs index 0556df30a6c6..12d188d19df8 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/MessageTypeInfoResolver.cs @@ -95,7 +95,8 @@ private JsonPropertyInfo CreatePropertyInfo(JsonTypeInfo typeInfo, string name, // A property with a wrapper type is usually the underlying type on the DTO. // For example, a field of type google.protobuf.StringValue will have a property of type string. - // Sometimes the wrapper type is exposed so we need to do some extra work to translate the value. + // However, the wrapper type is exposed if someone manually creates a DTO with it, or there is a problem + // detecting wrapper type in code generation. For example, https://github.com/protocolbuffers/protobuf/issues/22744 FieldDescriptor? wrapperTypeValueField = null; if (field.FieldType == FieldType.Message && ServiceDescriptorHelpers.IsWrapperType(field.MessageType)) {