generateOneOfClass function
Generates a sealed class hierarchy for types with oneOf fields.
The base sealed class contains all non-oneOf fields plus an abstract
toJson. Each oneOf field becomes a final class variant that extends the
base and carries that one field as required. A factory fromJson on the
base dispatches to the correct variant by checking which key is present.
Generates a single class with named constructors for oneOf types.
Each oneOf field gets a named constructor (e.g. .oidcToken(...)) that
initialises that field as required and null-initialises all other oneOf
fields. Base fields appear in every constructor. This mirrors the JS
codegen's ONEOF_FIELDS treatment while staying idiomatic Dart.
Implementation
String generateOneOfClass(
String className,
Map<String, dynamic> def,
Map<String, dynamic> allDefs,
List<String> oneofFields,
) {
final props = (def['properties'] as Map?)?.cast<String, dynamic>() ?? {};
final requiredList = (def['required'] is List)
? (def['required'] as List).cast<String>()
: <String>[];
final oneofSet = Set<String>.from(oneofFields);
final baseFields = <FieldSpec>[];
final variantSpecs = <String, FieldSpec>{};
for (final entry in props.entries) {
final jsonKey = entry.key;
final schema = (entry.value as Map).cast<String, dynamic>();
String dartType = 'dynamic';
if (schema[r'$ref'] is String) {
dartType = refToName(schema[r'$ref'] as String);
} else if (schema['type'] is String) {
dartType = typeToDart(schema['type'] as String, schema);
}
final isRequired = requiredList.contains(jsonKey);
final fieldName = sanitizeFieldName(jsonKey);
if (oneofSet.contains(jsonKey)) {
variantSpecs[jsonKey] = FieldSpec(
fieldName: fieldName,
jsonKey: jsonKey,
type: dartType, // non-nullable in constructors, nullable as field
nullable: true,
description: schema['description'] as String?,
);
} else {
baseFields.add(FieldSpec(
fieldName: fieldName,
jsonKey: jsonKey,
type: isRequired ? dartType : '$dartType?',
nullable: !isRequired,
description: schema['description'] as String?,
));
}
}
final b = StringBuffer();
b.writeln('class $className {');
// Base fields
for (final f in baseFields) {
if ((f.description ?? '').isNotEmpty) {
b.writeln(' /// ${sanitizeDocComment(f.description)}');
}
b.writeln(' final ${f.type} ${f.fieldName};');
}
// oneOf fields — nullable on the class itself
for (final jsonKey in oneofFields) {
final spec = variantSpecs[jsonKey]!;
if ((spec.description ?? '').isNotEmpty) {
b.writeln(' /// ${sanitizeDocComment(spec.description)}');
}
b.writeln(' final ${spec.type}? ${spec.fieldName};');
}
b.writeln();
// Named constructors — one per oneOf variant
for (final jsonKey in oneofFields) {
final spec = variantSpecs[jsonKey]!;
b.writeln(' const $className.${spec.fieldName}({');
for (final f in baseFields) {
final req = f.nullable ? '' : 'required ';
b.writeln(' $req this.${f.fieldName},');
}
b.writeln(' required ${spec.type} this.${spec.fieldName},');
b.write(' })');
final others = oneofFields.where((k) => k != jsonKey).toList();
if (others.isNotEmpty) {
b.write(' : ');
b.write(
others.map((k) => '${variantSpecs[k]!.fieldName} = null').join(', '));
}
b.writeln(';');
b.writeln();
}
// fromJson factory
b.writeln(' factory $className.fromJson(Map<String, dynamic> json) {');
for (final jsonKey in oneofFields) {
final spec = variantSpecs[jsonKey]!;
b.writeln(" if (json['$jsonKey'] != null) {");
b.writeln(' return $className.${spec.fieldName}(');
for (final f in baseFields) {
final read = "json['${f.jsonKey}']";
final parse = f.type.endsWith('?')
? _jsonReadExpr(
f.type.substring(0, f.type.length - 1), read, allDefs, false)
: _jsonReadExpr(f.type, read, allDefs, true);
b.writeln(' ${f.fieldName}: $parse,');
}
final variantParse =
_jsonReadExpr(spec.type, "json['$jsonKey']", allDefs, true);
b.writeln(' ${spec.fieldName}: $variantParse,');
b.writeln(' );');
b.writeln(' }');
}
b.writeln(
" throw ArgumentError('$className: none of the oneOf fields "
"(${oneofFields.join(', ')}) are present in json');",
);
b.writeln(' }\n');
// toJson
b.writeln(' Map<String, dynamic> toJson() {');
b.writeln(' final _json = <String, dynamic>{};');
for (final f in baseFields) {
final writeExpr = _jsonWriteExpr(f.type, f.fieldName, allDefs);
if (f.nullable) {
b.writeln(" if (${f.fieldName} != null) {");
b.writeln(" _json['${f.jsonKey}'] = $writeExpr;");
b.writeln(' }');
} else {
b.writeln(" _json['${f.jsonKey}'] = $writeExpr;");
}
}
for (final jsonKey in oneofFields) {
final spec = variantSpecs[jsonKey]!;
final writeExpr = _jsonWriteExpr('${spec.type}?', spec.fieldName, allDefs);
b.writeln(" if (${spec.fieldName} != null) {");
b.writeln(" _json['$jsonKey'] = $writeExpr;");
b.writeln(' }');
}
b.writeln(' return _json;');
b.writeln(' }');
b.writeln('}\n');
return b.toString();
}