guess-json-shape
Given a lump of JSON, this module will try to guess the shape of the JSON,
returning a structure that describes the types and structure of the data.
This can be useful when writing tools that perform transformations such as
"json-to-typescript" or "json-to-runtypes"
The library is able make reasonable guesses about the structure of objects in
arrays, including nullable fields.
The way most of the guessing is done is heavily inspired by
json-to-ts.
Quick start
import { guess } from 'guess-json-shape';
const jsonData = {
data: {
articles: [
{ id: '1', slug: 'tutorial', body: 'text here', published: true },
{ id: '2', slug: 'intermediate', body: 'text here', tags: ['docs'] },
],
},
links: {
self: 'http://example.com/articles',
next: 'http://example.com/articles?page=2',
last: 'http://example.com/articles?page=10',
},
};
const guessed = guess(jsonData);
The value of guessed
is the following:
[
{
name: 'Articles',
isRoot: false,
type: {
kind: 'object',
fields: [
{
name: 'id',
nullable: false,
type: { kind: 'primitive', type: 'string' },
},
{
name: 'slug',
nullable: false,
type: { kind: 'primitive', type: 'string' },
},
{
name: 'body',
nullable: false,
type: { kind: 'primitive', type: 'string' },
},
{
name: 'published',
nullable: true,
type: { kind: 'primitive', type: 'boolean' },
},
{
name: 'tags',
nullable: true,
type: {
kind: 'array',
type: {
kind: 'union',
types: [{ kind: 'primitive', type: 'string' }],
},
},
},
],
},
},
{
name: 'Data',
isRoot: false,
type: {
kind: 'object',
fields: [
{
name: 'articles',
type: {
kind: 'array',
type: {
kind: 'union',
types: [{ kind: 'named', name: 'Articles' }],
},
},
},
],
},
},
{
name: 'Links',
isRoot: false,
type: {
kind: 'object',
fields: [
{ name: 'self', type: { kind: 'primitive', type: 'string' } },
{ name: 'next', type: { kind: 'primitive', type: 'string' } },
{ name: 'last', type: { kind: 'primitive', type: 'string' } },
],
},
},
{
name: 'Root',
isRoot: true,
type: {
kind: 'object',
fields: [
{ name: 'data', type: { kind: 'named', name: 'Data' } },
{ name: 'links', type: { kind: 'named', name: 'Links' } },
],
},
},
];
The structure can be used to create for example type definitions. If you wrote
code to convert the above to typescript, it would look like this:
type Articles = {
id: string;
slug: string;
body: string;
published?: boolean;
tags?: Array<string>;
};
type Data = {
articles: Array<Articles>;
};
type Links = {
self: string;
next: string;
last: string;
};
type Root = {
data: Data;
links: Links;
};
API
A single function is exposed: guess(json)
. It takes a single argument, that
should be some parsed JSON. It returns an array of JsonType
objects that
represent the structure of the parsed JSON. See the
Cabeats and known issues
-
Discriminated unions are not detected. So all the candidate object shapes will
be merged into a single object that is mostly wrong. For example:
guess([
{ kind: 'user', name: 'Rune', passwordHash: 'some-hash' },
{ kind: 'bot', id: 'automation-bot', apiKey: 'some-key' },
]);
Will be detected like this:
type Guessed = {
kind: string;
name?: string;
passwordHash?: string;
id?: string;
apiKey?: string;
};
type Root = Array<Guessed>;
-
No attempt is made to guess if particular strings are string union types.
-
Empty arrays are guessed to be arrays of never
. That is,
guessJsonShape([])
is inferred to be Array<never>
. The consumer needs to
decide how to represent that in their output.
-
Does not work on cirular structures. JSON can not be circular, but it's still
possible to pass in something circular.