EfCoreJsonValueConverter
EfCoreJsonValueConverter copied to clipboard
Customise JSON serialisation
It's possible to customise JSON serialisation at a global level by changing JsonConvert.DefaultSettings
, but it would be good to have the option to customise it just for EF Core JSON serialisation by specifying a JsonSerializerSettings
, for example to prevent cyclic references from being serialised, configure null handling, or to add type information to the serialised JSON to support interfaces:
var jsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
};
var json = JsonConvert.SerializeObject(obj, jsonSettings);
var obj = JsonConvert.DeserializeObject<T>>(json, jsonSettings);
At it's most basic, this could be implemented as a global setting by adding a static JsonSerializerSettings
in this library (or a configuration class that composes it). I imagine a global setting would be what most people would want.
Alternatively (or additionally), the HasJsonValueConversion
extension method could have an optional JsonSerializerSettings
argument added to it. Not sure if there would really be a need to configure it for each property though.
I'd like this feature too. I've implemented the most basic solution in a fork, adding a static JsonSerializerSettings
exposed from JsonHelper
and making sure both the converter and comparer use the settings.
In my application, I added an extension method for DbContextOptionsBuilder
to modify the settings in my Startup.cs without referring to the JsonHelper
directly.
public static class DbContextOptionsBuilderExtensions
{
public static DbContextOptionsBuilder AddJsonConverterSettings(this DbContextOptionsBuilder dbContextOptions, Action<JsonSerializerSettings> setupAction)
{
setupAction(JsonHelper.SerializerSettings);
return dbContextOptions;
}
}
And use it like so:
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("Application"));
options.AddJsonConverterSettings(settings =>
{
settings.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
});
I suppose that extension method could also be included in the library instead and then JsonHelper
could remain internal
.
@cocowalla this seems useful suggestion. I suppose that global settings should be sufficient for usual needs and pretty straightforward to implement.
@webwizgordon this looks viable to me, as well as the consideration to add required extensions in library for less wiring. I wonder if it would be better to pass configuration settings inside the ApplicationDbContext setup though. For example if the context implementation is shared between multiple applications.
You might also want to consider having a separate instance of JsonSerializerSettings
per DbContext
type instead of a single static instance shared between all DbContext
types. Someone could conceivably have multiple types of DbContexts
and may want different JsonSerializerSettings
for each for some reason.
Is there any update about this? That would be really useful !
For those looking for a workaround, add this extension method to your project:
/// <summary>
/// Extensions for <see cref="T:Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder" />.
/// </summary>
public static class PropertyBuilderExtensions
{
/// <summary>Serializes field as JSON blob in database.</summary>
public static PropertyBuilder<T> HasJsonValueConversion<T>(
this PropertyBuilder<T> propertyBuilder)
where T : class
{
propertyBuilder.HasConversion(
v => JsonConvert.SerializeObject(v, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore}),
v => JsonConvert.DeserializeObject<T>(v, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore }));
return propertyBuilder;
}
}
Here you can change your serializerSettings and then use it per property like this:
builder
.Property(x => x.MetaData)
.HasJsonValueConversion();
This method also allows you to change on a per-context basis, just make a second extension method with your unique serializer settings, or tweak the HasJsonValueConversion()
method to pass a new JsonSerializerSettings()
.