NancyFx / Nancy.Serialization.JsonNet

NewtonSoft.Json serializer for Nancy
MIT License
40 stars 32 forks source link

how to convert the null to '' ,when using Response.AsJson(msg); #44

Closed bluesky001 closed 7 years ago

bluesky001 commented 8 years ago

I use the Nancy.Serialization.JsonNet and register the JsonSerializer:

container.Register<JsonSerializer, CustomJsonSerializer>();

And define the class

public class CustomJsonSerializer : JsonSerializer
{
    public CustomJsonSerializer()
    {
        this.Formatting = Formatting.Indented;
        this.DateFormatString = "yyyy-MM-dd HH:mm:ss";
        this.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
        this.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
        this.NullValueHandling = NullValueHandling.Include;       
    }
}

I return a JSON object using return Response.AsJson(msg) in the interface, but the result contain null values. I want to know how to convert the null value to '' value, thanks!

Such as:

{
  "action": "GET  /webbase/GetTypeList/2/10",
  "error": "",
 "result": [
    {
      "ID": 27,
      "CType": "2",
      "TypeName": "in",
      "TopID": 0,
      "LinkUrl": **null**
    },
    {
      "ID": 28,
      "CType": "2",
      "TypeName": "out",
      "TopID": 0,
      "LinkUrl": **null**
    }
  ],
  "result2": null,
  "result3": null,
  "result4": null,
  "result1": null,
  "total": 0,
  "ResultType": "json"
}
khellang commented 8 years ago

See http://stackoverflow.com/a/23832417/682105:

public class NullToEmptyStringResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return type.GetProperties().Select(property => CreateProperty(property, memberSerialization)).ToList();
    }

    private JsonProperty CreateProperty(PropertyInfo property, MemberSerialization memberSerialization)
    {
        var jsonProperty = base.CreateProperty(property, memberSerialization);

        jsonProperty.ValueProvider = new NullToEmptyStringValueProvider(jsonProperty.ValueProvider, property);

        return jsonProperty;
    }

    private class NullToEmptyStringValueProvider : IValueProvider
    {
        private readonly IValueProvider _valueProvider;

        private readonly PropertyInfo _property;

        public NullToEmptyStringValueProvider(IValueProvider valueProvider, PropertyInfo property)
        {
            _valueProvider = valueProvider;
            _property = property;
        }

        public object GetValue(object target)
        {
            var result = _valueProvider == null
                ? _property.GetValue(target)
                : _valueProvider.GetValue(target);

            if (_property.PropertyType == typeof(string) && result == null)
            {
                result = string.Empty;
            }

            return result;
        }

        public void SetValue(object target, object value)
        {
            if (_valueProvider == null)
            {
                _property.SetValue(target, value);
            }
            else
            {
                _valueProvider.SetValue(target, value);
            }
        }
    }
}
public class CustomJsonSerializer : JsonSerializer
{
    public CustomJsonSerializer()
    {
        this.Formatting = Formatting.Indented;
        this.DateFormatString = "yyyy-MM-dd HH:mm:ss";
        this.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
        this.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
        this.NullValueHandling = NullValueHandling.Include;
+       this.ContractResolver = new NullToEmptyStringResolver();
    }
}
bluesky001 commented 8 years ago

thanks khellang! I use your solution, but it seems that does not work,and also return the null. Whether or not to use the wrong. or the nancy framework do not distinguish the convertaion when serialization.

khellang commented 8 years ago

@bluesky001 Where are you wiring up the CustomJsonSerializer? Make sure you do it in ConfigureApplicationContainer, after calling base.ConfigureApplicationContainer(container);.

bluesky001 commented 8 years ago

@khellang

write here.

protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
    base.ConfigureApplicationContainer(container);
    container.Register<JsonSerializer, CustomJsonSerializer>();
}

public class CustomJsonSerializer : JsonSerializer
{
    public CustomJsonSerializer()
    {
        this.Formatting = Formatting.Indented;
        this.DateFormatString = "yyyy-MM-dd HH:mm:ss";
        this.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
        this.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
        this.NullValueHandling = NullValueHandling.Include;
        **this.ContractResolver = new NullToEmptyStringResolver();**
    }
}
bluesky001 commented 8 years ago

@khellang

i copy the whole bootstrapper.cs file, please help to check the problem. thanks very much!

using System; using System.Collections.Generic; using System.Dynamic; using System.IO; using System.Linq; using System.Reflection; using Nancy; using Nancy.Authentication.Stateless; using Nancy.Bootstrapper; using Nancy.Conventions; using Nancy.ErrorHandling; using Nancy.TinyIoc; using Nancy.ViewEngines; using Nancy.Serialization.JsonNet; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using BWFilm.Api.Common; using Nancy.Session;

namespace BWFilm.Api { public class BootStrapper:DefaultNancyBootstrapper { protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context) { base.ConfigureRequestContainer(container, context);

        // Here we register our user mapper as a per-request singleton.
        // As this is now per-request we could inject a request scoped
        // database "context" or other request scoped services.
    }
    protected override void ConfigureConventions(NancyConventions nancyConventions)
    {
        base.ConfigureConventions(nancyConventions);

        nancyConventions.StaticContentsConventions
            .Add(StaticContentConventionBuilder.AddDirectory("static", @"Static"));
    }

    /// <summary>
    /// Default assemblies that are ignored for autoregister
    /// </summary>
    private new static readonly IEnumerable<Func<Assembly, bool>> DefaultAutoRegisterIgnoredAssemblies = new Func<Assembly, bool>[]
        {
            asm => asm.FullName.StartsWith("Microsoft.", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("System.", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("System,", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("mscorlib,", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("IronPython", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("IronRuby", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("xunit", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("Nancy.Testing", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("MonoDevelop.NUnit", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("SMDiagnostics", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("CppCodeProvider", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("WebDev.WebHost40", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("nunit", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("nCrunch", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("EntityFramework", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("glTech.CommonLibrary.NET", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("glTech.EntityFramework", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("log4net", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("Newtonsoft.Json", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("NPOI", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("Senparc.Weixin", StringComparison.Ordinal),
            asm => asm.FullName.StartsWith("System.Web.Razor", StringComparison.Ordinal),
        };

    protected override IEnumerable<Func<Assembly, bool>> AutoRegisterIgnoredAssemblies
    {
        get
        {
            return DefaultAutoRegisterIgnoredAssemblies;
        }
    }

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

        container.Register<JsonSerializer, CustomJsonSerializer>();
    }

    protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
    {
        base.ApplicationStartup(container, pipelines);
        CookieBasedSessions.Enable(pipelines);
        //Repositories.ApplicationInitialize.DatabaseInitialize("BWFilm.Api", new[] { "BWFilm.Api" });

        Nancy.Json.JsonSettings.MaxJsonLength = Int32.MaxValue;
        Nancy.Json.JsonSettings.MaxRecursions = Int32.MaxValue;

        var statelessAuthConfiguration = new StatelessAuthenticationConfiguration(nancyContext =>
        {
            //string authorization = nancyContext.Request.Headers["Authorization"].FirstOrDefault();

            //string token = "";
            //if (authorization != null && authorization.ToLower().Contains("Bearer".ToLower()) == true)
            //{
            //    token = authorization.Replace("Bearer".ToLower(), "").Trim();
            //}

            //return UserDatabase.GetUserFromToken(token);
            string token = nancyContext.Request.Headers.Authorization;
            if (!string.IsNullOrEmpty(token))
            {
                return UserMapper.GetUserFromAccessToken(token);
            }
            else
            {
                return null;
            }
        });

        StatelessAuthentication.Enable(pipelines, statelessAuthConfiguration);

        //Jsonp.Disable(pipelines);//默认开启的Jsonp
    }

    protected override void RequestStartup(TinyIoCContainer requestContainer, IPipelines pipelines, NancyContext context)
    {
        base.RequestStartup(requestContainer, pipelines, context);

        var maxAge = 21600;
        //CORS Enable
        pipelines.AfterRequest.AddItemToEndOfPipeline((ctx) =>
        {
            if (ctx.Request.Headers.Keys.Contains("Origin"))
            {
                var origins = "" + string.Join(" ", ctx.Request.Headers["Origin"]);
                ctx.Response.Headers["Access-Control-Allow-Origin"] = origins;
                ctx.Response.WithHeader("Access-Control-Allow-Credentials", "true")
                    .WithHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS")
                    .WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type,authorization")
                    .WithHeader("Access-Control-Max-Age", maxAge.ToString());
            }
        });
    }
}

public class PageNotFoundHandler : DefaultViewRenderer, IStatusCodeHandler
{
    public PageNotFoundHandler(IViewFactory factory) : base(factory)
    {
    }

    public bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context)
    {
        return statusCode == HttpStatusCode.NotFound||
            statusCode == HttpStatusCode.Unauthorized;
    }

    public void Handle(HttpStatusCode statusCode, NancyContext context)
    {
        dynamic model = new ExpandoObject();
        model.Url = context.Request.Url;
        Response response = null;
        switch (statusCode)
        {
            case HttpStatusCode.NotFound:
                response = RenderView(context, "404", model);
                response.StatusCode = HttpStatusCode.NotFound;
                break;
                case HttpStatusCode.Unauthorized:
                response = RenderView(context, "401", model);
                response.StatusCode = HttpStatusCode.Unauthorized;
                break;
        }

        if (response != null)
        {
            context.Response = response;
        }
    }
}

public class CustomJsonSerializer : JsonSerializer
{
    public CustomJsonSerializer()
    {
        //this.ContractResolver = new CamelCasePropertyNamesContractResolver();//首字母小写
        this.Formatting = Formatting.Indented;
        this.DateFormatString = "yyyy-MM-dd HH:mm:ss";
        this.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
        this.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
        this.NullValueHandling = NullValueHandling.Include;
        this.ContractResolver = new NullToEmptyStringResolver();

    }
}

public class NullToEmptyStringResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return type.GetProperties().Select(property => CreateProperty(property, memberSerialization)).ToList();
    }

    private JsonProperty CreateProperty(PropertyInfo property, MemberSerialization memberSerialization)
    {
        var jsonProperty = base.CreateProperty(property, memberSerialization);

        jsonProperty.ValueProvider = new NullToEmptyStringValueProvider(jsonProperty.ValueProvider, property);

        return jsonProperty;
    }

    private class NullToEmptyStringValueProvider : IValueProvider
    {
        private readonly IValueProvider _valueProvider;

        private readonly PropertyInfo _property;

        public NullToEmptyStringValueProvider(IValueProvider valueProvider, PropertyInfo property)
        {
            _valueProvider = valueProvider;
            _property = property;
        }

        public object GetValue(object target)
        {
            var result = _valueProvider == null
                ? _property.GetValue(target)
                : _valueProvider.GetValue(target);

            if (_property.PropertyType == typeof(string) && result == null)
            {
                result = string.Empty;
            }

            return result;
        }

        public void SetValue(object target, object value)
        {
            if (_valueProvider == null)
            {
                _property.SetValue(target, value);
            }
            else
            {
                _valueProvider.SetValue(target, value);
            }
        }
    }
}

}

thecodejunkie commented 7 years ago

Here's a functioning sample using

public class Index : NancyModule
{
    public Index()
    {
        Get["/"] = _ =>
        {
            return Response.AsJson(new Model());
        };
    }
}

public class Model
{
    public string Name { get; set; }
}

public class Bootstrapper : DefaultNancyBootstrapper
{
    protected override void ConfigureApplicationContainer(TinyIoCContainer container)
    {
        base.ConfigureApplicationContainer(container);

        container.Register(new JsonSerializer
        {
            ContractResolver = new NullWithEmptyStringContractResolver()
        });
    }
}

public class NullWithEmptyStringContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (property.PropertyType == typeof(string))
        {
            property.ValueProvider = new NullToEmptyStringValueProvider(property.ValueProvider);
        }

        return property;
    }

    private class NullToEmptyStringValueProvider : IValueProvider
    {
        private readonly IValueProvider provider;

        public NullToEmptyStringValueProvider(IValueProvider provider)
        {
            this.provider = provider;
        }

        public object GetValue(object target)
        {
            return this.provider.GetValue(target) ?? string.Empty;
        }

        public void SetValue(object target, object value)
        {
            this.provider.SetValue(target, value);
        }
    }
}