Skip to content

Commit 29ce4b6

Browse files
alexaka1Copilot
andcommitted
fix(csharp): use numeric JSON operations for integer enum converters in C# generichost
Integer enums now correctly use reader.GetInt32()/GetInt64() and writer.WriteNumberValue() instead of reader.GetString() and writer.WriteStringValue() in the generated JsonConverter classes. This fixes System.InvalidOperationException when deserializing integer enum values from JSON numbers. Co-authored-by: copilot-swe-agent[bot] <198982749+copilot@users.noreply.github.com>
1 parent d81b052 commit 29ce4b6

File tree

78 files changed

+1080
-1554
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+1080
-1554
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isNumeric}}{{#isLong}}GetInt64{{/isLong}}{{#isFloat}}GetSingle{{/isFloat}}{{#isDouble}}GetDouble{{/isDouble}}{{#isDecimal}}GetDecimal{{/isDecimal}}{{^isLong}}{{^isFloat}}{{^isDouble}}{{^isDecimal}}GetInt32{{/isDecimal}}{{/isDouble}}{{/isFloat}}{{/isLong}}{{/isNumeric}}{{/-first}}{{/enumVars}}{{/allowableValues}}

modules/openapi-generator/src/main/resources/csharp/modelEnum.mustache

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@
121121
/// <returns></returns>
122122
public override {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
123123
{
124+
{{^isString}}
125+
{{>EnumValueDataType}} rawValue = reader.{{>EnumJsonReaderMethod}}();
126+
{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} result = ({{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}})rawValue;
127+
return result;
128+
{{/isString}}
129+
{{#isString}}
124130
string{{nrt?}} rawValue = reader.GetString();
125131

126132
{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}? result = rawValue == null
@@ -131,6 +137,7 @@
131137
return result.Value;
132138

133139
throw new JsonException();
140+
{{/isString}}
134141
}
135142

136143
/// <summary>
@@ -141,7 +148,12 @@
141148
/// <param name="options"></param>
142149
public override void Write(Utf8JsonWriter writer, {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} {{#lambda.camelcase_sanitize_param}}{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{/lambda.camelcase_sanitize_param}}, JsonSerializerOptions options)
143150
{
151+
{{^isString}}
152+
writer.WriteNumberValue({{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{/lambda.camelcase_sanitize_param}}));
153+
{{/isString}}
154+
{{#isString}}
144155
writer.WriteStringValue({{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{/lambda.camelcase_sanitize_param}}).ToString());
156+
{{/isString}}
145157
}
146158
}
147159

@@ -159,6 +171,15 @@
159171
/// <returns></returns>
160172
public override {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
161173
{
174+
{{^isString}}
175+
if (reader.TokenType == JsonTokenType.Null)
176+
return null;
177+
178+
{{>EnumValueDataType}} rawValue = reader.{{>EnumJsonReaderMethod}}();
179+
{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} result = ({{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}})rawValue;
180+
return result;
181+
{{/isString}}
182+
{{#isString}}
162183
string{{nrt?}} rawValue = reader.GetString();
163184

164185
{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}? result = rawValue == null
@@ -169,6 +190,7 @@
169190
return result.Value;
170191

171192
throw new JsonException();
193+
{{/isString}}
172194
}
173195

174196
/// <summary>
@@ -179,7 +201,15 @@
179201
/// <param name="options"></param>
180202
public override void Write(Utf8JsonWriter writer, {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}? {{#lambda.camelcase_sanitize_param}}{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{/lambda.camelcase_sanitize_param}}, JsonSerializerOptions options)
181203
{
204+
{{^isString}}
205+
if ({{#lambda.camelcase_sanitize_param}}{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{/lambda.camelcase_sanitize_param}}.HasValue)
206+
writer.WriteNumberValue({{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{/lambda.camelcase_sanitize_param}}.Value));
207+
else
208+
writer.WriteNullValue();
209+
{{/isString}}
210+
{{#isString}}
182211
writer.WriteStringValue({{#lambda.camelcase_sanitize_param}}{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{/lambda.camelcase_sanitize_param}}.HasValue ? {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}{{/lambda.camelcase_sanitize_param}}.Value).ToString() : "null");
212+
{{/isString}}
183213
}
184214
}
185215
{{/useGenericHost}}

modules/openapi-generator/src/test/java/org/openapitools/codegen/csharpnetcore/CSharpClientCodegenTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,4 +260,54 @@ private List<String> getNames(List<CodegenProperty> props) {
260260
if (props == null) return null;
261261
return props.stream().map(v -> v.name).collect(Collectors.toList());
262262
}
263+
264+
@Test
265+
public void testIntegerEnumJsonConverterUsesNumericOperations() throws IOException {
266+
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
267+
output.deleteOnExit();
268+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/csharp/integer-enum.yaml");
269+
final DefaultGenerator defaultGenerator = new DefaultGenerator();
270+
final ClientOptInput clientOptInput = new ClientOptInput();
271+
clientOptInput.openAPI(openAPI);
272+
CSharpClientCodegen cSharpClientCodegen = new CSharpClientCodegen();
273+
cSharpClientCodegen.setLibrary("generichost");
274+
cSharpClientCodegen.setOutputDir(output.getAbsolutePath());
275+
clientOptInput.config(cSharpClientCodegen);
276+
defaultGenerator.opts(clientOptInput);
277+
278+
Map<String, File> files = defaultGenerator.generate().stream()
279+
.collect(Collectors.toMap(File::getPath, Function.identity()));
280+
281+
// Verify integer enum uses numeric JSON reader/writer
282+
File intEnumFile = files.get(Paths
283+
.get(output.getAbsolutePath(), "src", "Org.OpenAPITools", "Model", "IntegerEnum.cs")
284+
.toString()
285+
);
286+
assertNotNull(intEnumFile, "Could not find file for model: IntegerEnum");
287+
assertFileContains(intEnumFile.toPath(),
288+
"reader.GetInt32()",
289+
"writer.WriteNumberValue(",
290+
"public static int ToJsonValue(IntegerEnum value)"
291+
);
292+
assertFileNotContains(intEnumFile.toPath(),
293+
"reader.GetString()",
294+
"writer.WriteStringValue("
295+
);
296+
297+
// Verify long enum uses int64 reader
298+
File longEnumFile = files.get(Paths
299+
.get(output.getAbsolutePath(), "src", "Org.OpenAPITools", "Model", "LongEnum.cs")
300+
.toString()
301+
);
302+
assertNotNull(longEnumFile, "Could not find file for model: LongEnum");
303+
assertFileContains(longEnumFile.toPath(),
304+
"reader.GetInt64()",
305+
"writer.WriteNumberValue(",
306+
"public static long ToJsonValue(LongEnum value)"
307+
);
308+
assertFileNotContains(longEnumFile.toPath(),
309+
"reader.GetString()",
310+
"writer.WriteStringValue("
311+
);
312+
}
263313
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
openapi: 3.0.1
2+
info:
3+
title: Integer Enum Test
4+
version: v1
5+
paths:
6+
/some/api:
7+
get:
8+
tags:
9+
- Example
10+
operationId: ExampleApi
11+
responses:
12+
'200':
13+
description: OK
14+
content:
15+
application/json:
16+
schema:
17+
$ref: '#/components/schemas/IntegerEnum'
18+
components:
19+
schemas:
20+
IntegerEnum:
21+
enum:
22+
- 0
23+
- 1
24+
type: integer
25+
format: int32
26+
x-enum-varnames:
27+
- None
28+
- Some
29+
LongEnum:
30+
enum:
31+
- 0
32+
- 1
33+
type: integer
34+
format: int64
35+
x-enum-varnames:
36+
- None
37+
- Some

samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Model/EnumTestEnumInteger.cs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,9 @@ public class EnumTestEnumIntegerJsonConverter : JsonConverter<EnumTestEnumIntege
104104
/// <returns></returns>
105105
public override EnumTestEnumInteger Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
106106
{
107-
string rawValue = reader.GetString();
108-
109-
EnumTestEnumInteger? result = rawValue == null
110-
? null
111-
: EnumTestEnumIntegerValueConverter.FromStringOrDefault(rawValue);
112-
113-
if (result != null)
114-
return result.Value;
115-
116-
throw new JsonException();
107+
int rawValue = reader.GetInt32();
108+
EnumTestEnumInteger result = (EnumTestEnumInteger)rawValue;
109+
return result;
117110
}
118111

119112
/// <summary>
@@ -124,7 +117,7 @@ public override EnumTestEnumInteger Read(ref Utf8JsonReader reader, Type typeToC
124117
/// <param name="options"></param>
125118
public override void Write(Utf8JsonWriter writer, EnumTestEnumInteger enumTestEnumInteger, JsonSerializerOptions options)
126119
{
127-
writer.WriteStringValue(EnumTestEnumIntegerValueConverter.ToJsonValue(enumTestEnumInteger).ToString());
120+
writer.WriteNumberValue(EnumTestEnumIntegerValueConverter.ToJsonValue(enumTestEnumInteger));
128121
}
129122
}
130123

@@ -142,16 +135,12 @@ public class EnumTestEnumIntegerNullableJsonConverter : JsonConverter<EnumTestEn
142135
/// <returns></returns>
143136
public override EnumTestEnumInteger? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
144137
{
145-
string rawValue = reader.GetString();
146-
147-
EnumTestEnumInteger? result = rawValue == null
148-
? null
149-
: EnumTestEnumIntegerValueConverter.FromStringOrDefault(rawValue);
150-
151-
if (result != null)
152-
return result.Value;
138+
if (reader.TokenType == JsonTokenType.Null)
139+
return null;
153140

154-
throw new JsonException();
141+
int rawValue = reader.GetInt32();
142+
EnumTestEnumInteger result = (EnumTestEnumInteger)rawValue;
143+
return result;
155144
}
156145

157146
/// <summary>
@@ -162,7 +151,10 @@ public class EnumTestEnumIntegerNullableJsonConverter : JsonConverter<EnumTestEn
162151
/// <param name="options"></param>
163152
public override void Write(Utf8JsonWriter writer, EnumTestEnumInteger? enumTestEnumInteger, JsonSerializerOptions options)
164153
{
165-
writer.WriteStringValue(enumTestEnumInteger.HasValue ? EnumTestEnumIntegerValueConverter.ToJsonValue(enumTestEnumInteger.Value).ToString() : "null");
154+
if (enumTestEnumInteger.HasValue)
155+
writer.WriteNumberValue(EnumTestEnumIntegerValueConverter.ToJsonValue(enumTestEnumInteger.Value));
156+
else
157+
writer.WriteNullValue();
166158
}
167159
}
168160
}

samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Model/EnumTestEnumIntegerOnly.cs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,9 @@ public class EnumTestEnumIntegerOnlyJsonConverter : JsonConverter<EnumTestEnumIn
104104
/// <returns></returns>
105105
public override EnumTestEnumIntegerOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
106106
{
107-
string rawValue = reader.GetString();
108-
109-
EnumTestEnumIntegerOnly? result = rawValue == null
110-
? null
111-
: EnumTestEnumIntegerOnlyValueConverter.FromStringOrDefault(rawValue);
112-
113-
if (result != null)
114-
return result.Value;
115-
116-
throw new JsonException();
107+
int rawValue = reader.GetInt32();
108+
EnumTestEnumIntegerOnly result = (EnumTestEnumIntegerOnly)rawValue;
109+
return result;
117110
}
118111

119112
/// <summary>
@@ -124,7 +117,7 @@ public override EnumTestEnumIntegerOnly Read(ref Utf8JsonReader reader, Type typ
124117
/// <param name="options"></param>
125118
public override void Write(Utf8JsonWriter writer, EnumTestEnumIntegerOnly enumTestEnumIntegerOnly, JsonSerializerOptions options)
126119
{
127-
writer.WriteStringValue(EnumTestEnumIntegerOnlyValueConverter.ToJsonValue(enumTestEnumIntegerOnly).ToString());
120+
writer.WriteNumberValue(EnumTestEnumIntegerOnlyValueConverter.ToJsonValue(enumTestEnumIntegerOnly));
128121
}
129122
}
130123

@@ -142,16 +135,12 @@ public class EnumTestEnumIntegerOnlyNullableJsonConverter : JsonConverter<EnumTe
142135
/// <returns></returns>
143136
public override EnumTestEnumIntegerOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
144137
{
145-
string rawValue = reader.GetString();
146-
147-
EnumTestEnumIntegerOnly? result = rawValue == null
148-
? null
149-
: EnumTestEnumIntegerOnlyValueConverter.FromStringOrDefault(rawValue);
150-
151-
if (result != null)
152-
return result.Value;
138+
if (reader.TokenType == JsonTokenType.Null)
139+
return null;
153140

154-
throw new JsonException();
141+
int rawValue = reader.GetInt32();
142+
EnumTestEnumIntegerOnly result = (EnumTestEnumIntegerOnly)rawValue;
143+
return result;
155144
}
156145

157146
/// <summary>
@@ -162,7 +151,10 @@ public class EnumTestEnumIntegerOnlyNullableJsonConverter : JsonConverter<EnumTe
162151
/// <param name="options"></param>
163152
public override void Write(Utf8JsonWriter writer, EnumTestEnumIntegerOnly? enumTestEnumIntegerOnly, JsonSerializerOptions options)
164153
{
165-
writer.WriteStringValue(enumTestEnumIntegerOnly.HasValue ? EnumTestEnumIntegerOnlyValueConverter.ToJsonValue(enumTestEnumIntegerOnly.Value).ToString() : "null");
154+
if (enumTestEnumIntegerOnly.HasValue)
155+
writer.WriteNumberValue(EnumTestEnumIntegerOnlyValueConverter.ToJsonValue(enumTestEnumIntegerOnly.Value));
156+
else
157+
writer.WriteNullValue();
166158
}
167159
}
168160
}

samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Model/OuterEnumInteger.cs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,9 @@ public class OuterEnumIntegerJsonConverter : JsonConverter<OuterEnumInteger>
115115
/// <returns></returns>
116116
public override OuterEnumInteger Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
117117
{
118-
string rawValue = reader.GetString();
119-
120-
OuterEnumInteger? result = rawValue == null
121-
? null
122-
: OuterEnumIntegerValueConverter.FromStringOrDefault(rawValue);
123-
124-
if (result != null)
125-
return result.Value;
126-
127-
throw new JsonException();
118+
int rawValue = reader.GetInt32();
119+
OuterEnumInteger result = (OuterEnumInteger)rawValue;
120+
return result;
128121
}
129122

130123
/// <summary>
@@ -135,7 +128,7 @@ public override OuterEnumInteger Read(ref Utf8JsonReader reader, Type typeToConv
135128
/// <param name="options"></param>
136129
public override void Write(Utf8JsonWriter writer, OuterEnumInteger outerEnumInteger, JsonSerializerOptions options)
137130
{
138-
writer.WriteStringValue(OuterEnumIntegerValueConverter.ToJsonValue(outerEnumInteger).ToString());
131+
writer.WriteNumberValue(OuterEnumIntegerValueConverter.ToJsonValue(outerEnumInteger));
139132
}
140133
}
141134

@@ -153,16 +146,12 @@ public class OuterEnumIntegerNullableJsonConverter : JsonConverter<OuterEnumInte
153146
/// <returns></returns>
154147
public override OuterEnumInteger? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
155148
{
156-
string rawValue = reader.GetString();
157-
158-
OuterEnumInteger? result = rawValue == null
159-
? null
160-
: OuterEnumIntegerValueConverter.FromStringOrDefault(rawValue);
161-
162-
if (result != null)
163-
return result.Value;
149+
if (reader.TokenType == JsonTokenType.Null)
150+
return null;
164151

165-
throw new JsonException();
152+
int rawValue = reader.GetInt32();
153+
OuterEnumInteger result = (OuterEnumInteger)rawValue;
154+
return result;
166155
}
167156

168157
/// <summary>
@@ -173,7 +162,10 @@ public class OuterEnumIntegerNullableJsonConverter : JsonConverter<OuterEnumInte
173162
/// <param name="options"></param>
174163
public override void Write(Utf8JsonWriter writer, OuterEnumInteger? outerEnumInteger, JsonSerializerOptions options)
175164
{
176-
writer.WriteStringValue(outerEnumInteger.HasValue ? OuterEnumIntegerValueConverter.ToJsonValue(outerEnumInteger.Value).ToString() : "null");
165+
if (outerEnumInteger.HasValue)
166+
writer.WriteNumberValue(OuterEnumIntegerValueConverter.ToJsonValue(outerEnumInteger.Value));
167+
else
168+
writer.WriteNullValue();
177169
}
178170
}
179171
}

0 commit comments

Comments
 (0)