Skip to content

Commit

Permalink
Merge pull request #16 from bonsai-rx/discriminator-dev
Browse files Browse the repository at this point in the history
Add support for JSON type discriminators
  • Loading branch information
glopesdev authored Jan 6, 2024
2 parents eb65dc8 + 54c8227 commit da2ce9d
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 5 deletions.
77 changes: 77 additions & 0 deletions Bonsai.Sgen.Tests/DiscriminatorGenerationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NJsonSchema;
using System.Threading.Tasks;

namespace Bonsai.Sgen.Tests
{
[TestClass]
public class DiscriminatorGenerationTests
{
[TestMethod]
public async Task GenerateDiscriminatorSchema_SerializerAnnotationsDeclareKnownTypes()
{
var schema = await JsonSchema.FromJsonAsync(@"
{
""$schema"": ""http://json-schema.org/draft-04/schema#"",
""type"": ""object"",
""title"": ""Container"",
""additionalProperties"": false,
""properties"": {
""Animal"": {
""oneOf"": [
{
""$ref"": ""#/definitions/Animal""
},
{
""type"": ""null""
}
]
}
},
""definitions"": {
""Dog"": {
""type"": ""object"",
""additionalProperties"": false,
""properties"": {
""Bar"": {
""type"": [
""null"",
""string""
]
}
},
""allOf"": [
{
""$ref"": ""#/definitions/Animal""
}
]
},
""Animal"": {
""type"": ""object"",
""discriminator"": ""discriminator"",
""x-abstract"": true,
""additionalProperties"": false,
""required"": [
""discriminator""
],
""properties"": {
""Foo"": {
""type"": [
""null"",
""string""
]
},
""discriminator"": {
""type"": ""string""
}
}
}
}
}
");
var generator = TestHelper.CreateGenerator(schema);
var code = generator.GenerateFile();
Assert.IsTrue(code.Contains("[JsonInheritanceAttribute(\"Dog\", typeof(Dog))]"));
}
}
}
26 changes: 23 additions & 3 deletions Bonsai.Sgen/CSharpClassTemplate.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System.CodeDom;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Text;
using System.Xml.Serialization;
using Newtonsoft.Json;
using NJsonSchema.CodeGeneration;
using NJsonSchema.Converters;
using YamlDotNet.Serialization;

namespace Bonsai.Sgen
Expand Down Expand Up @@ -34,7 +34,27 @@ public CSharpClassTemplate(
public string Render()
{
var type = new CodeTypeDeclaration(Model.ClassName) { IsPartial = true };
var jsonSerializer = Settings.SerializerLibraries.HasFlag(SerializerLibraries.NewtonsoftJson);
var yamlSerializer = Settings.SerializerLibraries.HasFlag(SerializerLibraries.YamlDotNet);
if (Model.IsAbstract) type.TypeAttributes |= System.Reflection.TypeAttributes.Abstract;
if (Model.HasDiscriminator)
{
if (jsonSerializer)
{
type.CustomAttributes.Add(new CodeAttributeDeclaration(
new CodeTypeReference(typeof(JsonConverter)),
new CodeAttributeArgument(new CodeTypeOfExpression(nameof(JsonInheritanceConverter))),
new CodeAttributeArgument(new CodePrimitiveExpression(Model.Discriminator))));
foreach (var derivedModel in Model.DerivedClasses)
{
type.CustomAttributes.Add(new CodeAttributeDeclaration(
new CodeTypeReference(nameof(JsonInheritanceAttribute)),
new CodeAttributeArgument(new CodePrimitiveExpression(derivedModel.Discriminator)),
new CodeAttributeArgument(new CodeTypeOfExpression(derivedModel.ClassName))));
}
}
}

if (Model.HasDescription)
{
type.Comments.Add(new CodeCommentStatement("<summary>", docComment: true));
Expand Down Expand Up @@ -84,7 +104,7 @@ public string Render()
new CodeTypeReference(typeof(XmlIgnoreAttribute))));
}

if (Settings.SerializerLibraries.HasFlag(SerializerLibraries.NewtonsoftJson))
if (jsonSerializer)
{
var jsonProperty = new CodeAttributeDeclaration(
new CodeTypeReference(typeof(JsonPropertyAttribute)),
Expand All @@ -99,7 +119,7 @@ public string Render()
}
propertyDeclaration.CustomAttributes.Add(jsonProperty);
}
if (Settings.SerializerLibraries.HasFlag(SerializerLibraries.YamlDotNet))
if (yamlSerializer)
{
propertyDeclaration.CustomAttributes.Add(new CodeAttributeDeclaration(
new CodeTypeReference(typeof(YamlMemberAttribute)),
Expand Down
13 changes: 12 additions & 1 deletion Bonsai.Sgen/CSharpCodeDomGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ private CodeArtifact GenerateSerializer(CSharpSerializerTemplate template)
return new CodeArtifact(template.ClassName, CodeArtifactType.Class, CodeArtifactLanguage.CSharp, CodeArtifactCategory.Contract, template);
}

private CodeArtifact ReplaceInitOnlyProperties(CodeArtifact modelType)
{
if (modelType.TypeName == nameof(NJsonSchema.Converters.JsonInheritanceAttribute))
{
var code = modelType.Code.Replace("{ get; }", "{ get; private set; }");
modelType = new CodeArtifact(modelType.TypeName, modelType.Type, modelType.Language, modelType.Category, code);
}

return modelType;
}

public override IEnumerable<CodeArtifact> GenerateTypes()
{
var types = base.GenerateTypes();
Expand All @@ -87,7 +98,7 @@ public override IEnumerable<CodeArtifact> GenerateTypes()
extraTypes.Add(GenerateSerializer(deserializer));
}

return types.Concat(extraTypes);
return types.Select(ReplaceInitOnlyProperties).Concat(extraTypes);
}
}
}
5 changes: 4 additions & 1 deletion Bonsai.Sgen/CSharpSerializerTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Xml.Serialization;
using NJsonSchema;
using NJsonSchema.CodeGeneration;
using NJsonSchema.Converters;

namespace Bonsai.Sgen
{
Expand All @@ -15,7 +16,9 @@ public CSharpSerializerTemplate(
CodeGeneratorOptions options,
CSharpCodeDomGeneratorSettings settings)
{
ModelTypes = modelTypes;
ModelTypes = modelTypes.ExceptBy(
new[] { nameof(JsonInheritanceAttribute), nameof(JsonInheritanceConverter) },
r => r.TypeName);
Provider = provider;
Options = options;
Settings = settings;
Expand Down

0 comments on commit da2ce9d

Please sign in to comment.