Eugene
Eugene

Reputation: 277

How to force a particular property to be deserialized as string instead of number?

I have different backends giving back almost the same JSON data model except for a nested json object with the id field which sometimes can be a pure number such as:

{ "something": { "id": 1 } }

or a string, such as:

{ "something": { "id": "ex.GUID" } }

plus the model is made of nullables, different System types and custom classes. And I would like those json numbers 1 to be set as"1" always(so the C# class model has the this property as public string Id { get; set; }), possibly for that precise property.

Is there any JsonConverter<T> or property [Attribute] that could handle carefully such parsing/deserialization?

ps: the current JsonConverter<object> answers don't seem to handle well(exceptions) such scenario as described above

Upvotes: 0

Views: 1495

Answers (2)

tmaj
tmaj

Reputation: 34987

Just for completeness, you can solve this with object.

using System.Text.Json;

DeserialiseAndPrint("{ \"something\": { \"id\": 1 } }");
DeserialiseAndPrint("{ \"something\": { \"id\": \"ex.GUID\" } }");
DeserialiseAndPrint("{ \"something\": { } }");

void DeserialiseAndPrint(string v)
{
   JsonSerializerOptions options = new (){ PropertyNameCaseInsensitive = true };
   var o = JsonSerializer.Deserialize<RootObject>(v, options);
   Console.WriteLine($"{o?.Something?.Id} -> {o?.Something?.IdParsed}");
}

class RootObject
{
    public Something? Something { get; set; }
}

class Something
{
    public object? Id { get; set; }

    public string? IdParsed => Id?.ToString();
}

This prints:

1 -> 1
ex.GUID -> ex.GUID
 -> 

Upvotes: 0

Sir Rufo
Sir Rufo

Reputation: 19096

You have to use a custom JsonConverter<T> for that.

/// taken from https://www.thecodebuzz.com/system-text-json-create-a-stringconverter-json-serialization/

public class StringConverter : System.Text.Json.Serialization.JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.Number)
        {
            var stringValue = reader.GetInt32();
            return stringValue.ToString();
        }
        else if (reader.TokenType == JsonTokenType.String)
        {
            return reader.GetString();
        }

        throw new System.Text.Json.JsonException();
    }

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

and register that in the JsonSerializerOptions

var options = new JsonSerializerOptions
    {
        Converters = { new StringConverter() }, 
        PropertyNameCaseInsensitive = true, 
    };

var jsonA = "{ \"something\": { \"id\": \"ex.GUID\" } }";
var a = JsonSerializer.Deserialize<RootObject>(jsonA, options);

var jsonB = "{ \"something\": { \"id\": 1 } }";
var b = JsonSerializer.Deserialize<RootObject>(jsonB, options);

complete example in this fiddle

Upvotes: 2

Related Questions