Skip to content

Commit 301f242

Browse files
authored
Merge pull request #13 from gregsdennis/schemagen/multiple-if-same-prop
Schemagen/multiple if same prop
2 parents 6f4cd36 + 68ebfb0 commit 301f242

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

.jekyll-metadata

2.6 KB
Binary file not shown.

_docs/schema/schemagen/conditional-generation.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ The `[If]` attribute creates a condition involving a discrete value. It takes t
6464
- `value` - This is the expected value for the property you named above. The condition will apply when the property equals this value. The value can be any compiler-constant value, but really should be JSON-compatible. So strings, numbers, and booleans are generally best. Enum values will work, too, but `[IfEnum]` may be a better option if you're using an enum property.
6565
- `group` - This is a key that identifies a group for this condition. It can be any compiler-constant value.
6666

67+
On its own, a single `[If]` attribute will create an `if` keyword containing a `const` keyword and the value you specify. However, the attribute can also be repeated with different values under the same group in order to create an `enum` keyword containing all of the values in that group. An example of the `enum` generation can be found [here](/schema/examples/multiple-ifs-one-group). (Note that this is slightly different from the [`[IfEnum]`](#ifenum-attribute) described below.)
68+
6769
#### Example {#if-example}
6870

6971
The example starts by defining a person which contains a couple properties that we want to constrain conditionally.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
layout: page
3+
title: Using Multiple `[If]` Attributes in a Single Group
4+
bookmark: Stacking [If]s
5+
permalink: /schema/examples/:title/
6+
icon: fas fa-tag
7+
order: "01.5.2.5"
8+
---
9+
This example shows how multiple `[If]` attributes can be combined to create "OR" logic.
10+
11+
Suppose we have the following model
12+
13+
```c#
14+
class CarColors
15+
{
16+
public string Interior { get; set; }
17+
public string Exterior { get; set; }
18+
}
19+
```
20+
21+
Now let's say that there is a requirement that if the exterior color is white or black, the interior needs to be gray; otherwise, the interior can be any color.
22+
23+
## The standard way
24+
25+
In normal usage, we could create two condition groups: one for white and one for black. We would the apply constraints under those conditions.
26+
27+
```c#
28+
[If(nameof(Exterior, "white", "isWhite"))]
29+
[If(nameof(Exterior, "black", "isBlack"))]
30+
class CarColors
31+
{
32+
[Const("gray", ConditionGroup = "isWhite")]
33+
[Const("gray", ConditionGroup = "isBlack")]
34+
public string Interior { get; set; }
35+
public string Exterior { get; set; }
36+
}
37+
```
38+
39+
This would create the schema:
40+
41+
```json
42+
{
43+
"type": "object",
44+
"properties": {
45+
"Interior": { "type": "string" },
46+
"Exterior": { "type": "string" }
47+
},
48+
"allOf": [
49+
{
50+
"if": {
51+
"properties": {
52+
"Exterior": { "const": "white" }
53+
},
54+
"required": [ "Exterior" ]
55+
},
56+
"then": {
57+
"properties": {
58+
"Interior": { "const": "gray" }
59+
}
60+
}
61+
},
62+
{
63+
"if": {
64+
"properties": {
65+
"Exterior": { "const": "black" }
66+
},
67+
"required": [ "Exterior" ]
68+
},
69+
"then": {
70+
"properties": {
71+
"Interior": { "const": "gray" }
72+
}
73+
}
74+
}
75+
]
76+
}
77+
```
78+
79+
This is fine. But notice how the `then` constraint sets are the same. It'd be better if the conditions could be combined to check if `Exterior` is _either_ `"white"` or `"black"` and just have a single `then`.
80+
81+
## Combining conditions
82+
83+
To do this, we just need to specify the same group in the appropriate `[If]` attributes.
84+
85+
```c#
86+
[If(nameof(Exterior, "white", "grayRequirement"))]
87+
[If(nameof(Exterior, "black", "grayRequirement"))]
88+
class CarColors
89+
{
90+
[Const("gray", ConditionGroup = "grayRequirement")]
91+
public string Interior { get; set; }
92+
public string Exterior { get; set; }
93+
}
94+
```
95+
96+
This will collect all of the possible values for that property and render them under an `enum` instead of a `const`.
97+
98+
```json
99+
{
100+
"type": "object",
101+
"properties": {
102+
"Interior": { "type": "string" },
103+
"Exterior": { "type": "string" }
104+
},
105+
"allOf": [
106+
{
107+
"if": {
108+
"properties": {
109+
"Exterior": { "enum": [ "white", "black" ] }
110+
},
111+
"required": [ "Exterior" ]
112+
},
113+
"then": {
114+
"properties": {
115+
"Interior": { "const": "gray" }
116+
}
117+
}
118+
}
119+
]
120+
}
121+
```
122+
123+
This is a much more concise schema that better expresses the requirement.

0 commit comments

Comments
 (0)