Copy JsonSerializerSettings from JsonSerializer to new JsonSerializer

人盡茶涼 提交于 2019-12-06 19:14:25

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!