Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy JsonSerializerSettings from JsonSerializer to new JsonSerializer

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)
like image 249
Zachary Dow Avatar asked Jul 06 '16 17:07

Zachary Dow


People also ask

Is JsonSerializer thread safe?

On iOS 7 and later and macOS 10.9 and later, JSONSerialization is thread safe.

What is JsonSerializerOptions?

JsonSerializerOptions.NumberHandling Property (System.Text.Json) Gets or sets an object that specifies how number types should be handled when serializing or deserializing.

What is Jsonconvert SerializeObject C#?

SerializeObject Method (Object, Type, JsonSerializerSettings) Serializes the specified object to a JSON string using a type, formatting and JsonSerializerSettings. Namespace: Newtonsoft.Json.


1 Answers

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.
}
like image 74
Zachary Dow Avatar answered Oct 19 '22 23:10

Zachary Dow