Creating a schema using StringEnumConverter
Is it possible to create a schema with Json.NET that outputs enum values as strings rather than integers? I noticed that someone was unwrapping the code to do this, but was wondering if there was another way to do this, or if there were any plans to do this.
EDIT
To be clear, I'm trying to use this to create a schematic:
var schemaGenerator = new JsonSchemaGenerator();
var schema = schemaGenerator.Generate(typeof(Class1));
return schema.ToString();
source to share
Install the package Newtonsoft.Json.Schema
through the NuGet Package Manager, then you can output enums as strings out of the box.
Lesson data
public class Foo
{
public Options Bar { get; set; }
}
public enum Options
{
Option1,
Option2
}
The schema will be generated as follows, no need to decorate the classes / properties [JsonConverter(typeof(StringEnumConverter))]
.
JSchemaGenerator generator = new JSchemaGenerator();
generator.GenerationProviders.Add(new StringEnumGenerationProvider());
JSchema schema = generator.Generate(typeof(Foo), false);
//Console.WriteLine(schema);
source to share
I had the same problem. I wrote a hack that replaces integers with enums through reflection. Still awaiting an official fix.
var jsonSchemaString = JsonUtility.getRawJSONSchema(typeof(Class1).FullName);
Not tested in all test cases.
public class JsonUtility
{
public static string getRawJSONSchema(string jsonObjectTypeName)
{
var jsonSchemaGenerator = new JsonSchemaGenerator();
var myType = Type.GetType(jsonObjectTypeName);
var schema = jsonSchemaGenerator.Generate(myType);
schema.Title = myType.Name;
var enumToFix = new Dictionary<string, string>();
FindEnums(schema, myType, ref enumToFix);
var writer = new StringWriter();
var jsonTextWriter = new JsonTextWriter(writer);
schema.WriteTo(jsonTextWriter);
var result = writer.ToString();
ReplaceEnums(ref result, enumToFix);
return result;
}
//This is a known issue with JsonSchemaGenarator
//Stay tuned with future releases of JSON.Net package
//Enums are generator as integers
//Lets convert intergers to string here
private static void FindEnums(JsonSchema schema, Type Type, ref Dictionary<string, string> result)
{
if (schema.Properties != null)
foreach (var prop in schema.Properties)
{
if (prop.Value.Enum != null)
{
var properties = Type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var p in properties)
{
SearchProperty(p, prop.Key, ref result);
}
}
FindEnums(prop.Value, Type, ref result);
}
}
private static void SearchProperty(PropertyInfo property, string propertyName, ref Dictionary<string, string> result)
{
//IF property name is same as JSON property name
if (property.Name.Trim().ToLower() == propertyName.Trim().ToLower())
{
result.Add(property.Name, EnumToJsonArray(property.PropertyType));
return;
}
//Custom JSON property names set via attributes
foreach (CustomAttributeData customAttr in property.CustomAttributes)
{
if (customAttr.AttributeType.Name == "JsonPropertyAttribute")
{
foreach (CustomAttributeNamedArgument arg in customAttr.NamedArguments)
{
if (arg.TypedValue.Value.ToString().Trim().ToLower() == propertyName.Trim().ToLower())
{
result.Add(propertyName, EnumToJsonArray(property.PropertyType));
return;
}
}
foreach (CustomAttributeTypedArgument arg in customAttr.ConstructorArguments)
{
if (arg.Value.ToString().Trim().ToLower() == propertyName.Trim().ToLower())
{
result.Add(propertyName, EnumToJsonArray(property.PropertyType));
return;
}
}
}
}
PropertyInfo[] info = property.PropertyType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
if (info.Length > 0)
{
foreach (var item in info)
{
SearchProperty(item, propertyName, ref result);
}
}
}
private static string EnumToJsonArray(Type type)
{
if (!type.IsEnum)
throw new InvalidOperationException("enum expected");
var results =
Enum.GetValues(type).Cast<object>().Select(enumValue => enumValue.ToString())
.ToList();
return Newtonsoft.Json.JsonConvert.SerializeObject(results);
}
private static void ReplaceEnums(ref string result, Dictionary<string, string> enumToFix)
{
foreach (var item in enumToFix)
{
result = Regex.Replace(result, @"""" + item.Key + ".*?}", @"""" + item.Key + @""":{""required"":true,""type"":""string"",""enum"":" + item.Value + @"}");
}
}
}
source to share
Use the following code to serialize an enumerated string schema and make sure the value type is also configured.
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
Converters = new List<JsonConverter>()
{
new StringEnumConverter()
}
};
var gen = new JSchemaGenerator()
{
DefaultRequired = Required.Default,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var myType = typeof(ConfigRoot);
var schema = gen.Generate(myType);
var schemaObj = JObject.Parse(schema.ToString());
var enumNodes = schemaObj.SelectTokens("$..enum");
foreach (var node in enumNodes)
{
var enumTypeNode = node.Parent.Parent;
enumTypeNode["type"] = "string";
}
Console.WriteLine(schemaObj.ToString());
source to share