Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e498c3c
cJSON - split generated code into source and header files
thives Jun 14, 2024
103bead
cJSON - add header-only option (default true) to opt in split header …
thives Jun 15, 2024
fa3b209
feat(kotlin): Support for date and datetime from JSON schema
nikhilunni Oct 31, 2025
5b905dc
Merge remote-tracking branch 'origin/master' into HEAD
schani Jul 9, 2026
0b819af
Merge master and resolve conflicts
schani Jul 9, 2026
379134e
Merge master into PR #2845 branch
schani Jul 12, 2026
3d45b9f
Kotlin/Jackson: make java.time date/time round-trip work
schani Jul 12, 2026
2d37aea
Kotlin/Klaxon: support date/time types from JSON schema
schani Jul 12, 2026
68516b4
Kotlin fixtures: skip misc JSONs whose date-times can't round-trip ja…
schani Jul 12, 2026
e19e14c
Merge remote-tracking branch 'origin/master' into kotlin-datetime-fix…
schani Jul 13, 2026
c194694
Drop kotlin skips for non-RFC3339 date-times, obsoleted by strict inf…
schani Jul 13, 2026
9b3fffe
fix(kotlin): emit kotlinx serializers for java.time types
schani Jul 19, 2026
7e64792
Merge remote-tracking branch 'origin/master' into kotlinx-datetime-fix
schani Jul 19, 2026
c700eed
Merge remote-tracking branch 'origin/master' into pr2845-update
schani Jul 19, 2026
347969b
test(kotlinx): enable date-time feature and adjust skips for date inf…
schani Jul 19, 2026
165d394
fix(kotlin): only infer date-times that java.time round-trips faithfully
schani Jul 19, 2026
3665111
Merge pull request #2845 from nikhilunni/master
schani Jul 19, 2026
0043ecc
fix(cjson): correct includes in generated source/header split
schani Jul 19, 2026
2d1c3dd
test(cjson): overhaul fixtures for the source/header split modes
schani Jul 19, 2026
42ce1cc
Merge remote-tracking branch 'origin/master' into cjson-split-fixes-2618
schani Jul 19, 2026
9726a94
test(cjson): unit-test include structure of split output
schani Jul 19, 2026
5a94eaf
Merge pull request #2618 from thives/cjson-split-c-h-files
schani Jul 19, 2026
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
1 change: 1 addition & 0 deletions .github/workflows/test-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
- javascript,schema-javascript
- golang,schema-golang
- cjson,schema-cjson
- cjson-default,cjson-multi-header,cjson-multi-split
- cplusplus,schema-cplusplus
- flow,schema-flow
- java,schema-java
Expand Down
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ test/golang/schema-from-schema.json
test/elm/elm-stuff/
test/elm/elm.js
test/elm/QuickType.elm
test/fixtures/cjson/cJSON.*
test/fixtures/cjson/hashtable.*
test/fixtures/cjson/list.*
test/fixtures/cjson/deps/
test/fixtures/rust/target
test/fixtures/java/target
test/fixtures/java-lombok/target
Expand Down
208 changes: 159 additions & 49 deletions packages/quicktype-core/src/language/CJSON/CJSONRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ import {
} from "./utils.js";

export class CJSONRenderer extends ConvenienceRenderer {
private currentFilename: string | undefined; /* Current filename */
private currentHeaderFilename:
| string
| undefined; /* Current header filename */

private currentSourceFilename:
| string
| undefined; /* Current source filename */

private readonly memberNameStyle: NameStyle; /* Member name style */

Expand Down Expand Up @@ -231,8 +237,8 @@ export class CJSONRenderer extends ConvenienceRenderer {
}

/**
* Function called to create header file(s)
* @param proposedFilename: source filename provided from stdin
* Function called to create header and source file(s)
* @param proposedFilename: source filename provided from stdin (without extensions)
*/
protected emitSourceStructure(proposedFilename: string): void {
/* Depending of source style option, generate a unique header or multiple header files */
Expand All @@ -244,12 +250,12 @@ export class CJSONRenderer extends ConvenienceRenderer {
}

/**
* Function called to create a single header file with types and generators
* Function called to create a single pair of header/source files with types and generators
* @param proposedFilename: source filename provided from stdin
*/
protected emitSingleSourceStructure(proposedFilename: string): void {
/* Create file */
this.startFile(proposedFilename);
/* Create header file */
this.startHeaderFile(proposedFilename);

/* Create types */
this.forEachDeclaration("leading-and-interposing", (decl) => {
Expand Down Expand Up @@ -306,6 +312,14 @@ export class CJSONRenderer extends ConvenienceRenderer {
(type) => this.namedTypeToNameForTopLevel(type) === undefined,
);

if (!this._options.headerOnly) {
/* Close header file */
this.finishHeaderFile();

/* Create source file */
this.startSourceFile(proposedFilename);
}

/* Create enum functions */
this.forEachEnum(
"leading-and-interposing",
Expand Down Expand Up @@ -333,8 +347,7 @@ export class CJSONRenderer extends ConvenienceRenderer {
(type) => this.namedTypeToNameForTopLevel(type) === undefined,
);

/* Close file */
this.finishFile();
this.finishCurrentFile();
}

/**
Expand Down Expand Up @@ -371,24 +384,31 @@ export class CJSONRenderer extends ConvenienceRenderer {
protected emitEnum(enumType: EnumType): void {
/* Create file */
const enumName = this.nameForNamedType(enumType);
const filename = this.sourcelikeToString(enumName).concat(".h");
this.includes.push(filename);
this.startFile(filename);
const headerFilename = this.sourcelikeToString(enumName).concat(".h");
this.includes.push(headerFilename);
this.startHeaderFile(headerFilename);

/* Create includes */
this.emitIncludes(enumType, this.sourcelikeToString(filename));
this.emitIncludes(enumType, this.sourcelikeToString(headerFilename));

/* Create types */
this.emitEnumTypedef(enumType);

/* Create prototypes */
this.emitEnumPrototypes(enumType);

if (!this._options.headerOnly) {
/* Close header file */
this.finishHeaderFile();

/* Create source file */
this.startSourceFile(headerFilename);
}

/* Create functions */
this.emitEnumFunctions(enumType);

/* Close file */
this.finishFile();
this.finishCurrentFile();
}

/**
Expand Down Expand Up @@ -548,24 +568,31 @@ export class CJSONRenderer extends ConvenienceRenderer {
protected emitUnion(unionType: UnionType): void {
/* Create file */
const unionName = this.nameForNamedType(unionType);
const filename = this.sourcelikeToString(unionName).concat(".h");
this.includes.push(filename);
this.startFile(filename);
const headerFilename = this.sourcelikeToString(unionName).concat(".h");
this.includes.push(headerFilename);
this.startHeaderFile(headerFilename);

/* Create includes */
this.emitIncludes(unionType, this.sourcelikeToString(filename));
this.emitIncludes(unionType, this.sourcelikeToString(headerFilename));

/* Create types */
this.emitUnionTypedef(unionType);

/* Create prototypes */
this.emitUnionPrototypes(unionType);

if (!this._options.headerOnly) {
/* Close header file */
this.finishHeaderFile();

/* Create source file */
this.startSourceFile(headerFilename);
}

/* Create functions */
this.emitUnionFunctions(unionType);

/* Close file */
this.finishFile();
this.finishCurrentFile();
}

/**
Expand Down Expand Up @@ -1991,24 +2018,31 @@ export class CJSONRenderer extends ConvenienceRenderer {
protected emitClass(classType: ClassType): void {
/* Create file */
const className = this.nameForNamedType(classType);
const filename = this.sourcelikeToString(className).concat(".h");
this.includes.push(filename);
this.startFile(filename);
const headerFilename = this.sourcelikeToString(className).concat(".h");
this.includes.push(headerFilename);
this.startHeaderFile(headerFilename);

/* Create includes */
this.emitIncludes(classType, this.sourcelikeToString(filename));
this.emitIncludes(classType, this.sourcelikeToString(headerFilename));

/* Create types */
this.emitClassTypedef(classType);

/* Create prototypes */
this.emitClassPrototypes(classType);

if (!this._options.headerOnly) {
/* Close header file */
this.finishHeaderFile();

/* Create source file */
this.startSourceFile(headerFilename);
}

/* Create functions */
this.emitClassFunctions(classType);

/* Close file */
this.finishFile();
this.finishCurrentFile();
}

/**
Expand Down Expand Up @@ -4598,8 +4632,8 @@ export class CJSONRenderer extends ConvenienceRenderer {
includes: string[],
): void {
/* Create file */
const filename = this.sourcelikeToString(className).concat(".h");
this.startFile(filename);
const headerFilename = this.sourcelikeToString(className).concat(".h");
this.startHeaderFile(headerFilename);

/* Create includes - This create too much includes but this is safer because of specific corner cases */
includes.forEach((name) => {
Expand All @@ -4613,11 +4647,18 @@ export class CJSONRenderer extends ConvenienceRenderer {
/* Create prototypes */
this.emitTopLevelPrototypes(type, className);

if (!this._options.headerOnly) {
/* Close header file */
this.finishHeaderFile();

/* Create source file */
this.startSourceFile(headerFilename);
}

/* Create functions */
this.emitTopLevelFunctions(type, className);

/* Close file */
this.finishFile();
this.finishCurrentFile();
}

/**
Expand Down Expand Up @@ -5535,24 +5576,25 @@ export class CJSONRenderer extends ConvenienceRenderer {
}

/**
* Function called to create a file
* Function called to create a header file
* @param proposedFilename: source filename provided from stdin
*/
protected startFile(proposedFilename: Sourcelike): void {
/* Check if previous file is closed, create a new file */
protected startHeaderFile(proposedFilename: Sourcelike): void {
/* Check if previous header file is closed, create a new file */
assert(
this.currentFilename === undefined,
"Previous file wasn't finished",
this.currentHeaderFilename === undefined,
"Previous header file wasn't finished",
);
if (proposedFilename !== undefined) {
this.currentFilename = this.sourcelikeToString(proposedFilename);
this.currentHeaderFilename =
this.sourcelikeToString(proposedFilename);
}

/* Check if file has been created */
if (this.currentFilename !== undefined) {
/* Check if header file has been created */
if (this.currentHeaderFilename !== undefined) {
/* Write header */
this.emitDescriptionBlock([
this.currentFilename,
this.currentHeaderFilename,
"This file has been autogenerated using quicktype https://github.com/quicktype/quicktype - DO NOT EDIT",
"This file depends of https://github.com/DaveGamble/cJSON, https://github.com/joelguittet/c-list and https://github.com/joelguittet/c-hashtable",
"To parse json data from json string use the following: struct <type> * data = cJSON_Parse<type>(<string>);",
Expand All @@ -5567,7 +5609,7 @@ export class CJSONRenderer extends ConvenienceRenderer {
this.emitLine(
"#ifndef __",
allUpperWordStyle(
this.currentFilename.replace(
this.currentHeaderFilename.replace(
new RegExp(/[^a-zA-Z0-9]+/, "g"),
"_",
),
Expand All @@ -5577,7 +5619,7 @@ export class CJSONRenderer extends ConvenienceRenderer {
this.emitLine(
"#define __",
allUpperWordStyle(
this.currentFilename.replace(
this.currentHeaderFilename.replace(
new RegExp(/[^a-zA-Z0-9]+/, "g"),
"_",
),
Expand Down Expand Up @@ -5617,11 +5659,42 @@ export class CJSONRenderer extends ConvenienceRenderer {
}

/**
* Function called to close current file
* Function called to create a source file
* @param headerFilename: filename of the header file corresponding to this source file
*/
protected startSourceFile(headerFilename: Sourcelike): void {
/* Check if previous source file is closed, create a new file */
assert(
this.currentSourceFilename === undefined,
"Previous source file wasn't finished",
);
if (headerFilename !== undefined) {
this.currentSourceFilename = this.getSourceNameFromHeaderName(
this.sourcelikeToString(headerFilename),
);
}

/* Check if source file has been created */
if (this.currentSourceFilename !== undefined) {
/* Write header */
this.emitDescriptionBlock([
this.currentSourceFilename,
"This file has been autogenerated using quicktype https://github.com/quicktype/quicktype - DO NOT EDIT",
]);
this.ensureBlankLine();

/* Include corresponding header file */
this.emitIncludeLine(this.sourcelikeToString(headerFilename));
this.ensureBlankLine();
}
}

/**
* Function called to close current header file
*/
protected finishFile(): void {
/* Check if file has been created */
if (this.currentFilename !== undefined) {
protected finishHeaderFile(): void {
/* Check if header file has been created */
if (this.currentHeaderFilename !== undefined) {
/* Write C++ guard */
this.emitLine("#ifdef __cplusplus");
this.emitLine("}");
Expand All @@ -5632,7 +5705,7 @@ export class CJSONRenderer extends ConvenienceRenderer {
this.emitLine(
"#endif /* __",
allUpperWordStyle(
this.currentFilename.replace(
this.currentHeaderFilename.replace(
new RegExp(/[^a-zA-Z0-9]+/, "g"),
"_",
),
Expand All @@ -5641,9 +5714,37 @@ export class CJSONRenderer extends ConvenienceRenderer {
);
this.ensureBlankLine();

/* Close file */
super.finishFile(defined(this.currentFilename));
this.currentFilename = undefined;
/* Close header file */
super.finishFile(defined(this.currentHeaderFilename));
this.currentHeaderFilename = undefined;
}
}

/**
* Function called to close current source file
*/
protected finishSourceFile(): void {
/* Check if source file has been created */
if (this.currentSourceFilename !== undefined) {
this.ensureBlankLine();

/* Close source file */
super.finishFile(defined(this.currentSourceFilename));
this.currentSourceFilename = undefined;
}
}

/**
* Function called to close the current file, either the header file when
* generating headers only, or the source file otherwise
*/
protected finishCurrentFile(): void {
if (this._options.headerOnly) {
/* Close header file */
this.finishHeaderFile();
} else {
/* Close source file */
this.finishSourceFile();
}
}

Expand Down Expand Up @@ -5909,4 +6010,13 @@ export class CJSONRenderer extends ConvenienceRenderer {
recur(false, false, 0, type);
return result;
}

/**
* Get the name of the source file corresponding to a header file
* @param headerName: header filename
* @return Source filename
*/
protected getSourceNameFromHeaderName(headerName: string): string {
return headerName.replace(/\.h$/, ".c");
}
}
Loading
Loading