Configuring JsonNetSerializer and JsonNetBodyDeser

2019-02-12 22:18发布

I am a noob to Nancy. I have been using it as a framework to produce a REST API. I am familiar with Json.NET so I've been playing with the Nancy.Serialization.JsonNet package.

My goal: to customize the behavior (i.e. change the settings) of the JsonNetSerializer and JsonNetBodyDeserializer.

Specifically I'd like to incorporate the following settings...

var settings = new JsonSerializerSettings { Formatting = Formatting.Indented };
settings.Converters.Add( new StringEnumConverter { AllowIntegerValues = false, CamelCaseText = true } );

I wanted to perform this customization using the builtin TinyIoC container to avoid the inheritance chain and limit potential issues arising from any changes in the Nancy.Serialization.JsonNet package.

NOTE: As a temporary workaround, I have leveraged inheritance to create CustomJsonNetSerializer and CustomJsonNetBodyDeserializer.

I have tried several approaches to incorporate this configuration at least for the JsonNetSerializer. I've not tried configuring the JsonNetBodyDeserializer using the TinyIoC yet. I imagine it will be done similarly. All the work I've tried is in my CustomNancyBootstrapper (which inherits from DefaultNancyBootstrapper).

Most successful approach so far: override ConfigureApplicationContainer

protected override void ConfigureApplicationContainer( TinyIoCContainer container )
{
    base.ConfigureApplicationContainer( container );

    // probably don't need both registrations, and I've tried only keeping one or the other
    var settings = new JsonSerializerSettings { Formatting = Formatting.Indented };
    settings.Converters.Add( new StringEnumConverter { AllowIntegerValues = false, CamelCaseText = true } );
    container.Register( new JsonNetSerializer( JsonSerializer.CreateDefault( settings ) ) );
    container.Register<ISerializer>( new JsonNetSerializer( JsonSerializer.CreateDefault( settings ) ) );
}

I have traced the code and watched the JsonNetSerializer(JsonSerializer serializer) constructor in the JsonNet package.

Potential problem: I noticed the constructor is called twice. I did not expect this behavior.

The first time everything is just right - my customization is added and registered properly. But, then the second time happens and the types are re-registered, without the settings customization. The re-registration appears to replace the original registration losing my settings customization.

The call stack the second time the constructor is called shows that it is called during GetEngine and GetEngineInternal which seems to try to build a NancyEngine (I am using the self-host package so this happens in program.cs -- using(var host = new NancyHost(uri)) ).

Seems like I either need to tell Nancy not to do something or I need to hook in to a later part in the chain.

Any help would be appreciated.

1条回答
干净又极端
2楼-- · 2019-02-12 22:47

Typically the way to solve this in Nancy is to implement your own JSON Serializer like so:

public sealed class CustomJsonSerializer : JsonSerializer
{
    public CustomJsonSerializer()
    {
        ContractResolver = new CamelCasePropertyNamesContractResolver();
        Converters.Add(new StringEnumConverter
        {
            AllowIntegerValues = false, 
            CamelCaseText = true
        });
        Formatting = Formatting.Indented;
    }
}

Here you can override all the settings.

Then you can register it, I do this by using IRegistrations

public class JsonRegistration : IRegistrations
{
    public IEnumerable<TypeRegistration> TypeRegistrations
    {
        get
        {
            yield return new TypeRegistration(typeof(JsonSerializer), typeof(CustomJsonSerializer));
        }
    }

    public IEnumerable<CollectionTypeRegistration> CollectionTypeRegistrations { get; protected set; }
    public IEnumerable<InstanceRegistration> InstanceRegistrations { get; protected set; }
}

Q: How does this approach differ from creating CustomJsonNetSerializer inheriting from JsonNetSerializer and then registering it in ConfigureApplicationContainer (container.Register( typeof(JsonNetSerializer), typeof(CustomJsonNetSerializer) )?

A: The JsonSerializer is the implementation if json.net for Nancy, this is the recommended method we define on the readme on github:

https://github.com/NancyFx/Nancy.Serialization.JsonNet#customization

The class you mention is the serialization of an object to JSON, there is another which handles deserialization, both of which utilize JsonSerializer internally:

https://github.com/NancyFx/Nancy.Serialization.JsonNet/blob/master/src/Nancy.Serialization.JsonNet/JsonNetSerializer.cs#L10

Using this method makes the implementation settings consistent anywhere that the JsonSerializer is used.

Q: can I correctly infer from the approach you've outlined that I would no longer need to explicitly register CustomJsonSerializer in the ConfigureApplicationContainer override in my CustomNancyBootstrapper?

A: The method I've done for registering is just a cleaner abstraction for registering dependencies, rather than making one giant Bootstrapper, you can create a few smaller specific classes.

Yes using my method means you do not need to register in the bootstrapper.

查看更多
登录 后发表回答