I'm serializing/deserializing a dictionary<string,object>, but when I deserialize, instead of the values being object
s, they are JsonElement
s.
I have a unit test that demonstrates the problem. How can I deserialize these values to object
s? Can anyone help? Do I have to write a custom converter?
[Test]
public void SerializationTest()
{
var coll = new PreferenceCollection();
coll.Set("email", "[email protected]");
coll.Set("age", 32);
coll.Set("dob", new DateOnly(1991, 2, 14));
var json = JsonSerializer.Serialize(coll);
Assert.NotNull(json);
var clone = JsonSerializer.Deserialize<PreferenceCollection>(json);
Assert.NotNull(clone);
Assert.IsNotEmpty(clone!.Values);
foreach (var kvp in clone.Values)
{
Assert.That(kvp.Value is not null);
// Test fails here because kvp.Value is JsonElement.
Assert.That(kvp.Value!.Equals(coll.Values[kvp.Key]));
}
}
using System.Text.Json.Serialization;
namespace MyCompany.Preferences;
public class PreferenceCollection
{
public PreferenceCollection()
{
Values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}
[JsonExtensionData] public IDictionary<string, object> Values { get; set; }
public object Get(string key)
{
if (Values.TryGetValue(key, out object value))
{
return value;
}
return null;
}
public T Get<T>(string key)
{
if (Values.TryGetValue(key, out var value))
{
return (T)value;
}
return default;
}
public void Set(string key, object value)
{
if (value == null)
{
Remove(key);
}
else
{
if (!Values.TryAdd(key, value))
{
Values[key] = value;
}
}
}
public void Remove(string key) => Values.Remove(key);
public void Clear() => Values.Clear();
}
This was solved with a JSON converter for object
.
This was my starting point: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?pivots=dotnet-7-0#deserialize-inferred-types-to-object-properties
/// <summary>
/// <seealso href="https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?pivots=dotnet-7-0#deserialize-inferred-types-to-object-properties"/>
/// </summary>
public sealed class ObjectJsonConverter : JsonConverter<object>
{
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
string str = reader.GetString()!;
if (Guid.TryParse(str, out Guid g))
{
return g;
}
if (DateOnly.TryParse(str, out DateOnly date))
{
return date;
}
if (DateTime.TryParse(str, out _))
{
return reader.GetDateTime();
}
return str;
}
if (reader.TokenType == JsonTokenType.Number && reader.TryGetInt64(out long l))
{
if (l is >= int.MinValue and <= int.MaxValue && reader.TryGetInt32(out int i)) return i;
return l;
}
return reader.TokenType switch
{
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Number => reader.GetDouble(),
_ => JsonDocument.ParseValue(ref reader).RootElement.Clone()
};
}
public override void Write(Utf8JsonWriter writer, object? objectToWrite, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, objectToWrite, objectToWrite?.GetType() ?? typeof(object), options);
}
}
The converter can then be used like this:
var jsonOptions = new JsonOptions();
jsonOptions.Converters.Add(new ObjectJsonConverter());
var myDictionary = JsonSerializer.Deserialize<string, object>(someJson, jsonOptions);