Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ea8c839

Browse files
committedJun 9, 2024
add lesson for generation
1 parent 5aadce3 commit ea8c839

File tree

5 files changed

+271
-29
lines changed

5 files changed

+271
-29
lines changed
 

‎LearnJsonEverything.Tests/ProvidedSolutionTests.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ public void Schema(LessonData lesson)
4848
foreach (var result in results)
4949
{
5050
Console.WriteLine(result);
51+
}
52+
53+
foreach (var result in results)
54+
{
5155
Assert.That(result, Does.StartWith(Iconography.SuccessIcon));
5256
}
5357
}

‎LearnJsonEverything.Tests/ReferenceLoader.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using System.Reflection;
12
using System.Text.Json.Nodes;
23
using Json.Schema;
4+
using Json.Schema.Generation;
35
using Microsoft.CodeAnalysis;
46
using Yaml2JsonNode;
57

@@ -18,6 +20,7 @@ static ReferenceLoader()
1820
SchemaRegistry.Global.Fetch = null!;
1921
_ = YamlSerializer.Parse(string.Empty);
2022
_ = new NullRunner();
23+
_ = typeof(NullRunner).GetCustomAttributes<MinimumAttribute>();
2124
}
2225

2326
public static MetadataReference[] Load()

‎LearnJsonEverything/LearnJsonEverything.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
1616
<PackageReference Include="BlazorMonaco" Version="3.2.0" />
1717
<PackageReference Include="JsonSchema.Net" Version="7.0.4" />
18+
<PackageReference Include="JsonSchema.Net.Generation" Version="4.3.0.2" />
1819
<PackageReference Include="Markdig" Version="0.37.0" />
1920
<PackageReference Include="Markdig.SyntaxHighlighting" Version="1.1.7" />
2021
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.5" />

‎LearnJsonEverything/Services/CompilationHelpers.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
using Microsoft.CodeAnalysis;
1+
using Json.Schema.Generation.XmlComments;
2+
using Microsoft.CodeAnalysis;
23
using Microsoft.CodeAnalysis.CSharp;
34
using System.Reflection;
5+
using Json.Schema.Generation;
46
using static LearnJsonEverything.Services.Iconography;
57

68
namespace LearnJsonEverything.Services;
@@ -15,6 +17,7 @@ public static class CompilationHelpers
1517
"Json.More",
1618
"JsonPointer.Net",
1719
"JsonSchema.Net",
20+
"JsonSchema.Net.Generation",
1821
"LearnJsonEverything.Template",
1922
"Yaml2JsonNode",
2023
];
@@ -105,10 +108,18 @@ public static (ILessonRunner<T>?, string[]) GetRunner<T>(LessonData lesson)
105108
#pragma warning disable IL2026
106109
#pragma warning disable IL2072
107110
#pragma warning disable IL2070
111+
#pragma warning disable CS0618
108112
var assembly = Assembly.Load(dllStream.ToArray());
109113

114+
xmlStream.Position = 0;
115+
using var reader = new StreamReader(xmlStream);
116+
var xmlContent = reader.ReadToEnd();
117+
DocXmlReader.ExplicitlyAddAssemblyXml(assembly, xmlContent);
118+
119+
110120
var type = assembly.DefinedTypes.Single(x => !x.IsInterface && x.ImplementedInterfaces.Contains(typeof(ILessonRunner<T>)));
111121
var runner = (ILessonRunner<T>)Activator.CreateInstance(type)!;
122+
#pragma warning restore CS0618
112123
#pragma warning restore IL2070
113124
#pragma warning restore IL2072
114125
#pragma warning restore IL2026

‎LearnJsonEverything/wwwroot/data/lessons/schema.yaml

Lines changed: 251 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@
44
JSON Schema is typically itself represented in JSON. To support this, the `JsonSchema`
55
type is completely compatible with the _System.Text.Json_ serializer.
66
docs: 'schema/basics/#schema-deserialization'
7-
title: Deserializing a schema
7+
title: Deserializing a Schema
88
instructions: |
99
Deserialize the text in `schemaText` into a `JsonSchema` variable called `schema`.
1010
inputTemplate: ''
1111
contextCode: |-
12-
using System;
13-
using System.Collections.Generic;
1412
using System.Text.Json;
1513
using System.Text.Json.Nodes;
16-
using System.Text.Json.Serialization;
1714
using Json.Schema;
1815
1916
namespace LearnJsonEverything;
@@ -65,14 +62,12 @@
6562
`JsonSchema` and `EvaluationOptions` to your serializer context in order to support
6663
source generation.
6764
docs: 'schema/basics/#aot'
68-
title: Deserializing a schema (AOT)
65+
title: Deserializing a Schema (AOT)
6966
instructions: |
7067
Create a serializer context class called `MySerializerContext` and add the appropriate
7168
attributes.
7269
inputTemplate: ''
7370
contextCode: |-
74-
using System;
75-
using System.Collections.Generic;
7671
using System.Text.Json;
7772
using System.Text.Json.Nodes;
7873
using System.Text.Json.Serialization;
@@ -122,7 +117,7 @@
122117
- instance: 6.8
123118
isValid: false
124119
- id: 26b6ebca-58e6-4814-86ea-4946d844c9a6
125-
title: 'Schema Builder: Any string'
120+
title: 'Schema Builder: Any String'
126121
background: |
127122
The `JsonSchemaBuilder` class provides a type-safe fluent interface for building schemas in code.
128123
Adding a keyword is accomplished by calling the same-name (usually) extension method.
@@ -132,11 +127,7 @@
132127
Configure the provided `JsonSchemaBuilder` to produce a schema that validates a string instance.
133128
inputTemplate: ''
134129
contextCode: |-
135-
using System;
136-
using System.Collections.Generic;
137-
using System.Text.Json;
138130
using System.Text.Json.Nodes;
139-
using System.Text.Json.Serialization;
140131
using Json.Schema;
141132
142133
namespace LearnJsonEverything;
@@ -166,7 +157,7 @@
166157
- instance: false
167158
isValid: false
168159
- id: 26b6ebca-58e6-4814-86ea-4946d844c9a7
169-
title: 'Schema Builder: Number with limits'
160+
title: 'Schema Builder: Number with Limits'
170161
background: |
171162
The `JsonSchemaBuilder` class provides a type-safe fluent interface for building schemas in code.
172163
Adding a keyword is accomplished by calling the same-name (usually) extension method.
@@ -177,11 +168,8 @@
177168
that is less than or equal to 10 but greater than 0.
178169
inputTemplate: ''
179170
contextCode: |-
180-
using System;
181171
using System.Collections.Generic;
182-
using System.Text.Json;
183172
using System.Text.Json.Nodes;
184-
using System.Text.Json.Serialization;
185173
using Json.Schema;
186174
187175
namespace LearnJsonEverything;
@@ -234,17 +222,13 @@
234222
that automatically calls the `.Build()` method so you don't have to.
235223
docs: 'schema/basics/#schema-inlining'
236224
api: api/JsonSchema.Net/JsonSchemaBuilderExtensions
237-
title: 'Schema Builder: Nesting schemas'
225+
title: 'Schema Builder: Nesting Schemas'
238226
instructions: |
239227
Configure the provided `JsonSchemaBuilder` to produce a schema that validates a array instance
240228
filled with integers. Ensure that the array has no more than 5 items.
241229
inputTemplate: ''
242230
contextCode: |-
243-
using System;
244-
using System.Collections.Generic;
245-
using System.Text.Json;
246231
using System.Text.Json.Nodes;
247-
using System.Text.Json.Serialization;
248232
using Json.Schema;
249233
250234
namespace LearnJsonEverything;
@@ -295,11 +279,7 @@
295279
Configure the evaluation options to enable `format` validation.
296280
inputTemplate: ''
297281
contextCode: |-
298-
using System;
299-
using System.Collections.Generic;
300-
using System.Text.Json;
301282
using System.Text.Json.Nodes;
302-
using System.Text.Json.Serialization;
303283
using Json.Schema;
304284
305285
namespace LearnJsonEverything;
@@ -381,11 +361,8 @@
381361
found in the `specVersion` variable.
382362
inputTemplate: ''
383363
contextCode: |-
384-
using System;
385-
using System.Collections.Generic;
386364
using System.Text.Json;
387365
using System.Text.Json.Nodes;
388-
using System.Text.Json.Serialization;
389366
using Json.Schema;
390367
using Json.More;
391368
@@ -454,3 +431,249 @@
454431
- instance: ["a string",true,2,4,6,8,4]
455432
version: Draft202012
456433
isValid: true
434+
- id: 26b6ebca-58e6-4824-8dea-4946d444c9a8
435+
background: |
436+
Most often you need to ensure that incoming JSON data properly represents the models
437+
you've created for your application. Often the best way to ensure this is to
438+
generate schemas directly from those models.
439+
440+
_JsonSchema.Net.Generation_ provides an additional extension on `JsonSchemaBuilder`
441+
called `.FromType<T>()`.
442+
docs: 'schema/schemagen/schema-generation'
443+
title: 'Generation: Essentials'
444+
instructions: |
445+
Configure the builder to generate a schema for the `Person` type.
446+
inputTemplate: ''
447+
contextCode: |-
448+
using System.Text.Json.Nodes;
449+
using Json.Schema;
450+
using Json.Schema.Generation;
451+
452+
namespace LearnJsonEverything;
453+
454+
public class Person
455+
{
456+
public string FirstName { get; set; }
457+
public string LastName { get; set; }
458+
public int Age { get; set; }
459+
}
460+
461+
public class Lesson : ILessonRunner<EvaluationResults>
462+
{
463+
public EvaluationResults Run(JsonObject test)
464+
{
465+
var instance = test["instance"];
466+
var builder = new JsonSchemaBuilder();
467+
468+
/* USER CODE */
469+
470+
var schema = builder.Build();
471+
return schema.Evaluate(instance);
472+
}
473+
}
474+
solution: |-
475+
builder.FromType<Person>();
476+
tests:
477+
- instance: { FirstName: Jane, LastName: Doe, Age: 24 }
478+
isValid: true
479+
- instance: { FirstName: Jane }
480+
isValid: true
481+
- instance: { FirstName: 24 }
482+
isValid: false
483+
- id: 26b6ebca-5de6-4824-8dea-4946d444c9a8
484+
background: |
485+
Most often you need to ensure that incoming JSON data properly represents the models
486+
you've created for your application. Often the best way to ensure this is to
487+
generate schemas directly from those models.
488+
489+
_JsonSchema.Net.Generation_ provides an additional extension on `JsonSchemaBuilder`
490+
called `.FromType<T>()`. It also provides numerous attributes that can be applied
491+
to your models in order to create additional constraints in the generated schema.
492+
docs: 'schema/schemagen/schema-generation#schema-schemagen-best-practices'
493+
title: 'Generation: More Constraints'
494+
instructions: |
495+
Add `FirstName`, `LastName`, and `Age` properties to the `Person` class, and add
496+
attributes that provide the following constraints:
497+
498+
- all properties are required
499+
- `FirstName` and `LastName` must be at least two characters long
500+
- `Age` must be greater than 0
501+
inputTemplate: ''
502+
contextCode: |-
503+
using System.Text.Json.Nodes;
504+
using Json.Schema;
505+
using Json.Schema.Generation;
506+
507+
namespace LearnJsonEverything;
508+
509+
public class Person
510+
{
511+
/* USER CODE */
512+
}
513+
514+
public class Lesson : ILessonRunner<EvaluationResults>
515+
{
516+
public EvaluationResults Run(JsonObject test)
517+
{
518+
var instance = test["instance"];
519+
var builder = new JsonSchemaBuilder().FromType<Person>();
520+
var schema = builder.Build();
521+
return schema.Evaluate(instance);
522+
}
523+
}
524+
solution: |-
525+
[Required]
526+
[MinLength(2)]
527+
public string FirstName { get; set; }
528+
[Required]
529+
[MinLength(2)]
530+
public string LastName { get; set; }
531+
[Required]
532+
[ExclusiveMinimum(0)]
533+
public int Age { get; set; }
534+
tests:
535+
- instance: { FirstName: Jane, LastName: Doe, Age: 24 }
536+
isValid: true
537+
- instance: { FirstName: Jane, LastName: Doe }
538+
isValid: false
539+
- instance: { FirstName: Jane, Age: 24 }
540+
isValid: false
541+
- instance: { LastName: Doe, Age: 24 }
542+
isValid: false
543+
- instance: { FirstName: 24 }
544+
isValid: false
545+
- instance: { FirstName: J, LastName: Doe, Age: 24 }
546+
isValid: false
547+
- instance: { FirstName: Jane, LastName: D, Age: 24 }
548+
isValid: false
549+
- instance: { FirstName: Jane, LastName: Doe, Age: -6 }
550+
isValid: false
551+
- id: 26b6ebca-5de6-4824-8dea-4a46d444c9a8
552+
background: |
553+
Most often you need to ensure that incoming JSON data properly represents the models
554+
you've created for your application. Often the best way to ensure this is to
555+
generate schemas directly from those models.
556+
557+
_JsonSchema.Net.Generation_ provides an additional extension on `JsonSchemaBuilder`
558+
called `.FromType<T>()`. It also provides numerous attributes that can be applied
559+
to your models in order to create additional constraints in the generated schema.
560+
561+
Nullability works differently in JSON and JSON Schema than it does in .Net. Use
562+
the `[Nullable()]` attribute to support null values the JSON / JSON Schema way.
563+
docs: 'schema/schemagen/schema-generation#schema-schemagen-nullability'
564+
title: 'Generation: Nullability'
565+
instructions: |
566+
Add a `DateTime` property called `BirthDate` and mark it as nullable.
567+
inputTemplate: ''
568+
contextCode: |-
569+
using System;
570+
using System.Text.Json.Nodes;
571+
using Json.Schema;
572+
using Json.Schema.Generation;
573+
574+
namespace LearnJsonEverything;
575+
576+
public class Person
577+
{
578+
[Required]
579+
[MinLength(2)]
580+
public string FirstName { get; set; }
581+
[Required]
582+
[MinLength(2)]
583+
public string LastName { get; set; }
584+
[Required]
585+
[ExclusiveMinimum(0)]
586+
public int Age { get; set; }
587+
588+
/* USER CODE */
589+
}
590+
591+
public class Lesson : ILessonRunner<EvaluationResults>
592+
{
593+
public EvaluationResults Run(JsonObject test)
594+
{
595+
var instance = test["instance"];
596+
var builder = new JsonSchemaBuilder().FromType<Person>();
597+
var schema = builder.Build();
598+
// DateTime will generate a `format: date-time` constraint,
599+
// so we need to be sure to validate the `format` keyword.
600+
var options = new EvaluationOptions { RequireFormatValidation = true };
601+
return schema.Evaluate(instance, options);
602+
}
603+
}
604+
solution: |-
605+
[Nullable(true)]
606+
public DateTime? BirthDate { get; set; }
607+
tests:
608+
- instance: { FirstName: Jane, LastName: Doe, Age: 24 }
609+
isValid: true
610+
- instance: { FirstName: Jane, LastName: Doe, Age: 24, BirthDate: null }
611+
isValid: true
612+
- instance: { FirstName: Jane, LastName: Doe, Age: 24, BirthDate: '2000-06-13T00:00:00-06:00' }
613+
isValid: true
614+
- instance: { FirstName: Jane, LastName: Doe, Age: 24, BirthDate: '13 June 2000' }
615+
isValid: false
616+
- instance: { FirstName: Jane, LastName: Doe, Age: 24, BirthDate: 24 }
617+
isValid: false
618+
- id: 26b6ebca-5de6-4824-8dea-4a46d414c9a8
619+
background: |
620+
Most often you need to ensure that incoming JSON data properly represents the models
621+
you've created for your application. Often the best way to ensure this is to
622+
generate schemas directly from those models.
623+
624+
_JsonSchema.Net.Generation_ provides an additional extension on `JsonSchemaBuilder`
625+
called `.FromType<T>()`.
626+
627+
Generation will also look for the `<summary>` tag in XML comments to add a `description`
628+
keyword to your schemas. This is supported on properties and types.
629+
docs: 'schema/schemagen/schema-generation#schema-schemagen-nullability'
630+
title: 'Generation: XML Comments'
631+
instructions: |
632+
Add some XML comments to the `Person` type to generate a `description` keyword.
633+
inputTemplate: ''
634+
contextCode: |-
635+
using System;
636+
using System.Text.Json;
637+
using System.Text.Json.Nodes;
638+
using Json.Schema;
639+
using Json.Schema.Generation;
640+
using Json.More;
641+
642+
namespace LearnJsonEverything;
643+
644+
/* USER CODE */
645+
public class Person
646+
{
647+
[Required]
648+
[MinLength(2)]
649+
public string FirstName { get; set; }
650+
[Required]
651+
[MinLength(2)]
652+
public string LastName { get; set; }
653+
[Required]
654+
[ExclusiveMinimum(0)]
655+
public int Age { get; set; }
656+
}
657+
658+
public class Lesson : ILessonRunner<EvaluationResults>
659+
{
660+
public EvaluationResults Run(JsonObject test)
661+
{
662+
var builder = new JsonSchemaBuilder().FromType<Person>();
663+
var schema = builder.Build();
664+
var schemaAsNode = JsonSerializer.SerializeToNode(schema);
665+
666+
var metaSchema = new JsonSchemaBuilder()
667+
.Type(SchemaValueType.Object)
668+
.Required("description");
669+
670+
return metaSchema.Evaluate(schemaAsNode);
671+
}
672+
}
673+
solution: |-
674+
/// <summary>
675+
/// This is my summary. It'll end up in a `description` keyword.
676+
/// </summary>
677+
tests:
678+
- instance: null
679+
isValid: true

0 commit comments

Comments
 (0)
Please sign in to comment.