Skip to content

Commit

Permalink
feat(schemas): update and enhance
Browse files Browse the repository at this point in the history
fixes: #7 , #6, #8
  • Loading branch information
ProbablePrime committed Nov 25, 2024
1 parent 4748148 commit 2253ae2
Show file tree
Hide file tree
Showing 6 changed files with 1,107 additions and 833 deletions.
19 changes: 8 additions & 11 deletions generator/JSONSchemaGenerator/JSONSchemaGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,19 @@
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Json.More.Net" Version="2.0.1.2" />
<PackageReference Include="JsonPointer.Net" Version="5.0.0" />
<PackageReference Include="JsonSchema.Net" Version="7.1.2" />
<PackageReference Include="JsonSchema.Net.Generation" Version="4.5.0" />
<PackageReference Include="Humanizer.Core" Version="3.0.0-beta.54" />
<PackageReference Include="Json.More.Net" Version="2.0.2" />
<PackageReference Include="JsonPointer.Net" Version="5.0.2" />
<PackageReference Include="JsonSchema.Net" Version="7.2.3" />
<PackageReference Include="JsonSchema.Net.Generation" Version="4.5.1" />
<PackageReference Include="JsonSchema.Net.Generation.DataAnnotations" Version="1.1.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Namotion.Reflection" Version="3.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NJsonSchema" Version="11.0.0" />
<PackageReference Include="NJsonSchema.Annotations" Version="11.0.0" />
<PackageReference Include="NJsonSchema.NewtonsoftJson" Version="11.0.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="Namotion.Reflection" Version="3.2.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" />
<PackageReference Include="System.Text.Encodings.Web" Version="9.0.0" />
<PackageReference Include="System.Text.Json" Version="9.0.0" />
</ItemGroup>
Expand Down
70 changes: 32 additions & 38 deletions generator/JSONSchemaGenerator/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using FrooxEngine.Headless;
using Json.Schema;
using Json.Schema.Generation;
using NJsonSchema.Generation;
using Json.Schema.Generation.DataAnnotations;
using JSONSchemaGenerator.Refiners;
using SkyFrost.Base;
using System;
using System.IO;
Expand All @@ -15,64 +16,57 @@ public class Program
public static Type[] TypesToGenerate = { typeof(AppConfig), typeof(HeadlessConfig) };
private static void Main(String[] args)
{
// Up 4
String projectDirectory = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "..", "..", "..", "..", ".."));;
// Up 5
//TODO: this is a mess
String projectDirectory = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "..", "..", "..", "..", "..")); ;

String outputPath = Path.Combine(projectDirectory, "schemas");
Directory.CreateDirectory(outputPath);

GenerateNJsonSchemas(outputPath);

GeneratJsonSchemaNet(outputPath);
GenerateSchemas(outputPath);
}

private static void GeneratJsonSchemaNet(String outputPath)
private static void GenerateSchemas(String outputPath)
{
var config = new SchemaGeneratorConfiguration();

SchemaGeneratorConfiguration config = new()
{
Nullability = Nullability.AllowForAllTypes,
Refiners = {
new ObsoleteEnumRefiner(),
new TitleRefiner()
}//,
//PropertyNameResolver = PropertyNameResolvers.CamelCase
};

JsonSerializerOptions jsonSerializerConfig = new JsonSerializerOptions
JsonSerializerOptions jsonSerializerConfig = new()
{
WriteIndented = true
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

DataAnnotationsSupport.AddDataAnnotations();

foreach (Type type in TypesToGenerate)
{
// https://localcoder.net/how-to-call-a-generic-method-using-a-type-variable-in-c
// Although this website looks like it just copies answers from Stack Overflow *shrugs*
MethodInfo genericMethod = typeof(Program).GetMethod(GenerateSchemaForName);
MethodInfo constructedMethod = genericMethod.MakeGenericMethod(type);
var schema = constructedMethod.Invoke(null, [jsonSerializerConfig, config]);
MethodInfo? genericMethod = typeof(Program).GetMethod(GenerateSchemaForName);
MethodInfo? constructedMethod = genericMethod?.MakeGenericMethod(type);
JsonSchema? schema = (JsonSchema?)(constructedMethod?.Invoke(null, [jsonSerializerConfig, config]));
if (schema == null)
continue;

File.WriteAllText($"{outputPath}/{type.Name}.schema.json", JsonSerializer.Serialize(schema, jsonSerializerConfig));
}
}

public const string GenerateSchemaForName = "GenerateSchemaFor";
public static JsonSchema GenerateSchemaFor<T>(JsonSerializerOptions serializerOptions, SchemaGeneratorConfiguration schemaGeneratiorConfig) {
JsonSchemaBuilder builder = new JsonSchemaBuilder();
schemaGeneratiorConfig.RegisterXmlCommentFile<T>();
return builder.FromType<T>(schemaGeneratiorConfig).Build();
}

private static void GenerateNJsonSchemas(String outputPath)
public const String GenerateSchemaForName = "GenerateSchemaFor";
public static JsonSchema GenerateSchemaFor<T>(JsonSerializerOptions serializerOptions, SchemaGeneratorConfiguration schemaGeneratiorConfig)
{
SystemTextJsonSchemaGeneratorSettings systemJsonSettings = new SystemTextJsonSchemaGeneratorSettings
{
SerializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
},
// VSCode hates me?
FlattenInheritanceHierarchy = true
};

JsonSchemaGenerator systemGenerator = new JsonSchemaGenerator(systemJsonSettings);

foreach (Type type in TypesToGenerate)
{
File.WriteAllText($"{outputPath}/{type.Name}.NJSON.schema.json", systemGenerator.Generate(type).ToJson());
}
JsonSchemaBuilder builder = new();
schemaGeneratiorConfig.RegisterXmlCommentFile<T>();
return builder.FromType<T>(schemaGeneratiorConfig).Schema(Json.Schema.MetaSchemas.DraftNextId)
.Build();
}
}
}
53 changes: 53 additions & 0 deletions generator/JSONSchemaGenerator/Refiners/ObsoleteEnumRefiner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Json.Schema.Generation;
using Json.Schema.Generation.Intents;
using System;
using System.Linq;

namespace JSONSchemaGenerator.Refiners;

/// <summary>
/// Removes Obsolete Enum values from an EnumIntent.
/// </summary>
/// <remarks>The standard generator will output obsolete values for enums. This makes sense from a "schema" perspective as
/// these items still exist and are therefore valid for the schema. But for documentation reasons, we should remove them.</remarks>
/// TODO: Would https://json-everything.net/ be interested in using this?
/// Based on work in: https://github.com/DW2MC/DW2ModLoader/blob/main/ModDevToolsMod/Dw2ContentDefinitionSchemaRefiner.cs and
/// https://github.com/LicoGame/Magus/blob/main/src/Magus.Json/MagusRefiner.cs without which I would have no idea how to do this.
public class ObsoleteEnumRefiner : ISchemaRefiner
{
public void Run(SchemaGenerationContextBase context)
{
Type type = context.Type;

// Shouldn't happen, but better safe then sorry.
if (!type.IsEnum)
{
return;
}

EnumIntent enumIntent = context.Intents.OfType<EnumIntent>().First();

if (enumIntent == null)
{
return;
}

enumIntent.Names = enumIntent.Names.Where((name) => !ShouldFilterEnumName(type, name)).ToList();
}

public static Boolean IsObsolete(Type type, String key)
{
return type.GetField(key)?.GetCustomAttributes(typeof(ObsoleteAttribute), false).Any() ?? false;
}

public Boolean ShouldFilterEnumName(Type type, String name)
{
return IsObsolete(type, name);
}

public Boolean ShouldRun(SchemaGenerationContextBase context)
{
// Only run on enums
return context.Type.IsEnum;
}
}
31 changes: 31 additions & 0 deletions generator/JSONSchemaGenerator/Refiners/TitleRefiner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Json.Schema.Generation;
using Json.Schema.Generation.Intents;
using System;
using System.Collections;
using System.Linq;

namespace JSONSchemaGenerator.Refiners;

/// <summary>
/// Gives any JSON Objects, a title property. Filters out most non-JSON Objects.
/// </summary>
public class TitleRefiner : ISchemaRefiner
{
public void Run(SchemaGenerationContextBase context)
{
context.Intents.Add(new TitleIntent(context.Type.Name));
}

public bool ShouldRun(SchemaGenerationContextBase context)
{
if (context.Intents.OfType<TitleIntent>().Any())
return false;

return !context.Type.IsPrimitive &&
context.Type != typeof(string) &&
context.Type != typeof(String) &&
context.Type.GetInterfaces().All(f => f != typeof(IEnumerable)) &&
!context.Type.IsNullableValueType() &&
!context.Type.IsNullableNumber();
}
}
Loading

0 comments on commit 2253ae2

Please sign in to comment.