Skip to content

Schema/codegen #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions _docs/schema/codegen/close.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: __close
close: true
permalink: /schema/codegen/:title/
order: "1.6.9"
---
192 changes: 192 additions & 0 deletions _docs/schema/codegen/schema-codegen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
layout: page
title: Generating Code from JSON Schema
bookmark: Basics
permalink: /schema/:title/
icon: fas fa-tag
order: "1.6.1"
---
*JsonSchema.Net.CodeGeneration* is a tool that can create C# code from JSON Schemas.

For example, given the schema:

```json
{
"title": "MyObject",
"type": "object",
"properties": {
"Foo": { "type": "string" }
}
}
```

it can generate a C# class like

```c#
public class MyObject
{
public string Foo { get; set; }
}
```

## Capabilities {#schema-codegen-capabilities}

The code generation is currently quite basic. It will generate types for simple custom objects and any named array or dictionary type.

`$ref` is generally supported, even for recursive models like linked lists and binary trees, however there is no loop detection, so if you do something like this:

```json
{
"$ref": "#"
}
```

you'll just get a stack overflow exception. That's on you. Don't do that.

### Built-in types

Built-in types, like strings and arrays, are supported by reference only. These generally will not have type declarations generated for them, however they can appear as types used within other declarations. For example, `string` in the example above is used for the `Foo` property, but there isn't a `string` type declaration included in the output because the type is built-in.

There is an exception to this behavior for arrays and dictionaries, which is explained [below](#including-a-name).

## Usage

There is currently limited support for translating JSON Schema into code. However there is ongoing discussion for an official [JSON Schema code generation vocabulary](https://github.com/json-schema-org/vocab-idl). Please feel free to read up and join in on the effort there.

Currently, the class name is derived from the `title` keyword. There is an open issue in the repository above to discuss using this keyword. It's currently leaning toward the vocabulary defining a custom keyword, but as nothing has been decided yet, `title` is used here for now.

### Custom objects

Generating for custom objects is the real power behind code generation. Being able to read a schema produced by some other developer (e.g. from an OpenAPI document) and automatically create types can save developers a lot of time.

This library generates custom types for schemas that declare an `object` type and include `title` and `properties` without `additionalProperties`. It will also automatically generate types found nested in the schema. For example

```json
{
"title": "MyCustomObject",
"type": "object",
"properties": {
"Foo": { "type": "string" },
"Bar": {
"title": "Bar",
"type": "object",
"properties": {
"Baz": { "type": "integer" }
}
}
}
}
```

generates

```c#
public class MyCustomObject
{
public string Foo { get; set; }
public Bar Bar { get; set; }
}

public class Bar
{
public int Baz { get; set; }
}
```

There is some basic duplicate definition detection that serves two purposes:

1. It avoids creating multiple declarations for the same type. For example, if `Foo` had `Bar1` and `Bar2` properties, only one `Bar` declaration would be generated. Ideally this kind of duplication should be defined in the schema using a `$ref`.
2. It prevents creating multiple types with the same name. For example, if there are two subschemas with the same name that define two different types, an exception would be thrown indicating the name re-use.

For type and property naming, some basic string transformation occurs:

| Original | Transformed |
|:-|:-|
| `kebab-case` | `KebabCase` |
| `snake_case` | `SnakeCase` |
| `spaced words` |`SpacedWords` |
| `words-with-2-numbers` | `WordsWith2Numbers` |
| `just-a-letter` | `JustALetter` |

Anything other conventions will likely result in undesirable code output or an exception.

### Arrays

When a schema declares an `array` type and includes an `items` keyword (in the single schema form, not an array of schemas), an array is used.

```json
{
"type": "array",
"items": {
"type": "integer"
}
}
```

produces no declaration, but using this schema (e.g. to define object properties) appears as

```c#
int[]
```

### Dictionaries

When a schema declares an `object` type and only includes `additionalProperties` without `properties`, this models a dictionary.

```json
{
"title": "MyIntDictionary",
"type": "object",
"additionalProperties": {
"type": "integer"
}
}
```

This also produces no declaration, but using the schema appears as

```c#
Dictionary<string, int>
```

### Including a name on basic types {#including-a-name}

When you have an array or dictionary schema with a `title` keyword

```json
{
"title": "MyIntArray",
"type": "array",
"items": {
"type": "integer"
}
}
```

or

```json
{
"title": "MyIntDictionary",
"type": "object",
"additionalProperties": {
"type": "integer"
}
}
```

you now get a type declaration inheriting from `List<T>`

```c#
public class MyIntArray : List<int> {}
```

and `Dictionary<TKey, TValue>`

```c#
public class MyIntDictionary : Dictionary<string, int> {}
```

and usages will correctly appear as `MyIntArray` and `MyIntDictionary`, respectively.

Other basic types (e.g. strings) will ignore the `title` keyword as these types cannot be inherited.
7 changes: 7 additions & 0 deletions _docs/schema/codegen/title.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: __title
bookmark: Code Generation
permalink: /schema/datagen/:title/
folder: true
order: "1.6"
---
2 changes: 1 addition & 1 deletion _docs/schema/datagen/close.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
title: __close
close: true
permalink: /schema/datagen/:title/
order: "1.6.9"
order: "1.7.9"
---
2 changes: 1 addition & 1 deletion _docs/schema/datagen/schema-datagen.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Generating Sample JSON Data from a Schema
bookmark: Basics
permalink: /schema/:title/
icon: fas fa-tag
order: "1.6.1"
order: "1.7.1"
---
*JsonSchema.Net.DataGeneration* is a tool that can create JSON data instances using a JSON schema as a framework.

Expand Down
2 changes: 1 addition & 1 deletion _docs/schema/datagen/title.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ title: __title
bookmark: Data Generation
permalink: /schema/datagen/:title/
folder: true
order: "1.6"
order: "1.7"
---
2 changes: 1 addition & 1 deletion _docs/schema/vocabs/close.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
title: __close
permalink: /schema/vocabs/:title/
close: true
order: "1.7.9"
order: "1.8.9"
---
2 changes: 1 addition & 1 deletion _docs/schema/vocabs/title.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ title: __title
bookmark: Prebuilt Vocabularies
permalink: /schema/vocabs/:title/
folder: true
order: "1.7"
order: "1.8"
---
2 changes: 1 addition & 1 deletion _docs/schema/vocabs/vocabs-data-2022.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: A Vocabulary for Accessing Data Stored in JSON
bookmark: data
permalink: /schema/vocabs/data-2022/
icon: fas fa-tag
order: "1.7.1"
order: "1.8.1"
---
## 1. Purpose {#schema-data-2022-purpose}

Expand Down
2 changes: 1 addition & 1 deletion _docs/schema/vocabs/vocabs-openapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: OpenAPI v3.1 Vocabulary
bookmark: Open API
permalink: /schema/vocabs/openapi/
icon: fas fa-tag
order: "1.7.3"
order: "1.8.3"
---

This library adds support for the vocabularies, meta-schemas, and keywords defined by the [OpenAPI v3.1 specification](https://spec.openapis.org/oas/latest.html).
Expand Down
2 changes: 1 addition & 1 deletion _docs/schema/vocabs/vocabs-unique-keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: A Vocabulary for Identifying Uniqueness of Array Items
bookmark: uniqueKeys
permalink: /schema/vocabs/uniquekeys/
icon: fas fa-tag
order: "1.7.2"
order: "1.8.2"
---
## 1. Purpose {#schema-uniquekeys-purpose}

Expand Down