使用JSchemaGenerator生成json模式时添加options属性



我正在使用Newtonsoft的Json.NET架构生成器,我想生成一个Json架构并隐藏几个字段。我知道使用options属性是可能的。下面是一个使用此属性的示例模式。

{
"title": "Person",
"type": "object",
"properties": {
"name": {
"type": "string",
"options": { "hidden": true },
"description": "First and Last name",
"minLength": 4,
"default": "Jeremy Dorn"
}
}
} 

我有一个类,它是模式的基础,我决定在生成模式期间对要隐藏的属性使用自定义属性。然后使用自定义GenerationProvider,我想检查字段是否具有属性,如果具有,则添加"options": { "hidden": true },位。

问题是JSchema类既没有Hidden属性(就像以前的JsonSchema类一样(,也没有Options属性。

注意:我不想使用[JsonIgnore],因为我需要在某些地方序列化这些属性,但我只希望在创建模式时隐藏它们。

有什么想法可以实现吗?

关键字"options": { "hidden": true }甚至"hidden": true似乎不在当前JSONSchema规范或任何早期版本的验证关键字中。唯一看起来相关的关键字是readOnlywriteOnly。来自文档:

草案7新增布尔关键字readOnlywriteOnly通常用于API上下文。readOnly表示不应修改某个值。它可以用于指示改变值的PUT请求将导致400 Bad Request响应。writeOnly表示可以设置一个值,但将保持隐藏。In可用于指示您可以使用PUT请求设置一个值,但在使用GET请求检索该记录时不会包含该值。

{
"title": "Match anything",
"description": "This is a schema that matches anything.",
"default": "Default value",
"examples": [
"Anything",
4035
],
"readOnly": true,
"writeOnly": false 
}

因此,"options": { "hidden": true }似乎是JSON模式标准的某种自定义或第三方扩展。Json.NET架构通过JSchema.ExtensionData属性支持此类自定义验证关键字。要在自动模式生成期间在此扩展数据中设置隐藏选项,请定义以下JSchemaGenerationProvider:

[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class HiddenAttribute : System.Attribute
{
}
public class HiddenOptionProvider : CustomizedProviderBase
{
public override JSchema GetSchema(JSchemaTypeGenerationContext context)
{
var schema = base.GetSchema(context);
// Get the JsonObjectContract for this type.
var contract = (JsonObjectContract)context.Generator.ContractResolver.ResolveContract(context.ObjectType);
foreach (var propertySchema in schema.Properties)
{
// Find the corresponding JsonProperty from the contract resolver.
var jProperty = contract.Properties[propertySchema.Key];
// Check to see if the member has HiddenAttribute set.
if (jProperty.AttributeProvider.GetAttributes(typeof(HiddenAttribute), true).Any())
// If so add "options": { "hidden": true }
propertySchema.Value.ExtensionData["options"] = new JObject(new JProperty("hidden", true));
}

return schema;
}
public override bool CanGenerateSchema(JSchemaTypeGenerationContext context) =>
base.CanGenerateSchema(context) && context.Generator.ContractResolver.ResolveContract(context.ObjectType) is JsonObjectContract;
}
public abstract class CustomizedProviderBase : JSchemaGenerationProvider
{
// Base class that allows generation of a default schema which may then be subsequently customized. 
// Note this class contains state information and so is not thread safe.
readonly Stack<Type> currentTypes = new ();
public override JSchema GetSchema(JSchemaTypeGenerationContext context)
{
if (CanGenerateSchema(context))
{
var currentType = context.ObjectType;
try
{
currentTypes.Push(currentType);
return context.Generator.Generate(currentType);
}
finally
{
currentTypes.Pop();
}
}
else
throw new NotImplementedException();
}

public override bool CanGenerateSchema(JSchemaTypeGenerationContext context) => 
!currentTypes.TryPeek(out var t) || t != context.ObjectType;
}

然后定义您的Person类型如下:

[DisplayName("Person")]
public class Person
{
[JsonProperty("name", Required = Required.DisallowNull)]
[DefaultValue("Jeremy Dorn"), MinLength(4), System.ComponentModel.DescriptionAttribute("First and Last name")]
[Hidden] // Your custom attribute
public string Name { get; set; } = "Jeremy Dorn";
}

并生成如下模式:

var generator = new JSchemaGenerator();
generator.GenerationProviders.Add(new HiddenOptionProvider());
var schema = generator.Generate(typeof(Person));            

根据需要,您将获得以下模式:

{
"title": "Person",
"type": "object",
"properties": {
"name": {
"description": "First and Last name",
"options": {
"hidden": true
},
"type": "string",
"default": "Jeremy Dorn",
"minLength": 4
}
}
}

在这里演示小提琴。

我不知道Newtonsoft,但JsonSchema.Net.Generation可以通过内置的[JsonIgnore]属性轻松做到这一点。这个模式库是建立在System.Text.Json之上的。

我显然需要证明这是专门支持的,但以下是库的其余文档。我确实有一个测试(第168行(确认它有效。

这是对@dbc的一个相当长的回复,它帮助我完成了这项工作。由于我为其创建模式的类非常大,并且其中包含不同类型的负载,我无法使此解决方案发挥作用。我在这里注意到的很少。我正在使用"Newtonsoft.Json.Schema" Version="3.0.14",在自定义提供商内部使用

var contract = (JsonObjectContract)context.Generator.ContractResolver.ResolveContract(context.ObjectType);

正在引发异常,因为由于context.Generator.ContractResolver.ResolveContract(context.ObjectType);正在返回JsonPrimitiveContract,因此无法转换为JsonObjectContrac。我不想花太多时间来解决这个问题,所以我继续编写代码,并尝试执行dbc在这段代码中所做的事情:

foreach (var propertySchema in schema.Properties)
{
// Find the corresponding JsonProperty from the contract resolver.
var jProperty = contract.Properties[propertySchema.Key];
// Check to see if the member has HiddenAttribute set.
if (jProperty.AttributeProvider.GetAttributes(typeof(HiddenAttribute), true).Any())
// If so add "options": { "hidden": true }
propertySchema.Value.ExtensionData["options"] = new JObject(new JProperty("hidden", true));
}

在大多数情况下,由于schema.Properties是空的,因此出现了另一个问题。我注意到,这个自定义提供程序不仅被调用一次,而且它被调用了一次,用于架构的基类的每一个属性,以及基类本身(基本上这个提供程序被调用了数百次(。因此,我最终只是在类中创建了模式,然后应用了ExtensionData。因此,我的提供者除了做一些其他逻辑外,还有CheckIsHidden(JSchemaTypeGenerationContext context, JSchema schema)方法来完成这项工作:

public static void CheckIsHidden(JSchemaTypeGenerationContext context, JSchema schema)
{
var hiddenAttribute = context.MemberProperty?.AttributeProvider?.GetAttributes(true)
?.FirstOrDefault(a => a.GetType().Name == nameof(JsonConfigIgnoreAttribute));
if (hiddenAttribute != null)
{
schema.ExtensionData["options"] = new JObject(new JProperty("hidden", true));
}
}

这条评论无疑帮助我实现了这一点,因为我主要是在寻找这条特定的线路schema.ExtensionData["options"] = new JObject(new JProperty("hidden", true));。非常感谢!

相关内容

  • 没有找到相关文章

最新更新