using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.ComponentModel;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Schema.Generation;
internal class CustomNameRuleValidator : JsonValidator
const string CustomRulePropertyName = "customNameRule";
public override bool CanValidate(JSchema schema) =>
schema.ExtensionData.ContainsKey(CustomRulePropertyName);
public override void Validate(JToken value, JsonValidatorContext context)
var customRuleValue = (string)context.Schema.ExtensionData[CustomRulePropertyName];
if (value.Type != JTokenType.String || !((string)value).StartsWith(customRuleValue))
context.RaiseError($"Property value {value.ToString(Formatting.None)} does not begin with {customRuleValue}");
internal class CustomValueTypeRuleValidator : JsonValidator
const string CustomRulePropertyName = "customValueTypeRule";
readonly JSchema customValueSchema;
readonly string expectedValueType;
public CustomValueTypeRuleValidator(string expectedValueType) =>
(this.expectedValueType, this.customValueSchema) = (expectedValueType, JSchema.Parse($"{{ 'type': '{expectedValueType}' }}"));
public override bool CanValidate(JSchema schema) =>
schema.ExtensionData.ContainsKey(CustomRulePropertyName);
public override void Validate(JToken value, JsonValidatorContext context)
if (value is JObject obj)
var valueToken = obj["value"];
if (valueToken == null || !valueToken.IsValid(customValueSchema))
context.RaiseError($"Type mismatch, expected {expectedValueType}");
public static void Test()
var customNameRuleSchemaJson =
"description":"A configuration setting value",
"customNameRule" : "Setting",
"required": ["name", "value"],
Test(customNameRuleSchemaJson);
"description":"A configuration setting value",
"required": ["name", "value"],
"customValueTypeRule" : true,
public static void Test(string schemaJson)
var settingJson = @"{ 'name': 'AA', 'value': 'XYZ' }";
var setting = JToken.Parse(settingJson);
var readerSettings = new JSchemaReaderSettings
Validators = [new CustomNameRuleValidator(), new CustomValueTypeRuleValidator("number")]
var schema = JSchema.Parse(schemaJson, readerSettings);
var isValid = setting.IsValid(schema, out IList<string> errors);
Console.WriteLine(setting.ToString(Formatting.None));
Console.WriteLine(schema);
"IsValid={0}, Errors:\r\n{1}",
errors != null ? string.Join("\r\n", errors) : string.Empty);
public static void Main()
Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Json.NET Schema version: " + typeof(Newtonsoft.Json.Schema.JSchema).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
internal class ValueTypeValidator : JsonValidator
public override bool CanValidate(JSchema schema)
public override void Validate(JToken value, JsonValidatorContext context)
if (value.Type == JTokenType.Object)
var obj = (JObject)value;
var name = (string)obj["name"];
var type = GetExpectedValueType(name);
var schema = JSchema.Parse($@"{{ 'type': '{type}' }}");
var valueToken = obj["value"];
if (!valueToken.IsValid(schema))
context.RaiseError($"Type mismatch, expected {type}");
private string GetExpectedValueType(string name)