Skip to content

Instantly share code, notes, and snippets.

@karupanerura
Created June 20, 2019 21:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karupanerura/c40dca168a9f0450cbe3276884c3fd9e to your computer and use it in GitHub Desktop.
Save karupanerura/c40dca168a9f0450cbe3276884c3fd9e to your computer and use it in GitHub Desktop.
use strict;
use warnings;
use utf8;
use Path::Tiny qw/path/;
use JSON::Pointer;
use Cpanel::JSON::XS qw/decode_json/;
use Cpanel::JSON::XS::Type;
my $schema = decode_json(<<'__JSON__');
{
"$id": "https://example.com/arrays.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "A representation of a person, company, organization, or place",
"type": "object",
"properties": {
"fruits": {
"type": "array",
"items": {
"type": "string"
}
},
"vegetables": {
"type": "array",
"items": { "$ref": "#/definitions/veggie" }
}
},
"definitions": {
"veggie": {
"type": "object",
"required": [ "veggieName", "veggieLike" ],
"properties": {
"veggieName": {
"type": "string",
"description": "The name of the vegetable."
},
"veggieLike": {
"type": "boolean",
"description": "Do I like this vegetable?"
}
}
}
}
}
__JSON__
sub resolve {
my $schema = shift;
local our $SCHEMA = $schema;
_resolve($schema);
}
sub _resolve {
my $schema = shift;
if (exists $schema->{'$ref'}) {
my ($uri, $internal_path) = split /#/, $schema->{'$ref'}, 2;
die "external resolve is not yet supported" if $uri;
return JSON::Pointer->get(our $SCHEMA, $internal_path);
}
$schema = {%$schema};
if (exists $schema->{properties}) {
my %resolved;
for my $k (keys %{ $schema->{properties} }) {
$resolved{$k} = _resolve($schema->{properties}->{$k});
}
$schema->{properties} = \%resolved;
}
if (exists $schema->{items}) {
$schema->{items} = _resolve($schema->{items});
}
for my $key (qw/oneOf anyOf allOf/) {
next unless exists $schema->{$key};
$schema->{$key} = [map _resolve($_), @{ $schema->{$key} }];
}
return $schema;
}
my %SCHEMA_TYPE_MAP = (
null => JSON_TYPE_NULL,
boolean => JSON_TYPE_BOOL,
number => JSON_TYPE_FLOAT,
string => JSON_TYPE_STRING,
array => JSON_TYPE_ARRAYOF_CLASS,
object => JSON_TYPE_HASHOF_CLASS,
);
sub convert {
my $schema = shift;
die '$ref is not yet supported' if exists $schema->{'$ref'};
die 'not is not yet supported' if exists $schema->{'not'};
if (exists $schema->{properties}) {
my %typemap;
for my $k (keys %{ $schema->{properties} }) {
$typemap{$k} = convert($schema->{properties}->{$k});
}
return \%typemap;
}
if (exists $schema->{items}) {
return json_type_arrayof(convert($schema->{items}));
}
if (exists $schema->{anyOf}) {
return json_type_anyof(map convert($_), @{ $schema->{anyOf} });
}
if (exists $schema->{oneOf}) {
return json_type_anyof(map convert($_), @{ $schema->{oneOf} }); ## OK?
}
if (exists $schema->{allOf}) {
my %merged;
%merged = (%merged, %$_) for @{ $schema->{allOf} };
return convert(\%merged);
}
my $type = $schema->{type};
unless ($type) {
# XXX: not perfect
if (exists $schema->{enum} || exists $schema->{pattern}) {
$type = 'string';
}
elsif (exists $schema->{multipleOf}) {
$type = 'number';
}
elsif (exists $schema->{maximum} || exists $schema->{exclusiveMaximum}) {
$type = 'number';
}
elsif (exists $schema->{minimum} || exists $schema->{exclusiveMinimum}) {
$type = 'number';
}
elsif (exists $schema->{maxLength} || exists $schema->{minLength}) {
$type = 'string';
}
elsif (exists $schema->{additionalItems} || exists $schema->{maxItems} || exists $schema->{minItems}) {
$type = 'array';
}
elsif (exists $schema->{additionalProperties} || exists $schema->{maxProperties} || exists $schema->{minProperties}) {
$type = 'object';
}
elsif (exists $schema->{requires}) {
$type = 'object';
}
}
return $SCHEMA_TYPE_MAP{$type} || JSON_TYPE_ANYOF_CLASS unless ref $type;
return json_type_anyof(grep defined, map $SCHEMA_TYPE_MAP{$_}, @$type);
}
use DDP;
p $schema;
$schema = resolve($schema);
p $schema;
$schema = convert($schema);
p $schema;
print+Cpanel::JSON::XS->new->canonical->pretty->encode({
fruits => [qw/apple orange pear/],
vegetables => [
{
veggieName => 'potato',
veggieLike => 1,
},
{
veggieName => 'broccoli',
veggieLike => 0,
}
]
}, $schema);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment