SampleJsonSchemaGenerator
Generates a JSON Schema from sample JSON data.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace NJsonSchema.Generation
{
public class SampleJsonSchemaGenerator
{
public JsonSchema4 Generate(string json)
{
JToken token = JsonConvert.DeserializeObject<JToken>(json, new JsonSerializerSettings {
DateFormatHandling = DateFormatHandling.IsoDateFormat
});
JsonSchema4 jsonSchema = new JsonSchema4();
Generate(token, jsonSchema, jsonSchema, "Anonymous");
return jsonSchema;
}
private void Generate(JToken token, JsonSchema4 schema, JsonSchema4 rootSchema, string typeNameHint)
{
if (schema != rootSchema && token.Type == JTokenType.Object) {
JsonSchema4 jsonSchema = null;
JObject jObject;
if ((jObject = (token as JObject)) != null) {
IEnumerable<JProperty> properties = jObject.Properties();
jsonSchema = (from t in rootSchema.Definitions
select t.Value).FirstOrDefault(delegate(JsonSchema4 s) {
if (s.Type == JsonObjectType.Object)
return properties.All((JProperty p) => s.Properties.ContainsKey(p.Name));
return false;
});
}
if (jsonSchema == null) {
jsonSchema = new JsonSchema4();
AddSchemaDefinition(rootSchema, jsonSchema, typeNameHint);
}
schema.Reference = jsonSchema;
GenerateWithoutReference(token, jsonSchema, rootSchema, typeNameHint);
} else
GenerateWithoutReference(token, schema, rootSchema, typeNameHint);
}
private void GenerateWithoutReference(JToken token, JsonSchema4 schema, JsonSchema4 rootSchema, string typeNameHint)
{
if (token != null) {
switch (token.Type) {
case JTokenType.Object:
GenerateObject(token, schema, rootSchema);
break;
case JTokenType.Array:
GenerateArray(token, schema, rootSchema, typeNameHint);
break;
case JTokenType.Date:
schema.Type = JsonObjectType.String;
schema.Format = ((token.Value<DateTime>() == token.Value<DateTime>().Date) ? "date" : "date-time");
break;
case JTokenType.String:
schema.Type = JsonObjectType.String;
break;
case JTokenType.Boolean:
schema.Type = JsonObjectType.Boolean;
break;
case JTokenType.Integer:
schema.Type = JsonObjectType.Integer;
break;
case JTokenType.Float:
schema.Type = JsonObjectType.Number;
break;
case JTokenType.Bytes:
schema.Type = JsonObjectType.String;
schema.Format = "byte";
break;
case JTokenType.TimeSpan:
schema.Type = JsonObjectType.String;
schema.Format = "time-span";
break;
case JTokenType.Guid:
schema.Type = JsonObjectType.String;
schema.Format = "guid";
break;
case JTokenType.Uri:
schema.Type = JsonObjectType.String;
schema.Format = "uri";
break;
}
if (schema.Type == JsonObjectType.String && Regex.IsMatch(token.Value<string>(), "^[0-2][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$"))
schema.Format = "date";
if (schema.Type == JsonObjectType.String && Regex.IsMatch(token.Value<string>(), "^[0-2][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9](:[0-9][0-9])?$"))
schema.Format = "date-time";
if (schema.Type == JsonObjectType.String && Regex.IsMatch(token.Value<string>(), "^[0-9][0-9]:[0-9][0-9](:[0-9][0-9])?$"))
schema.Format = "time-span";
}
}
private void GenerateObject(JToken token, JsonSchema4 schema, JsonSchema4 rootSchema)
{
schema.Type = JsonObjectType.Object;
foreach (JProperty item in ((JObject)token).Properties()) {
JsonProperty jsonProperty = new JsonProperty();
string typeNameHint = ConversionUtilities.ConvertToUpperCamelCase((item.Value.Type == JTokenType.Array) ? ConversionUtilities.Singularize(item.Name) : item.Name, true);
Generate(item.Value, jsonProperty, rootSchema, typeNameHint);
schema.Properties[item.Name] = jsonProperty;
}
}
private void GenerateArray(JToken token, JsonSchema4 schema, JsonSchema4 rootSchema, string typeNameHint)
{
schema.Type = JsonObjectType.Array;
List<JsonSchema4> list = ((JArray)token).Select(delegate(JToken item) {
JsonSchema4 jsonSchema = new JsonSchema4();
GenerateWithoutReference(item, jsonSchema, rootSchema, typeNameHint);
return jsonSchema;
}).ToList();
if (list.Count == 0)
schema.Item = new JsonSchema4();
else if ((from s in list
group s by s.Type).Count() == 1) {
MergeAndAssignItemSchemas(rootSchema, schema, list, typeNameHint);
} else {
schema.Item = list.First();
}
}
private void MergeAndAssignItemSchemas(JsonSchema4 rootSchema, JsonSchema4 schema, List<JsonSchema4> itemSchemas, string typeNameHint)
{
JsonSchema4 jsonSchema = itemSchemas.First();
JsonSchema4 jsonSchema2 = new JsonSchema4 {
Type = jsonSchema.Type
};
if (jsonSchema.Type == JsonObjectType.Object) {
foreach (IGrouping<string, KeyValuePair<string, JsonProperty>> item in from p in itemSchemas.SelectMany((JsonSchema4 s) => s.Properties)
group p by p.Key) {
jsonSchema2.Properties[item.Key] = item.First().Value;
}
}
AddSchemaDefinition(rootSchema, jsonSchema2, typeNameHint);
schema.Item = new JsonSchema4 {
Reference = jsonSchema2
};
}
private void AddSchemaDefinition(JsonSchema4 rootSchema, JsonSchema4 schema, string typeNameHint)
{
if (string.IsNullOrEmpty(typeNameHint) || rootSchema.Definitions.ContainsKey(typeNameHint))
rootSchema.Definitions["Anonymous" + (rootSchema.Definitions.Count + 1)] = schema;
else
rootSchema.Definitions[typeNameHint] = schema;
}
}
}