【问题标题】:Validate response to filter array objects which are having no elements验证对没有元素的过滤器数组对象的响应
【发布时间】:2017-10-02 11:13:13
【问题描述】:

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

例如:我正在使用下面的方法来过滤空对象。

using Newtonsoft.Json;

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

我怎样才能使用上面的东西来过滤掉[]空数组对象?

例如:

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

【问题讨论】:

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


【解决方案1】:

您可以使用 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 自动为所有 then 生成一个 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 以获得最佳性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 2023-04-01
    • 2023-02-07
    • 1970-01-01
    相关资源
    最近更新 更多