gpt4 book ai didi

c# - newtonsoft json 模式反序列化 ValidationError

转载 作者:行者123 更新时间:2023-12-04 15:05:48 27 4
gpt4 key购买 nike

使用 Newtonsoft.Json.Schema。似乎 Newtonsoft 的 JsonConvert 无法反序列化它自己的 ValidationError .

具体来说,以下将失败:

var dataFile = System.IO.File.ReadlAllText("data.json");
var schemaFile = System.IO.File.ReadlAllText("schema.json");
... // parsing the files
model.IsValid(schema, out IList<Newtonsoft.Json.Schema.ValidationError> ve);
var errors = ve as List<Newtonsoft.Json.Schema.ValidationError>; // <-- this may be a problem?
var resultStr = Newtonsoft.Json.JsonConvert.SerializeObject(errors); // <-- this works as expected, though
var ReSerializedResult = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Newtonsoft.Json.Schema.ValidationError>>(resultStr);

我在序列化后得到了一个有效的 resultStr 字符串,类似于

[{\"Message\": \"String 'key' does not match regex pattern ... \", ... }] 

再次反序列化后,我得到了一个类型为 Newtonsoft.Json.Schema.ValidationError 的项目的数组(验证结果有一个错误所以没关系),但它的所有字段都是默认值。

有没有其他人也遇到过这个?有没有办法使用 Json.NET 往返 ValidationError,或者我应该在其 GitHub issues page 上打开一个问题?

最佳答案

您在这里遇到了几个问题。

首先,ValidationError是公开不可变的(即所有属性都缺少公共(public) setter )并且只有一个构造函数,即 nonparameterized .因此第三方应用程序(包括 Json.NET 本身)无法填充此类型的实例。

reference source然而,显示大多数属性都有私有(private) setter 。因此,应该可以从 this answer 中调整 SisoJsonDefaultContractResolver通过 daniel Private setters in Json.Net 到往返 ValidationError。首先定义:

public class SelectedPrivateSettersContractResolver : DefaultContractResolver
{
HashSet<Type> privateSetterTypes { get; } = new ();

public SelectedPrivateSettersContractResolver(params Type [] types) : this((IEnumerable<Type>)types) { }

public SelectedPrivateSettersContractResolver(IEnumerable<Type> types) =>
privateSetterTypes.UnionWith(types ?? throw new ArgumentNullException());

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

if (!prop.Ignored && prop.Readable && !prop.Writable)
if (privateSetterTypes.Contains(prop.DeclaringType))
if (member is PropertyInfo property)
prop.Writable = property.GetSetMethod(true) != null;

return prop;
}
}

现在您可以:

var settings = new JsonSerializerSettings
{
ContractResolver = new SelectedPrivateSettersContractResolver(typeof(ValidationError)),
};
var resultStr = JsonConvert.SerializeObject(errors, Formatting.Indented, settings);
var ReSerializedResult = JsonConvert.DeserializeObject<List<ValidationError>>(resultStr, settings);

演示 fiddle #1 here .

但是,虽然这允许大多数 ValidationError 属性成功往返,但 Message 属性却不能。第二个问题的出现是因为,在当前的实现中,Message没有 setter/getter 。相反,它返回按需计算的字段 _message 的值。因此,有必要强制序列化 _message(以及相关字段 _extendedMessage)。 custom contract resolver继承自 SelectedPrivateSettersContractResolver 可用于执行此操作:

public class ValidationErrorsContractResolver : SelectedPrivateSettersContractResolver
{
static readonly string [] validationErrorFields = new [] { "_message", "_extendedMessage" };

public ValidationErrorsContractResolver() : base(typeof(ValidationError)) { }

protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var list = base.GetSerializableMembers(objectType);
if (typeof(ValidationError).IsAssignableFrom(objectType))
{
foreach (var fieldName in validationErrorFields)
if (objectType.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic) is var f && f != null)
list.Add(f);
}
return list;
}

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
if (prop.DeclaringType == typeof(ValidationError))
{
if (validationErrorFields.Contains(prop.UnderlyingName))
{
prop.Ignored = false;
prop.Readable = prop.Writable = true;
}
}
return prop;
}

protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (typeof(ValidationError).IsAssignableFrom(objectType))
{
// Ensure _message and _extendedMessage are calculated.
contract.OnSerializingCallbacks.Add((o, c) => { var m = ((ValidationError)o).Message; });
}
return contract;
}
}

现在如果你按如下方式往返:

var settings = new JsonSerializerSettings
{
ContractResolver = new ValidationErrorsContractResolver(),
};
var resultStr = JsonConvert.SerializeObject(errors, Formatting.Indented, settings);
var ReSerializedResult = JsonConvert.DeserializeObject<List<ValidationError>>(resultStr, settings);

消息成功往返。演示 fiddle #2 here .

注意事项:

  • 以这种方式使用反射强制序列化私有(private)字段是脆弱的,如果 Newtonsoft 更改 ValidationError 的实现,可能很容易崩溃。

  • 您可能想要缓存和重用 ValidationErrorsContractResolver 以获得最佳性能,如 the documentation 中所建议的那样.

  • 您可能会注意到第三个问题,即 Schema ValidationError 的属性未被序列化或反序列化,因为 Newtonsoft 已在 source code 中用 [JsonIgnore] 明确标记它.我怀疑他们这样做是为了防止序列化的 JSON 变得过于臃肿。如果您希望 Schema 进行往返,您可以在 ValidationErrorsContractResolver.CreateProperty() 中强制将其序列化,如下所示:

     protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
    var prop = base.CreateProperty(member, memberSerialization);
    if (prop.DeclaringType == typeof(ValidationError))
    {
    if (validationErrorFields.Contains(prop.UnderlyingName)
    || prop.UnderlyingName == "Schema")
    {
    prop.Ignored = false;
    prop.Readable = prop.Writable = true;
    }
    }
    return prop;
    }

    但是,如果这样做,您的 JSON 将变得更加臃肿,并且如果您序列化多个验证错误,JSchema Schema 值将在反序列化过程中重复,因为它未通过引用保存。

    演示 fiddle #3 here .

关于c# - newtonsoft json 模式反序列化 ValidationError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66136063/

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