当前位置: 首页 > 面试题库 >

JSON.Net自定义合同序列化和集合

汪晟睿
2023-03-14
问题内容

我试图创建一个IContractResolver来简化WebApi项目上的安全处理。

我正在尝试:

我想基于一组动态条件来序列化某些对象/属性(例如,调用端点的用户角色)。

因此,我实现了一个自定义属性,该属性在Interface的CreateProperty重写中进行了检查,并将ShouldSerialize函数设置为我自己的逻辑。

我现在的问题是,是否可以有条件地序列化某个列表中的完整对象?与其在预处理步骤中过滤列表(如果我更改对象,这很容易出错),我希望它由当前的ContractResolver递归处理。

在某种程度上,我试图得到这样的东西:

override void CreateObject(JSONObject ob){
if ( ob.DeclaringType == MyType)
{
   ob.ShouldSerialize = instance => {[...] }; //Custom Logic
}
}

我是否缺少覆盖,这根本不可能吗?有没有一种更好的方法可以真正做到这一点,而无需我“预先解析”我的所有价值观?


问题答案:

这不是开箱即用的。如果您检查源,JsonSerializerInternalWriter.SerializeList()您将发现没有逻辑可以基于某些过滤器跳过收集条目。

但是,Json.NET确实具有强大的异常处理功能。如果在开始序列化对象时引发了异常,则在[OnError]回调中将其捕获并吞下:

  • 如果写入数组条目,则 跳过 该数组条目(您想要的行为)。
  • 如果编写根对象,则不会捕获异常(可能是错误?)
  • 否则null被写入。

因此,实现所需功能的一种可能性是从JsonContract.OnSerializingCallbacks自定义合同解析器添加的人工回调中引发异常,然后使用添加的处理程序捕获并吞下该异常JsonContract.OnErrorCallbacks。与已经进行的对属性值的过滤结合使用时,此方法的优点在于,即使秘密对象是根对象或包含在字典,动态对象或多维数组中,也无法对它进行序列化。这种方法不会干扰PreserveReferencesHandling.Arrays

一个执行此操作的合同解析器如下:

sealed class JsonSkipObjectException : JsonException
{
}

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    readonly Predicate<object> shouldSerialize;
    readonly SerializationCallback serializationCallback;
    readonly SerializationErrorCallback onErrorCallback;

    public ShouldSerializeContractResolver(Predicate<object> shouldSerialize)
        : base()
    {
        this.shouldSerialize = shouldSerialize;
        this.serializationCallback = (o, context) =>
            {
                if (shouldSerialize != null && !this.shouldSerialize(o))
                    throw new JsonSkipObjectException();
            };
        this.onErrorCallback = (o, context, errorContext) =>
            {
                if (errorContext.Error is JsonSkipObjectException)
                {
                    errorContext.Handled = true;
                }
            };
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (shouldSerialize != null)
        {
            if (property.Readable)
            {
                var oldShouldSerialize = property.ShouldSerialize;
                property.ShouldSerialize = (o) =>
                    {
                        if (oldShouldSerialize != null && !oldShouldSerialize(o))
                            return false;
                        var value = property.ValueProvider.GetValue(o);
                        if (!this.shouldSerialize(value))
                            return false;
                        return true;
                    };
            }
        }
        return property;
    }

    protected override JsonContract CreateContract(Type objectType)
    {
        var contract = base.CreateContract(objectType);
        contract.OnSerializingCallbacks.Add(serializationCallback);
        contract.OnErrorCallbacks.Add(onErrorCallback);
        return contract;
    }
}

那么一种可能的用途是:

public interface IConditionalSerialization
{
    bool ShouldSerialize();
}

public class ConditionalSerializationObject : IConditionalSerialization
{
    public bool IsSecret { get; set; }

    public string SecretProperty { get { return "should not see me"; } }

    // Ensure "normal" conditional property serialization is not broken
    public bool ShouldSerializeSecretProperty()
    {
        return false;
    }

    #region IConditionalSerialization Members

    bool IConditionalSerialization.ShouldSerialize()
    {
        return !IsSecret;
    }

    #endregion
}

public class TestClass
{
    public static void Test()
    {
        Predicate<object> filter = (o) => 
            {
                var conditional = o as IConditionalSerialization;
                return conditional == null || conditional.ShouldSerialize();
            };
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new ShouldSerializeContractResolver(filter),
        };

        var ok = new ConditionalSerializationObject { IsSecret = false };
        var notOk = new ConditionalSerializationObject { IsSecret = true };

        Test(ok, settings);
        Test(new { Public = ok, Private = notOk }, settings);
        Test(new [] { ok, notOk, ok, notOk }, settings);
        Test(new[,] {{ ok, notOk, ok, notOk }}, settings);
        Test(new { Array = new[,] { { ok, notOk, ok, notOk } } }, settings);
        try
        {
            Test(notOk, settings);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception thrown and not caught serializing root object " + notOk.GetType());
            Console.WriteLine(ex);
        }
    }

    static void Test<T>(T value, JsonSerializerSettings settings)
    {
        Console.WriteLine("Unfiltered object: ");
        Console.WriteLine(JToken.FromObject(value));

        var serializer = JsonSerializer.CreateDefault(settings);
        var token = JToken.FromObject(value, serializer);
        Console.WriteLine("Filtered object: ");
        Console.WriteLine(token);
        if (!token.SelectTokens("..IsSecret").All(t => JToken.DeepEquals(t, (JValue)false)))
        {
            throw new InvalidOperationException("token.SelectTokens(\"..IsSecret\").All(t => JToken.DeepEquals(t, (JValue)true))");
        }
        if (token.SelectTokens("..SecretProperty").Any())
        {
            throw new InvalidOperationException("token.SelectTokens(\"..SecretProperty\").Any()");
        }
        Console.WriteLine("Secret objects and properties were successfully filtered.");
        Console.WriteLine("");
    }
}

原型小提琴。

请注意,引发和捕获大量异常可能会对性能产生影响。您将需要分析您的Web应用程序,以确定是否存在问题。您还需要确定您的Web服务在尝试序列化“秘密”根对象时是应返回异常还是执行其他操作。



 类似资料:
  • 问题内容: 我有一堂课 我想将下面的JSON数据反序列化到上面的类/对象中 我的想法是在JSON中是一个对象,但我只想获取(在JSON中)在反序列化期间将像在类中那样传递。 如何使用Json.NET实现该目标? 我相信我可以使用CustomJsonConverter完成它。但是我很困惑。docs中的示例仅用于,但不适用。 问题答案: 我只是使用上面在问题中提到的方法解决了我的问题。在我完整的代码下

  • 问题内容: 我有一个需要序列化为XML的对象,其中包含以下字段: XStream可以很好地序列化它(在使用一些别名之后),如下所示: 就目前而言还可以,但是我希望能够将元素重命名为。从XStream站点上的别名文档中,我看不到一种明显的方法。我是否缺少明显的东西? 问题答案: 我建议将更改为,其中Tag是本质上仅包含字符串的域对象。然后你说: 您将得到您想要的。这样可以避免滚动自己的Convert

  • easyopen序列化使用fastjson处理json,xstream处理xml。现在我们来自定义实现一个json处理: 新建一个类JsonFormatter,实现ResultSerializer接口 public class JsonFormatter implements ResultSerializer { @Override public String serialize(

  • 问题内容: 我有一个对象,其中包含一些要序列化的不可序列化字段。它们来自我无法更改的单独API,因此使它们可序列化不是一种选择。主要问题是Location类。它包含我需要的四个可以序列化的东西,所有整数。如何使用read / writeObject创建可以执行以下操作的自定义序列化方法: 我怎样才能做到这一点? 问题答案: Java支持自定义序列化。阅读“自定义默认协议”部分。 去引用: 但是,有

  • 我正在开发一个API,使用ASP公开一些数据。NET Web API。 在其中一个API中,客户端希望我们以格式公开日期。我不想为此更改全局设置(例如,),因为它非常特定于此客户端。我在为多个客户开发的解决方案中做到了这一点。 我能想到的一个解决方案是创建一个自定义的,然后将其放入我需要进行自定义格式化的属性中 例如。 只是想知道是否还有其他简单的方法。

  • 本文向大家介绍C#使用Json.Net进行序列化和反序列化及定制化,包括了C#使用Json.Net进行序列化和反序列化及定制化的使用技巧和注意事项,需要的朋友参考一下 序列化(Serialize)是将对象转换成字节流,并将其用于存储或传输的过程,主要用途是保存对象的状态,以便在需要时重新创建该对象;反序列化(Deserialize)则是将上面的字节流转换为相应对象的过程;在.Net阵营中,Json