验证对没有元素的过滤器数组对象的响应

时间:2017-10-02 11:13:14

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

如何从ASP.NET Web API模型中过滤掉具有0个元素的数组对象。

Ex:我正在使用以下方法来过滤空对象。

using Newtonsoft.Json;

public string FaxWork { get; set; }
[JsonProperty(PropertyName = "phoneWork", NullValueHandling = NullValueHandling.Ignore)]

如何使用上面的内容来过滤掉[]空数组对象?

例如:

"postalAddress": [],
"electronicAddress": []

1 个答案:

答案 0 :(得分:2)

您可以使用Json.NET的conditional property serialization功能完成此操作。

如果您只想在数组值为空时忽略单个成员,请在您不希望序列化时返回ShouldSerialize{PropertyName}()的类中添加false方法,例如:

public class RootObject
{
    public string[] PostalAddress { get; set; }

    public bool ShouldSerializePostalAddress() { return PostalAddress != null && PostalAddress.Length > 0; }
}

如果您需要为许多不同类型的许多不同的集合值成员执行此操作,您可以创建一个custom contract resolver,为所有这些成员自动生成ShouldSerialize谓词:

public class SkipEmptyCollectionsContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization)
            .AddShouldSerializeEmptyCollections(this);
        return property;
    }
}

public static class JsonPropertyExtensions
{
    public static JsonProperty AddShouldSerializeEmptyCollections(this JsonProperty property, IContractResolver resolver)
    {
        if (property == null)
            throw new ArgumentNullException();
        if ((typeof(IEnumerable).IsAssignableFrom(property.PropertyType) || property.PropertyType.IsAssignableFrom(typeof(IEnumerable)))
            && property.PropertyType != typeof(string)
            && property.Readable)
        {
            Predicate<object> shouldSerialize = (parent) =>
            {
                var value = property.ValueProvider.GetValue(parent);
                if (value == null || value is string)
                    return true; // null properties are filtered by the NullValueHandling setting.
                var contract = resolver.ResolveContract(value.GetType());
                if (contract is JsonArrayContract)
                {
                    return (value as IEnumerable).Any();
                }
                return true;
            };
            var oldShouldSerialize = property.ShouldSerialize;
            if (oldShouldSerialize == null)
                property.ShouldSerialize = shouldSerialize;
            else
                property.ShouldSerialize = (o) => shouldSerialize(o) && oldShouldSerialize(o);
        }

        return property;
    }
}

public static class EnumerableExtensions
{
    public static bool Any(this IEnumerable enumerable)
    {
        if (enumerable == null)
            return false;
        if (enumerable is ICollection)
        {
            return ((ICollection)enumerable).Count > 0;
        }
        var enumerator = enumerable.GetEnumerator();
        using (enumerator as IDisposable)
        {
            return enumerator.MoveNext();
        }
    }
}

然后使用JsonSerializerSettings进行序列化,如下所示,这也可以启用名称的驼峰外壳:

var settings = new JsonSerializerSettings
{
    ContractResolver = new SkipEmptyCollectionsContractResolver { NamingStrategy = new CamelCaseNamingStrategy() },
    NullValueHandling = NullValueHandling.Ignore,
};

如果您想使用属性有条件地过滤掉空集合,可以使用以下合约解析程序和属性来执行此操作:

public enum EmptyArrayHandling
{
    Include = 0,
    Ignore = 1,
}

[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class JsonPropertyExtensionsAttribute : System.Attribute
{
    public EmptyArrayHandling EmptyArrayHandling { get; set; }
}

public class ConditionallySkipEmptyCollectionsContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        var attr = property.AttributeProvider.GetAttributes(typeof(JsonPropertyExtensionsAttribute), false).Cast<JsonPropertyExtensionsAttribute>().FirstOrDefault();
        if (attr != null && attr.EmptyArrayHandling == EmptyArrayHandling.Ignore)
            property = property.AddShouldSerializeEmptyCollections(this);
        return property;
    }
}

然后按以下方式向您的会员申请:

public class RootObject
{
    [JsonPropertyExtensions(EmptyArrayHandling = EmptyArrayHandling.Ignore)]
    public string[] PostalAddress { get; set; }
}

请注意,如果您的“集合”实际上是一个复杂的LINQ查询,ShouldSerialize方法将必须枚举查询的第一个元素以查看它是否为空,这可能会导致性能不佳,因为查询将被评估两次。为避免这种情况,您可以在序列化之前将整个查询评估为列表。

您可能需要cache the contract resolver才能获得最佳效果。