1

So it seems like the new Json library doesn't deserialize ( or even serialize for that matter) the UInt32 types. In an MVC application the following is sent in the payload of a POST:

{
   "Overrides":{
      
   },
   "Directions":{
      "data1" : "data2"
   },   
   "ModeId": "1782354543",
   "TestNumber" : 10
}

And the class it should be serialized to is like this:

public class Payload
    {
        public uint ModeId;
        public Dictionary<string, string> Directions{ get; set; } = new();
        public Dictionary<string, string> Overrides { get; set; } = new();
        public int TestNumber { get; set; }
    }

But when the request is received, ModeId is 0. I tried adding a custom converter based on the unit tests I found here Like so:

public class UIntConverter : JsonConverter<uint>
    {
        public override uint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.String)
            {
                string stringValue = reader.GetString();
                if (uint.TryParse(stringValue, out uint x))
                {
                    return x;
                }
            }
            if (reader.TokenType == JsonTokenType.Number)
            {
                return reader.GetUInt32();
            }

            throw new JsonException();
        }

        public override void Write(Utf8JsonWriter writer, uint value, JsonSerializerOptions options)
        {
            writer.WriteNumberValue(value);
        }
    }

And registering it like this:

services.AddControllers()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.WriteIndented = false;
                    options.JsonSerializerOptions.IgnoreNullValues = true;
                    options.JsonSerializerOptions.PropertyNamingPolicy = null;
                    options.JsonSerializerOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
                    options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
                    options.JsonSerializerOptions.Converters.Insert(options.JsonSerializerOptions.Converters.Count,new UIntConverter());

                });

I even failed to serialize my class into string using the same converter. It just ignores the uid property.

var payload = new Payload()
{
   Directions = new Directions
   {
     {"data1", "data2" }
   },
   ModeId = 1782354543,
   TestNumber = 10
}
//then try to serialize
var defaultJsonSerializerSettings = new JsonSerializerOptions()
        {
            IgnoreNullValues = true,
            WriteIndented = false,
            PropertyNamingPolicy = null,
            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
            Converters = { new UIntConverter()}            
        };
Serializer.Serialize(payload, typeof(Payload), defaultJsonSerializerSettings); 

It serializes everything, except the uint property which is ignored altogether.

What do you suggest/advise for the serialization/deserialization of a uint property?

[UPDATE] : The issue was the fact that I was trying to serialize/deserialize a field as opposed to a property with public accessors as is pointed out in the answers. It just so happened that field was of type uint.

Ben
  • 538
  • 1
  • 9
  • 24
  • 2
    Possibly the cause: [Fields are not supported in System.Text.Json in .NET Core 3.1](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-core-3-1#include-fields) – devNull Apr 22 '21 at 23:12
  • 2
    Also see https://stackoverflow.com/q/58139759/5803406 – devNull Apr 22 '21 at 23:13
  • 4
    I.e., you're missing `{get; set;}` in `public uint ModeId;` (is that intentional?), then your converter can work. -- `= new()` is not needed. – Jimi Apr 22 '21 at 23:16
  • 1
    uint works fine – Keith Nicholas Apr 22 '21 at 23:48
  • 1
    @Keith Nicholas Yep, setting `NumberHandling = JsonNumberHandling.AllowReadingFromString` among the options. Or include Fields, setting `IncludeFields = true`. So many possibilities... – Jimi Apr 23 '21 at 02:35
  • Less likely, but also possible: your converter is inserted at the end of the Converters list; is there a compatible converter in the list before yours? – Kit Apr 23 '21 at 03:11
  • @devNull yes, that was exactly the issue. Right in my face! Thanks for the hint. – Ben Apr 23 '21 at 13:39
  • 1
    @Jimi It was not intentional. It was missing the accessors. And that was the issue. Thanks for the hint about including fields. I was looking for an option that allows parsing numbers, I tried different JsonNumberHandling options, but who'd have thought that Fields are the issue. Thanks. – Ben Apr 23 '21 at 13:41

1 Answers1

3

If you're on .NET 5, or if - as @Jimi pointed out - install <PackageReference Include="System.Text.Json" Version="5.0.2" />, you can then use IncludeFields and AllowReadingFromString options:

var serializeOptions = new JsonSerializerOptions
{
    IncludeFields = true,
    NumberHandling = JsonNumberHandling.AllowReadingFromString
};

var p = JsonSerializer.Deserialize<Payload>(json, serializeOptions);

Before .NET5 you'll need to change ModeId to be a property.


BTW. You could deal with string/int with no converter:

public string ModeId {get; set}
public uint ModeIdParsed => uint.Parse(ModeId);

If you need to keep ModeId name then you could

public class Payload
{
   [JsonPropertyName("ModeId")]
   public string modeId {get;set;}

   [JsonIgnore]
   public uint ModeId => uint.Parse(modeId);
}

Unfortunately in 3.1 you'll need to keep the string variant public (deserializing privates is available from v5).

tymtam
  • 31,798
  • 8
  • 86
  • 126
  • That's of course correct, but it's not the .Net version, it's the `System.Text.Json` version that matters. You can add v.5.0.2 to .Net Framework, too, just getting the [NuGet Package](https://www.nuget.org/packages/System.Text.Json). – Jimi Apr 23 '21 at 03:06
  • Thank you both for the clues. The issue as Jimi had pointed out was the field. And turns out it doesn't need a converter as you pointed. I'm using .net5 and System.Text.Json 5.0.2 – Ben Apr 23 '21 at 13:45