Skip to content

STJ: Deserializing UInt16 fails when using JsonTypeInfo #56792

Closed
@jasper-d

Description

@jasper-d

Description

Deserializing a number to a property of type System.UInt16 fails when using JsonTypeInfo<T>. The same JSON string can be deserialized when using other integral types or reflection.

The generated code for UInt16 (see below) looks odd compared to the generated source for other value types. The UInt16 getter calls CreateObjectInfo<T> instead of CreateValueInfo<T> in the else branch.

I reckon that is because UInt16 is not added to _numberTypes here (UInt64 is added twice though):

private void PopulateNumberTypes()
{
Debug.Assert(_numberTypes != null);
_numberTypes.Add(ResolveType(SpecialType.System_Byte));
_numberTypes.Add(ResolveType(SpecialType.System_Decimal));
_numberTypes.Add(ResolveType(SpecialType.System_Double));
_numberTypes.Add(ResolveType(SpecialType.System_Int16));
_numberTypes.Add(ResolveType(SpecialType.System_SByte));
_numberTypes.Add(ResolveType(SpecialType.System_Int32));
_numberTypes.Add(ResolveType(SpecialType.System_Int64));
_numberTypes.Add(ResolveType(SpecialType.System_Single));
_numberTypes.Add(ResolveType(SpecialType.System_UInt64));
_numberTypes.Add(ResolveType(SpecialType.System_UInt32));
_numberTypes.Add(ResolveType(SpecialType.System_UInt64));
}

InfoCtx.UInt16.g.cs:

namespace stj_ushort
{
    internal sealed partial class InfoCtx
    {
        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.UInt16> _UInt16;
        public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.UInt16> UInt16
        {
            get
            {
                if (_UInt16 == null)
                {
                    global::System.Text.Json.Serialization.JsonConverter customConverter;
                        if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(global::System.UInt16))) != null)
                        {
                            _UInt16 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::System.UInt16>(Options, customConverter);
                        }
                        else
                        {
                            _UInt16 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateObjectInfo<global::System.UInt16>(
                                Options,
                                createObjectFunc: static () => new global::System.UInt16(),
                                propInitFunc: UInt16PropInit,
                                default,
                                serializeFunc: null);
                        }
                }
        
                return _UInt16;
            }
        }
        
        private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] UInt16PropInit(global::System.Text.Json.Serialization.JsonSerializerContext context)
        {
            global::stj_ushort.InfoCtx jsonContext = (global::stj_ushort.InfoCtx)context;
            global::System.Text.Json.JsonSerializerOptions options = context.Options;
        
            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[0];
        
            return properties;
        }
    }
}

Configuration

Windows 10 x64
.NET SDK 6.0.100-preview.6.21355.2
System.Text.Json 6.0.0-rc.1.21403.3

Repro

using System.Text.Json; // 6.0.0-rc.1.21403.3
using System.Text.Json.Serialization;
using Xunit;

namespace stj_ushort {
    internal sealed class InfoUshort  { public ushort Port { get; set; } }
    internal sealed class InfoShort  { public short Port { get; set; } }
    internal sealed class InfoByte { public byte Port { get; set; } }

    [JsonSerializable(typeof(InfoUshort), GenerationMode = JsonSourceGenerationMode.Metadata)]
    [JsonSerializable(typeof(InfoShort), GenerationMode = JsonSourceGenerationMode.Metadata)]
    internal sealed partial class InfoCtx : JsonSerializerContext { }

    public class UnitTest1 {
        private const byte Expected = 42;
        private static readonly string JsonString = "{\"Port\":42}";
        private static readonly byte[] JsonBytes = System.Text.Encoding.ASCII.GetBytes(JsonString);
        
        [Fact] // Red, System.Text.Json.JsonException: The JSON value could not be converted to System.UInt16. Path: $.port | LineNumber: 0 | BytePositionInLine: 10.
        public void Deserialize_Ushort() {
            InfoUshort result = JsonSerializer.Deserialize(JsonBytes, InfoCtx.Default.InfoUshort);
            Assert.Equal(Expected, result.Port);
        }
        
        [Fact] // Green
        public void Deserialize_Short() {
            InfoShort result = JsonSerializer.Deserialize(JsonBytes, InfoCtx.Default.InfoShort);
            Assert.Equal(Expected, result.Port);
        }
        
        [Fact] // Green
        public void Deserialize_Ushort_Reflection() {
            InfoUshort result = JsonSerializer.Deserialize<InfoUshort>(JsonBytes);
            Assert.Equal(Expected, result.Port);
        }
    }
}

Stack trace

System.Text.Json.JsonException: The JSON value could not be converted to System.UInt16. Path: $.Port | LineNumber: 0 | BytePositionInLine: 10.

System.Text.Json.JsonException
The JSON value could not be converted to System.UInt16. Path: $.Port | LineNumber: 0 | BytePositionInLine: 10.
   at System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType) in System.Text.Json.dll:token 0x6000100+0x18
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) in System.Text.Json.dll:token 0x6000a51+0x22
   at System.Text.Json.Serialization.Converters.JsonMetadataServicesConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) in System.Text.Json.dll:token 0x600097e+0x24
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) in System.Text.Json.dll:token 0x60007b5+0x197
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader) in System.Text.Json.dll:token 0x60008e4+0xdb
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) in System.Text.Json.dll:token 0x6000a51+0x90
   at System.Text.Json.Serialization.Converters.JsonMetadataServicesConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) in System.Text.Json.dll:token 0x600097e+0x24
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) in System.Text.Json.dll:token 0x60007b5+0x197
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) in System.Text.Json.dll:token 0x60007a1+0xbf
   at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) in System.Text.Json.dll:token 0x60003bc+0xa
   at System.Text.Json.JsonSerializer.ReadUsingMetadata[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo jsonTypeInfo, Nullable`1 actualByteCount) in System.Text.Json.dll:token 0x60003bd+0x2f
   at System.Text.Json.JsonSerializer.Deserialize[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo) in System.Text.Json.dll:token 0x60003c0+0xe
   at stj_ushort.UnitTest1.Deserialize_Ushort()

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions