1

I have a .NET 5 ASP REST service and I'm trying to remove Newtonsoft with System.Text.
Problem is that with Newtonsoft I was able to send GUID's without dashes and the model was bonded successfully, now I get nulls (with System.Text.Json)

I was able to isolate the problem using https://dotnetfiddle.net/ocayBp

using System;

public class Program
{
    public static void Main()
    {
        //this works
        var json = "{\"AuthorId\":\"6917d697-c1ab-4fb7-b297-8b2a23100bfc\",\"Title\":\"test title\"}";
        //this doesn't work
        //var json = "{\"AuthorId\":\"6917d697c1ab4fb7b2978b2a23100bfc\",\"Title\":\"test title\"}";
        var result1 = Newtonsoft.Json.JsonConvert.DeserializeObject<Post>(json);
        result1.Dump();
        var result2 = System.Text.Json.JsonSerializer.Deserialize<Post>(json);
        result2.Dump();
    }
}

public class Post
{
    public Guid AuthorId { get; set; }

    public string Title { get; set; }
}

Newtonsoft correctly deserialized string to GUID, with System.Text.Json approach I get this error:

Unhandled exception. System.Text.Json.JsonException: The JSON value could not be converted to System.Guid. Path: $.AuthorId | LineNumber: 0 | BytePositionInLine: 46. ---> System.FormatException: The JSON value is not in a supported Guid format. at System.Text.Json.Utf8JsonReader.GetGuid() at System.Text.Json.Serialization.Converters.GuidConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader) at System.Text.Json.Serialization.Converters.ObjectDefaultConverter1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.Serialization.JsonConverter1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.Serialization.JsonConverter1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) --- End of inner exception stack trace --- at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex) at System.Text.Json.Serialization.JsonConverter1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan1 utf8Json, JsonTypeInfo jsonTypeInfo, Nullable1 actualByteCount) at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan1 json, JsonTypeInfo jsonTypeInfo) at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options) at Program.Main()

I'm setting up JSON in ConfigureServices:

services.AddControllers(
    options =>
    {
        options.ValueProviderFactories.Add(new ClaimValueProviderFactory());
    }).AddJsonOptions(options =>
{
    options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});

How can I get the same behavior with System.Text.Json as with Newtonsoft.Json.

I found an issue on GitHub(https://github.com/dotnet/runtime/issues/30692) but there is no TryGetGuid method where you can specify the format.

4
  • Your fiddle fails because the GUID doesn't have any dashes in it. Is your code example the wrong way around (works vs not)? Commented May 19, 2022 at 13:47
  • 1
    when I send GUID without dashes I get an exception. The point is that Newtonsoft supports GUID's with and without dashes. I'd like to get the same behavior with System.Text.Json. Commented May 19, 2022 at 13:50
  • 1
    That's not standard. You need to write a override the default converter with a custom one. Read this Commented May 19, 2022 at 13:57
  • @JHBonarius I just did that Commented May 19, 2022 at 13:58

2 Answers 2

3

We can try to write a customer JsonConverter for Guid by JsonConverter<T>

public class GuidJsonConverter: JsonConverter<Guid>
{
    public override Guid Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options) =>
                Guid.Parse(reader.GetString()!);

    public override void Write(
            Utf8JsonWriter writer,
            Guid value,
            JsonSerializerOptions options) =>
                writer.WriteStringValue(value.ToString());
}

Then try to add convert to the property by JsonConverter Attribute.

public class Post
{
    [JsonConverter(typeof(GuidJsonConverter))]
    public Guid AuthorId { get; set; }

    public string Title { get; set; }
}

c# online

Sign up to request clarification or add additional context in comments.

Comments

2

I created a custom converter:

public sealed class JsonConverterGuid : System.Text.Json.Serialization.JsonConverter<Guid>
{
    public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TryGetGuid(out Guid value))
        {
            Console.WriteLine("guid");
            return value;
        }

        if (reader.TryGetString(out string str))
        {
            Console.WriteLine("string");
            return Guid.Parse(str);
        }

        throw new FormatException("The JSON value is not in a supported Guid format.");
    }

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

public static class Ext
{
    public static bool TryGetString(this Utf8JsonReader je, out string parsed)
    {
        var(p, r) = je.TokenType switch
        {
            JsonTokenType.String => (je.GetString(), true),
            JsonTokenType.Null => (null, true),
            _ => (default, false)};
        parsed = p;
        return r;
    }
}

Maybe GUID without dashes isn't a standard, but I want to be compatible with Newtonsoft. I'm posting the answer, because someone might have the same error as I did.

and used it like this: https://dotnetfiddle.net/K9UXpb

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.