Skip to content
Open
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
45 changes: 45 additions & 0 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- [Where can I learn more about JSON Schema?](#where-can-i-learn-more-about-json-schema)
- [I'd like to customize the output for my particular application.](#id-like-to-customize-the-output-for-my-particular-application)
- [How can I control the property order in JSON Schema?](#how-can-i-control-the-property-order-in-json-schema)
- [Does quicktype support default values from JSON Schema?](#does-quicktype-support-default-values-from-json-schema)
- [quicktype is awesome, I'd like to support it!](#quicktype-is-awesome-id-like-to-support-it)
- [How is this different from other JSON converters?](#how-is-quicktype-different-from-other-json-converters)

Expand Down Expand Up @@ -140,3 +141,47 @@ There are many ways you can support quicktype:
- Tell all your friends about it! Show it around, tweet about it, write a blog post, present it at a lightning talk.

- quicktype is open source - please contribute! We need documentation at least as much as code, so you don't need strong coding skills to make an impact. If you do, we have [lots of open issues](https://github.com/quicktype/quicktype/issues) that need resolving, almost all of our target languages can be improved, and there are many, many programming languages that quicktype doesn't support yet. Talk to us [on Slack](http://slack.quicktype.io) if you're interested - we're always happy to help.

## Does quicktype support default values from JSON Schema?

Yes! quicktype supports default values from JSON Schema for Dart language and generates appropriate default values in constructors.

For example, with this JSON Schema:

```json
{
"type": "object",
"properties": {
"name": {
"type": "string",
"default": "John Doe"
},
"age": {
"type": "integer",
"default": 25
},
"isActive": {
"type": "boolean",
"default": true
}
}
}
```

quicktype will generate Dart code with default values in constructors. This generates:

```dart
class Example {
final String name;
final int age;
final bool isActive;

Example({
String name = "John Doe",
int age = 25,
bool isActive = true,
});
}
```

Default values work with all supported types including strings, numbers, booleans, arrays, and objects in Dart. This makes your generated Dart models more convenient to use and reduces boilerplate code. Support for other languages may be added in future versions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,32 @@ quicktype https://api.somewhere.com/data -o Data.java

The recommended way to use `quicktype` is to generate a JSON schema from sample data, review and edit the schema, commit the schema to your project repo, then generate code from the schema as part of your build process:

#### Default Values Support

`quicktype` supports default values from JSON Schema for Dart, automatically generating appropriate default values in constructors:

```json
{
"type": "object",
"properties": {
"name": {
"type": "string",
"default": "John Doe"
},
"age": {
"type": "integer",
"default": 25
},
"isActive": {
"type": "boolean",
"default": true
}
}
}
```

This generates Dart code with default values in constructors, making your generated models more convenient to use. Default values support is currently available for Dart language.

```bash
# First, infer a JSON schema from a sample.
quicktype pokedex.json -l schema -o schema.json
Expand Down
8 changes: 4 additions & 4 deletions packages/quicktype-core/src/GraphRewriting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class TypeReconstituter<TBuilder extends BaseGraphRewriteBuilder> {
private readonly _typeAttributes: TypeAttributes,
private readonly _forwardingRef: TypeRef | undefined,
private readonly _register: (tref: TypeRef) => void,
) {}
) { }

private builderForNewType(): TBuilder {
assert(!this._wasUsed, "TypeReconstituter used more than once");
Expand Down Expand Up @@ -211,8 +211,9 @@ export class TypeReconstituter<TBuilder extends BaseGraphRewriteBuilder> {
public makeClassProperty(
tref: TypeRef,
isOptional: boolean,
defaultValue: unknown = undefined,
): ClassProperty {
return this._typeBuilder.makeClassProperty(tref, isOptional);
return this._typeBuilder.makeClassProperty(tref, isOptional, defaultValue);
}

public getObjectType(
Expand Down Expand Up @@ -333,8 +334,7 @@ export class TypeReconstituter<TBuilder extends BaseGraphRewriteBuilder> {

export abstract class BaseGraphRewriteBuilder
extends TypeBuilder
implements TypeLookerUp
{
implements TypeLookerUp {
protected readonly reconstitutedTypes: Map<number, TypeRef> = new Map();

private _lostTypeAttributes = false;
Expand Down
17 changes: 11 additions & 6 deletions packages/quicktype-core/src/Type/Type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export abstract class Type {
public constructor(
public readonly typeRef: TypeRef,
protected readonly graph: TypeGraph,
) {}
) { }

public get index(): number {
return typeRefIndex(this.typeRef);
Expand Down Expand Up @@ -235,7 +235,7 @@ export abstract class Type {
const workList: Type[] = [this];
const processed = new Set<Type>();
const ancestors = new Set<Type>();
for (;;) {
for (; ;) {
const t = workList.pop();
if (t === undefined) break;

Expand Down Expand Up @@ -400,7 +400,8 @@ export class GenericClassProperty<T> {
public constructor(
public readonly typeData: T,
public readonly isOptional: boolean,
) {}
public readonly defaultValue: unknown = undefined,
) { }

public equals(other: GenericClassProperty<unknown>): boolean {
if (!(other instanceof GenericClassProperty)) {
Expand All @@ -409,12 +410,13 @@ export class GenericClassProperty<T> {

return (
areEqual(this.typeData, other.typeData) &&
this.isOptional === other.isOptional
this.isOptional === other.isOptional &&
areEqual(this.defaultValue, other.defaultValue)
);
}

public hashCode(): number {
return hashCodeOf(this.typeData) + (this.isOptional ? 17 : 23);
return hashCodeOf(this.typeData) + (this.isOptional ? 17 : 23) + (this.defaultValue !== undefined ? 31 : 0);
}
}

Expand All @@ -423,8 +425,9 @@ export class ClassProperty extends GenericClassProperty<TypeRef> {
typeRef: TypeRef,
public readonly graph: TypeGraph,
isOptional: boolean,
defaultValue: unknown = undefined,
) {
super(typeRef, isOptional);
super(typeRef, isOptional, defaultValue);
}

public get typeRef(): TypeRef {
Expand Down Expand Up @@ -591,6 +594,7 @@ export class ObjectType extends Type {
builder.makeClassProperty(
defined(maybePropertyTypes.get(n)),
cp.isOptional,
cp.defaultValue,
),
);

Expand Down Expand Up @@ -639,6 +643,7 @@ export class ObjectType extends Type {
builder.makeClassProperty(
defined(reconstitutedTypes.get(n)),
cp.isOptional,
cp.defaultValue,
),
);
const additionalProperties = definedMap(
Expand Down
5 changes: 3 additions & 2 deletions packages/quicktype-core/src/Type/TypeBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,9 @@ export class TypeBuilder {
public makeClassProperty(
tref: TypeRef,
isOptional: boolean,
defaultValue: unknown = undefined,
): ClassProperty {
return new ClassProperty(tref, this.typeGraph, isOptional);
return new ClassProperty(tref, this.typeGraph, isOptional, defaultValue);
}

public getUniqueObjectType(
Expand Down Expand Up @@ -523,7 +524,7 @@ export class TypeBuilder {

if (this._allPropertiesOptional) {
properties = mapMap(properties, (cp) =>
this.makeClassProperty(cp.typeRef, true),
this.makeClassProperty(cp.typeRef, true, cp.defaultValue),
);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/quicktype-core/src/Type/TypeGraphUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function optionalToNullable(
ref = builder.getUnionType(attributes, members);
}

return builder.makeClassProperty(ref, p.isOptional);
return builder.makeClassProperty(ref, p.isOptional, p.defaultValue);
});
if (c.isFixed) {
return builder.getUniqueClassType(
Expand Down
2 changes: 1 addition & 1 deletion packages/quicktype-core/src/UnifyClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function getCliqueProperties(
([name, types, isOptional]) => {
return [
name,
builder.makeClassProperty(makePropertyType(types), isOptional),
builder.makeClassProperty(makePropertyType(types), isOptional, undefined),
] as [string, ClassProperty];
},
);
Expand Down
4 changes: 2 additions & 2 deletions packages/quicktype-core/src/input/Inference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class TypeInference {
private readonly _typeBuilder: TypeBuilder,
private readonly _inferMaps: boolean,
private readonly _inferEnums: boolean,
) {}
) { }

private addValuesToAccumulator(
valueArray: NestedValueArray,
Expand Down Expand Up @@ -421,7 +421,7 @@ export class TypeInference {
const isOptional = values.length < objects.length;
properties.set(
key,
this._typeBuilder.makeClassProperty(t, isOptional),
this._typeBuilder.makeClassProperty(t, isOptional, undefined),
);
}

Expand Down
25 changes: 13 additions & 12 deletions packages/quicktype-core/src/input/JSONSchemaInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ function withRef<T extends object>(
typeof refOrLoc === "function"
? refOrLoc()
: refOrLoc instanceof Ref
? refOrLoc
: refOrLoc.canonicalRef;
? refOrLoc
: refOrLoc.canonicalRef;
return Object.assign({ ref }, props ?? {});
}

Expand Down Expand Up @@ -262,7 +262,7 @@ export class Ref {
public get name(): string {
const path = Array.from(this.path);

for (;;) {
for (; ;) {
const e = path.pop();
if (e === undefined || e.kind === PathElementKind.Root) {
let name =
Expand Down Expand Up @@ -499,7 +499,7 @@ class Canonizer {

private readonly _schemaAddressesAdded = new Set<string>();

public constructor(private readonly _ctx: RunContext) {}
public constructor(private readonly _ctx: RunContext) { }

private addIDs(schema: unknown, loc: Location): void {
if (schema === null) return;
Expand Down Expand Up @@ -690,7 +690,7 @@ class Resolver {
private readonly _ctx: RunContext,
private readonly _store: JSONSchemaStore,
private readonly _canonizer: Canonizer,
) {}
) { }

private async tryResolveVirtualRef(
fetchBase: Location,
Expand All @@ -702,7 +702,7 @@ class Resolver {
// we don't know its $id mapping yet, which means we don't know where we
// will end up. What we do if we encounter a new schema is add all its
// IDs first, and then try to canonize again.
for (;;) {
for (; ;) {
const loc = this._canonizer.canonize(fetchBase, virtualRef);
const canonical = loc.canonicalRef;
assert(
Expand All @@ -715,9 +715,9 @@ class Resolver {
canonical.addressURI === undefined
? undefined
: await this._store.get(
address,
this._ctx.debugPrintSchemaResolving,
);
address,
this._ctx.debugPrintSchemaResolving,
);
if (schema === undefined) {
return [undefined, loc];
}
Expand Down Expand Up @@ -837,7 +837,8 @@ async function addTypesInSchema(
makeNamesTypeAttributes(propName, true),
);
const isOptional = !required.has(propName);
return typeBuilder.makeClassProperty(t, isOptional);
const defaultValue = propSchema.default;
return typeBuilder.makeClassProperty(t, isOptional, defaultValue);
},
);
let additionalPropertiesType: TypeRef | undefined;
Expand Down Expand Up @@ -872,7 +873,7 @@ async function addTypesInSchema(

const additionalProps = mapFromIterable(
additionalRequired,
(_name) => typeBuilder.makeClassProperty(t, false),
(_name) => typeBuilder.makeClassProperty(t, false, undefined),
);
mapMergeInto(props, additionalProps);
}
Expand Down Expand Up @@ -985,7 +986,7 @@ async function addTypesInSchema(
({ forType, forUnion, forCases }) => {
assert(
forUnion === undefined &&
forCases === undefined,
forCases === undefined,
"We can't have attributes for unions and cases if we don't have a union",
);
return forType;
Expand Down
Loading