json序列化程序NullValueHandling不使用datamember属性

时间:2015-09-25 17:04:30

标签: c# json json.net asp.net-web-api

在我的Web api项目中,现在我正在跳过空值。因此,返回json忽略空值并打印属性。

在Global.asax文件中:

//manage the null in the response
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;

但是,我想用" - "替换空值。但是,我不想为每个属性使用数据成员属性......

[DefaultValue("-")]. 

我的项目中有10个以上的课程...所以,这不是最优雅的解决方案。

我希望这是一个简单的解决方案并适用于任何转换,就像Global.asax中的空值一样

实施例

public class User
{
    public string user { get; set; }

    public string name { get; set; }

    public string dni { get; set; }
}

当存在所有数据时,我的服务返回

{
  "user": "usertest",
  "name": "nametest",
  "dni": "123456789"
}

但是,当dni存在时,请回复此

{
  "user": "usertest",
  "name": "nametest",
  "dni": ""
}

所以,我想回复如下

{
  "user": "usertest",
  "name": "nametest",
  "dni": "-"
}

3 个答案:

答案 0 :(得分:1)

您可以使用自定义IContractResolver处理此问题。解析器可以将IValueProvider应用于每个字符串属性,然后处理空值到-的转换(如果要反序列化相同的JSON,则返回)。

以下是解析器所需的代码:

public class NullStringReplacementResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);

        // Attach a NullStringReplacementProvider instance to each string property
        foreach (JsonProperty prop in props.Where(p => p.PropertyType == typeof(string)))
        {
            PropertyInfo pi = type.GetProperty(prop.UnderlyingName);
            if (pi != null)
            {
                prop.ValueProvider = new NullStringReplacementProvider(pi);
            }
        }

        return props;
    }

    protected class NullStringReplacementProvider : IValueProvider
    {
        PropertyInfo targetProperty;

        public NullStringReplacementProvider(PropertyInfo targetProperty)
        {
            this.targetProperty = targetProperty;
        }

        // GetValue is called by Json.Net during serialization.
        // The target parameter has the object from which to read the string;
        // the return value is the string that gets written to the JSON
        public object GetValue(object target)
        {
            // if the value of the target property is null, replace it with "-"
            string s = (string)targetProperty.GetValue(target);
            return (s == null ? "-" : s);
        }

        // SetValue gets called by Json.Net during deserialization.
        // The value parameter has the original value read from the JSON;
        // target is the object on which to set the value.
        public void SetValue(object target, object value)
        {
            // if the value in the JSON is "-" replace it with null
            string s = (string)value;
            targetProperty.SetValue(target, s == "-" ? null : s);
        }
    }
}

要使用自定义解析程序,您需要将其添加到序列化和反序列化期间使用的JsonSerializerSettings。如果您使用的是ASP.NET Web API,可以通过在Application_Start中的Global.asax.cs方法中添加以下内容来实现:

var config = GlobalConfiguration.Configuration;
var settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new NullStringReplacementResolver();

小提琴:https://dotnetfiddle.net/FVA3p8

答案 1 :(得分:0)

我不建议这样做。问题是,如果你的字符串值属性恰好与“空值”"-"具有相同的值,那么在序列化时你将丢失信息,即属性是否实际为空。

话虽如此,您可以使用ContractResolver通过覆盖JsonProperty.ValueProvider字符串值属性来执行此操作:

public class StringRemappingContractResolver : DefaultContractResolver
{
    static readonly object NullValue;

    static StringRemappingContractResolver() { NullValue = new object(); }

    readonly Dictionary<object, object> map;
    readonly ILookup<object, object> reverseMap;

    public StringRemappingContractResolver() : this(new KeyValuePair<object, object> [] { new KeyValuePair<object, object>(null, "-")}) 
    {
    }

    public StringRemappingContractResolver(IEnumerable<KeyValuePair<object, object>> map)
    {
        if (map == null)
            throw new ArgumentNullException("map");
        this.map = map.ToDictionary(p => p.Key ?? NullValue, p => p.Value);
        this.reverseMap = map.ToLookup(p => p.Value ?? NullValue, p => p.Key);
    }

    class StringRemappingValueProvider : IValueProvider
    {
        readonly IValueProvider baseProvider;
        readonly Dictionary<object, object> map;
        readonly ILookup<object, object> reverseMap;

        public StringRemappingValueProvider(IValueProvider baseProvider, Dictionary<object, object> map, ILookup<object, object> reverseMap)
        {
            if (baseProvider == null)
                throw new ArgumentNullException("baseProvider");
            this.baseProvider = baseProvider;
            this.map = map;
            this.reverseMap = reverseMap;
        }

        #region IValueProvider Members

        public object GetValue(object target)
        {
            var value = baseProvider.GetValue(target);
            object mapped;
            if (map.TryGetValue(value ?? NullValue, out mapped))
                value = mapped;
            return value;
        }

        public void SetValue(object target, object value)
        {
            foreach (var mapped in reverseMap[value ?? NullValue])
            {
                // Use the first.
                value = mapped;
                break;
            }
            baseProvider.SetValue(target, value);
        }

        #endregion
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (property.PropertyType == typeof(string))
        {
            property.ValueProvider = new StringRemappingValueProvider(property.ValueProvider, map, reverseMap);
        }
        return property;
    }
}

然后使用它:

public class User
{
    public string user { get; set; }

    public string name { get; set; }

    public string dni { get; set; }
}

public class TestClass
{
    public static void Test()
    {
        var settings = new JsonSerializerSettings { ContractResolver = new StringRemappingContractResolver() };

        var user = new User { user = "usertest", name = "nametest", dni = null };
        var json = JsonConvert.SerializeObject(user, settings);
        Debug.WriteLine(json); // Prints {"user":"usertest","name":"nametest","dni":"-"}
        Debug.Assert(JToken.DeepEquals(JToken.Parse(json), JToken.Parse(@"{'user':'usertest','name':'nametest','dni':'-'}"))); // No assert
        var userBack = JsonConvert.DeserializeObject<User>(json, settings);
        Debug.Assert(user.dni == userBack.dni && user.name == userBack.name && user.user == userBack.user); // No assert
    }
}

答案 2 :(得分:-1)

您可以在globals.asax中注册转换器。你会添加这样的东西:

config.Formatters.JsonFormatter.SerializerSettings.Converters.add(new NullHandlerConverter());

其中,NullHandlerConverter是您需要编写的自定义转换器。

修改:Brian是对的。转换器无法使用,因为无法在{{::name}} 值上调用它们。