问题
Is there any way to take the settings out of a JsonSerializer
class and reimplement them in a new JsonSerializer
?
There doesn't seem to be any methods to do anything like that. The best I found was a private method to be called through reflection, ApplySerializerSettings
.
I'm trying to take the serializer from the WriteJson
method and copy it into a new serializer, tweaking it a bit. Specifically I want to replace the ContractResolver
.
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
回答1:
Seems like the best way is to just copy all the settings into a new object. There are a ton of them, so here's a nice extension method to work with (as of 8.0.3).
public static class JsonSerializerExtensions
{
public static JsonSerializer DeepCopy(this JsonSerializer serializer)
{
var copiedSerializer = new JsonSerializer
{
Context = serializer.Context,
Culture = serializer.Culture,
ContractResolver = serializer.ContractResolver,
ConstructorHandling = serializer.ConstructorHandling,
CheckAdditionalContent = serializer.CheckAdditionalContent,
DateFormatHandling = serializer.DateFormatHandling,
DateFormatString = serializer.DateFormatString,
DateParseHandling = serializer.DateParseHandling,
DateTimeZoneHandling = serializer.DateTimeZoneHandling,
DefaultValueHandling = serializer.DefaultValueHandling,
EqualityComparer = serializer.EqualityComparer,
FloatFormatHandling = serializer.FloatFormatHandling,
Formatting = serializer.Formatting,
FloatParseHandling = serializer.FloatParseHandling,
MaxDepth = serializer.MaxDepth,
MetadataPropertyHandling = serializer.MetadataPropertyHandling,
MissingMemberHandling = serializer.MissingMemberHandling,
NullValueHandling = serializer.NullValueHandling,
ObjectCreationHandling = serializer.ObjectCreationHandling,
PreserveReferencesHandling = serializer.PreserveReferencesHandling,
ReferenceResolver = serializer.ReferenceResolver,
ReferenceLoopHandling = serializer.ReferenceLoopHandling,
StringEscapeHandling = serializer.StringEscapeHandling,
TraceWriter = serializer.TraceWriter,
TypeNameHandling = serializer.TypeNameHandling,
SerializationBinder = serializer.SerializationBinder,
TypeNameAssemblyFormatHandling = serializer.TypeNameAssemblyFormatHandling
};
foreach (var converter in serializer.Converters)
{
copiedSerializer.Converters.Add(converter);
}
return copiedSerializer;
}
}
It's ugly, but at least you only have to write it once. Be slightly careful, as the properties themselves are not deep copied.
Answer below is flaky depending on your implementation, especially when it comes to resolving a contract. Keeping it in just in case it helps someone.
So, I can't quite copy the settings, but I found a good work around that might want to be considered. You can simply set the properties you want to change, in a locked context, and then reset them afterward.
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var thing = value as IThing;
if (thing == null)
throw new ArgumentException($"Writing Json failed because " +
"value was not a 'Thing' of type, {typeof(IThing).FullName}");
JObject jsonThing;
//If your solution is multithreaded,
//and is using a shared serializer (which you probably are),
//you should lock the serializer so that it doesn't accidentally use
//the "CustomObjectResolver"
lock (serializer)
{
//Hold the original value(s) to reset later
var originalContractResolver = serializer.ContractResolver;
//Set custom value(s)
serializer.ContractResolver = new CustomObjectResolver();
//Serialization with custom properties
jsonThing = JObject.FromObject(value, serializer);
//Reset original value(s)
serializer.ContractResolver = originalContractResolver;
}
//Finish serializing and write to writer.
}
回答2:
Not sure if this is on-topic - but as this was the first post that came up when searching for Clone JSONSerializerSettings in my Google, I thought I would leave a notice.
I have just had a somewhat similar requirement.
In my .Net Core WebApi solution we have a default for serializing the JSON, with
jsonSerializaerSettings.NullValueHandling = NullValueHandling.Ignore;
But in a few endpoints I would like to serialize null properties as well, as these endpoints represent a dataset. So all objects should have all properties when serialized.
I've tried a few approaches, and then reading up on Options pattern, I stumbled upon IOptionSnapshot - https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1#reload-configuration-data-with-ioptionssnapshot
So I ended up getting an IOptionsSnapshot injected into my controller method, and this gives me a copy of the default options, which I can modify and use for this single request.
And on next request, injected options are back to default.
Nice and easy - at least for my purpose.
So a final solution for my sample is like this
[HttpPost("dataset", Name = "GetReportDataset")]
public IActionResult GetReportDataset(
[FromServices]IOptionsSnapshot<MvcJsonOptions> jsonOptions,
[FromBody]DataSetReport report,
[FromQuery]int? top)
{
//Logic to fetch dataset
var myDataSet = myRepository.GetDataset(report, top);
var serializerSettings = jsonOptions.Value.SerializerSettings;
serializerSettings.NullValueHandling = NullValueHandling.Include;
return new JsonResult(myDataSet, serializerSettings);
}
Hope this helps someone else coming here for similar usage.
/Anders
来源:https://stackoverflow.com/questions/38230326/copy-jsonserializersettings-from-jsonserializer-to-new-jsonserializer