Home > front end >  Can I use a set method instead of a Json Property?
Can I use a set method instead of a Json Property?

Time:11-26

I am using this to deserialize my Json response from an Api.

var apiResponse = await GetAsync<MyResponseModel>(request);

In my response model there is property that is an int, but the api for some reason formats it as a float. So it looks like this:

{
"Quantity": 6.000
}

Now I parse it with this trick:

[JsonProperty("Quantity")]
private float QuantityFloat {
    set => Quantity = IsInt(value) ? (int) value: throw new ArgumentException("Tried to parse number to Quantity that is not an int.");
}

public int Quantity { get; set; }

private static bool IsInt(float value)
{
    var x = (int) value;
    var temp2 = value - x;
    return temp2 <= 0;
}

My linter now complains: "Properties with only setters are confusing and counterintuitive. Instead, a property getter should be added if possible, or the property should be replaced with a setter method." So I was asking myself if there is a better more elegant way of doing this.

CodePudding user response:

I would suggest creating a JsonConverter<int> that can handle both integer and decimal-formatted values:

(This solution is for System.Text.Json)

public class IntConverter : JsonConverter<int>
{
    public override int Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TryGetInt32(out var intVal))
        {
            return intVal;
        }
        // customize this part as necessary to satisfactorily deserialize
        // the float value as int, or throw exception, etc.
        float floatValue = reader.GetSingle();
        return (int)floatValue;
    }

    public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

Then you can decorate your property with [JsonConverter(typeof(IntConverter))] which will cause the converter to be used during serialization:

public class Model
{
    [JsonConverter(typeof(IntConverter))]
    public int Quantity { get; set; }
}

Handling it like this takes away the need for any extra properties, thus simplifying your solution. This is especially true if you have to handle this kind of scenario in multiple places.

Try it online


JSON.NET Solution:

public class IntConverter : JsonConverter<int>
{
    public override int ReadJson(JsonReader reader, Type objectType, int existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Float)
        {
            // customize this part as necessary to satisfactorily deserialize
            // the float value as int, or throw exception, etc.
            double value = (double)reader.Value;
            return (int)value;
        }
        long intVal = (long)reader.Value;
        return (int)intVal;
    }

    public override void WriteJson(JsonWriter writer, int value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

with the following model:

public class Model
{
    [JsonConverter(typeof(IntConverter))]
    public int Quantity { get; set; }
}

Try it online

  • Related