I'm using an api endpoint and they are returning invalid responses in some of the fields instead of null values for decimals. here is an example
{
"name": "MyName",
"decimalOne": "0.01",
"decimalTwo": "None",
"decimalThree": "-"
}
I would like my code to handle these custom "null" things they are sending me as just regular nulls. Is there a way for me to do this?
CodePudding user response:
you can try something like this, ( I don't know how many different decimal fields you have)
Name name=System.Text.Json.JsonSerializer.Deserialize<Name>(json);
public class Name
{
public string name {get; set;}
[JsonPropertyName("decimalOne")]
public string decimalOneString { get; set;}
[System.Text.Json.Serialization.JsonIgnore]
public decimal? decimalOne
{
get { if (decimal.TryParse(decimalOneString, out var result)) return result;
return null;
}
set { decimalOneString = value.ToString();}
}
}
CodePudding user response:
I've decided to do a NullableConverterFactory with additional "null" parameters we can pass in.
looks like this.
/// <summary>
/// Converter that can parse nullable types. int? decimal? etc
/// </summary>
public class NullableConverterFactory : JsonConverterFactory
{
private static readonly byte [] EMPTY = Array.Empty<byte>();
private static readonly HashSet<byte[]> NULL_VALUES = new()
{
EMPTY
};
/// <summary>
/// empty constructor.
/// </summary>
public NullableConverterFactory()
{
}
/// <summary>
/// Supply additional accepted null values as byte[] here.
/// </summary>
/// <param name="additionalNullValues"></param>
public NullableConverterFactory(HashSet<byte[]> additionalNullValues)
{
foreach (byte[] nullValue in additionalNullValues)
{
NULL_VALUES.Add(nullValue);
}
}
/// <summary>
/// Returns true if this converter can convert the value.
/// </summary>
/// <param name="typeToConvert"></param>
/// <returns></returns>
public override bool CanConvert(Type typeToConvert) => Nullable.GetUnderlyingType(typeToConvert) != null;
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options) =>
(JsonConverter)Activator.CreateInstance(
typeof(NullableConverter<>).MakeGenericType(
new Type[] { Nullable.GetUnderlyingType(type) }),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: new object[] { options, NULL_VALUES },
culture: null);
class NullableConverter<T> : JsonConverter<T?> where T : struct
{
private readonly HashSet<byte[]> _nullValues;
// DO NOT CACHE the return of (JsonConverter<T>)options.GetConverter(typeof(T)) as DoubleConverter.Read() and DoubleConverter.Write()
// DO NOT WORK for nondefault values of JsonSerializerOptions.NumberHandling which was introduced in .NET 5
public NullableConverter(JsonSerializerOptions options, HashSet<byte[]> nullValues)
{
_nullValues = nullValues;
}
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.String)
{
return JsonSerializer.Deserialize<T>(ref reader, options);
}
foreach (byte[] nullValue in _nullValues)
{
if (reader.ValueTextEquals(nullValue))
{
return null;
}
}
return JsonSerializer.Deserialize<T>(ref reader, options);
}
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) =>
JsonSerializer.Serialize(writer, value.Value, options);
}
}
usage:
new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true,
Converters =
{
new NullableConverterFactory(new HashSet<byte[]>
{
Encoding.UTF8.GetBytes("-"),
Encoding.UTF8.GetBytes("None")
})
},
NumberHandling = JsonNumberHandling.AllowReadingFromString,
AllowTrailingCommas = true
};