gpt4 book ai didi

c# - JSON.Net 自定义合约序列化和集合

转载 作者:行者123 更新时间:2023-11-30 23:24:03 25 4
gpt4 key购买 nike

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

我正在尝试:

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

所以我实现了一个自定义属性,该属性在接口(interface)的 CreateProperty 重写中被选中,并将 ShouldSerialize 函数设置为我自己的逻辑。

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

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

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

我是否缺少覆盖,这根本不可能吗?有没有更好的方法来实际执行此操作,而无需“预解析”我所有的值?

最佳答案

这不是开箱即用的。如果您检查 JsonSerializerInternalWriter.SerializeList() 的来源您会看到没有逻辑可以根据某些过滤器跳过集合条目。

但是,Json.NET 确实有 robust exception handling .如果在开始序列化对象时抛出异常,则在 [OnError] 中捕获并吞噬回调:

  • 如果写入数组条目,则跳过数组条目(您希望的行为)。
  • 如果写根对象,异常没有被捕获(可能是bug?)
  • 否则写入null

因此,实现所需功能的一种可能性是从添加到 JsonContract.OnSerializingCallbacks 的人工回调中抛出异常。通过您的自定义契约(Contract)解析器,然后使用添加到 JsonContract.OnErrorCallbacks 的处理程序捕获并吞下异常.当与您已经在做的属性值过滤相结合时,这种方法的优点是保证 secret 对象不能被序列化,即使它是根对象或包含在字典、动态对象或多维数组中也是如此。这种方法不会干扰 PreserveReferencesHandling.Arrays .

执行此操作的一个契约(Contract)解析器如下:

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("");
}
}

原型(prototype) fiddle .

请注意,抛出和捕获大量异常可能会对性能产生影响。参见 How expensive are exceptions in C#? .您将需要分析您的 Web 应用程序以确定这是否是一个问题。您还需要决定您的 Web 服务在尝试序列化“ secret ”根对象时是否应该返回异常,或者做一些不同的事情。

关于c# - JSON.Net 自定义合约序列化和集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38038793/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com