Nucs / JsonSettings

This library simplifies creating configuration for your C# app/service by utilizing the serialization capabilities of Json.NET to serialize nested (custom) objects, dictionaries and lists as simply as by creating a POCO and inheriting JsonSettings class.
MIT License
76 stars 18 forks source link

How to override serializer settings? In 2.x #29

Closed dave7280 closed 3 years ago

dave7280 commented 3 years ago

How to override serializer settings so i can add my own serialization converters for newtonsoft json? Is it possible to override serialization settings after JsonSettings object has been created but not yet loaded/read anything?

I tried to do:

MySettings s = JsonSettings.Configure<MySettings>("config.json");

  //would be nice to have s.OverrideSerializerSettings(mySerializationSettingsWithCustomConverters)

            s.BeforeLoad += (JsonSettings sender, ref string destinition) =>
            {
                sender.OverrideSerializerSettings = null; //new settings with my custom converters
//not possible as OverrideSerializerSettings  is protected
            };

however this is not possible as OverrideSerializerSettings is protected.

I can't do it in static regular way, by adding overriding serialization settings in MySettings class definiton either, as my custom JSON converter is dynamic. Therefore i'd need to pass variable somehow to MySettings class and this is not possible either My custom converter is taking argument coming from rest of application logic like this.

new ExchangeConverter{ListExchanges = model.listExchanges});
Nucs commented 3 years ago

Better approach would be to modify existing configuration on static JsonSettings.SerializationSettings. Although for individual configuration, OverrideSerializerSettings is virtual and can be overriden - but you should include the default configuration the library has configured in-order for the Json.NET to work as expected.

Heres the default as of 2.0.0-alpha5:

public static JsonSerializerSettings SerializationSettings { get; set; } = new JsonSerializerSettings {
    Formatting = Formatting.Indented, 
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 
    NullValueHandling = NullValueHandling.Include, 
    ContractResolver = new FileNameIgnoreResolver(), 
    TypeNameHandling = TypeNameHandling.Auto
};

The resolving of settings during serialization/deserialization works as follows:

/// <summary>
///     Returns configuration based on the following fallback: <br/>
///     settings ?? this.OverrideSerializerSettings ?? JsonSettings.SerializationSettings ?? JsonConvert.DefaultSettings?.Invoke()
///              ?? throw new JsonSerializationException("Unable to resolve JsonSerializerSettings to serialize this JsonSettings");
/// </summary>
/// <param name="settings">If passed a non-null, This is the settings intended to use, not any of the fallbacks.</param>
/// <exception cref="JsonSerializationException">When no configuration valid was found.</exception>
protected virtual JsonSerializerSettings ResolveConfiguration(JsonSerializerSettings? settings = null) {
    return settings
           ?? this.OverrideSerializerSettings
           ?? JsonSettings.SerializationSettings
           ?? JsonConvert.DefaultSettings?.Invoke()
           ?? throw new JsonSerializationException("Unable to resolve JsonSerializerSettings to serialize this JsonSettings");
}

As you can see, ResolveConfiguration is also virtual and can be overriden but the logic of settings parameter has to be kept.

Nucs commented 3 years ago

For specifying a converter for a property, the easiest way is JsonConverterAttribute Simply put this attribute on your property and Json.NET will do the rest.

[JsonConverter(typeof(ExchangeConverter))]
public ExchangeType Exchange { get; set; }

Note: JsonConverterAttribute can also be specified on an interface property as I use it in IVersionable and will apply to any class inheriting it

public interface IVersionable {
        [JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
        public Version Version { get; set; }
}