diff --git a/head/crystal/test/inputs/json/misc/00c36.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/00c36.json/default/TopLevel.cr
new file mode 100644
index 0000000..4b9cdae
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/00c36.json/default/TopLevel.cr
@@ -0,0 +1,39 @@
+require "json"
+
+alias TopLevel = Array(TopLevelUnion)
+
+class PurpleTopLevel
+  include JSON::Serializable
+
+  property country : Country
+
+  property date : String
+
+  property decimal : String
+
+  property indicator : Country
+
+  property value : String
+end
+
+class Country
+  include JSON::Serializable
+
+  property id : String
+
+  property value : String
+end
+
+class FluffyTopLevel
+  include JSON::Serializable
+
+  property page : Int32
+
+  property pages : Int32
+
+  property per_page : String
+
+  property total : Int32
+end
+
+alias TopLevelUnion = FluffyTopLevel | Array(PurpleTopLevel)
diff --git a/head/crystal/test/inputs/json/misc/00ec5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/00ec5.json/default/TopLevel.cr
new file mode 100644
index 0000000..a9250ce
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/00ec5.json/default/TopLevel.cr
@@ -0,0 +1,121 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "basePath")]
+  property base_path : String
+
+  property definitions : Definitions
+
+  property host : String
+
+  property info : Info
+
+  property paths : Paths
+
+  property produces : Array(String)
+
+  property schemes : Array(String)
+
+  property swagger : String
+end
+
+class Definitions
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Provider")]
+  property provider : Provider
+end
+
+class Provider
+  include JSON::Serializable
+
+  property properties : Hash(String, Property)
+end
+
+class Property
+  include JSON::Serializable
+
+  property description : String
+
+  @[JSON::Field(key: "type")]
+  property property_type : String
+end
+
+class Info
+  include JSON::Serializable
+
+  property description : String
+
+  property title : String
+
+  property version : String
+end
+
+class Paths
+  include JSON::Serializable
+
+  @[JSON::Field(key: "/business_service_providers/search")]
+  property business_service_providers_search : BusinessServiceProvidersSearch
+end
+
+class BusinessServiceProvidersSearch
+  include JSON::Serializable
+
+  property get : Get
+end
+
+class Get
+  include JSON::Serializable
+
+  property description : String
+
+  property parameters : Array(Parameter)
+
+  property responses : Responses
+
+  property summary : String
+
+  property tags : Array(String)
+end
+
+class Parameter
+  include JSON::Serializable
+
+  property description : String
+
+  property format : String
+
+  property name : String
+
+  @[JSON::Field(key: "in")]
+  property parameter_in : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : String
+
+  property required : Bool
+end
+
+class Responses
+  include JSON::Serializable
+
+  @[JSON::Field(key: "200")]
+  property the_200 : The200
+end
+
+class The200
+  include JSON::Serializable
+
+  property description : String
+
+  property schema : Schema
+end
+
+class Schema
+  include JSON::Serializable
+
+  @[JSON::Field(key: "$ref")]
+  property ref : String
+end
diff --git a/head/crystal/test/inputs/json/misc/010b1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/010b1.json/default/TopLevel.cr
new file mode 100644
index 0000000..943073c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/010b1.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property age : Int32
+
+  property country : String
+
+  property females : Int32
+
+  property males : Int32
+
+  property total : Int32
+
+  property year : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/016af.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/016af.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/016af.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/033b1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/033b1.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/033b1.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/050b0.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/050b0.json/default/TopLevel.cr
new file mode 100644
index 0000000..6d7a453
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/050b0.json/default/TopLevel.cr
@@ -0,0 +1,57 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(OtherName)
+
+  property superseded_by : String?
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class OtherName
+  include JSON::Serializable
+
+  property name : String
+
+  property note : String?
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/06bee.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/06bee.json/default/TopLevel.cr
new file mode 100644
index 0000000..8c033c5
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/06bee.json/default/TopLevel.cr
@@ -0,0 +1,49 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(JSON::Any?)
+
+  property superseded_by : Nil
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/07540.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/07540.json/default/TopLevel.cr
new file mode 100644
index 0000000..90a4346
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/07540.json/default/TopLevel.cr
@@ -0,0 +1,12 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property cookies : Cookies
+end
+
+class Cookies
+  include JSON::Serializable
+
+end
diff --git a/head/crystal/test/inputs/json/misc/0779f.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/0779f.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/0779f.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/07c75.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/07c75.json/default/TopLevel.cr
new file mode 100644
index 0000000..8c033c5
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/07c75.json/default/TopLevel.cr
@@ -0,0 +1,49 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(JSON::Any?)
+
+  property superseded_by : Nil
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/09f54.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/09f54.json/default/TopLevel.cr
new file mode 100644
index 0000000..c658019
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/09f54.json/default/TopLevel.cr
@@ -0,0 +1,29 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Hash(String, Datum)
+
+  property description : Description
+end
+
+class Datum
+  include JSON::Serializable
+
+  property anomaly : String
+
+  property value : String
+end
+
+class Description
+  include JSON::Serializable
+
+  property base_period : String
+
+  property missing : Int32
+
+  property title : String
+
+  property units : String
+end
diff --git a/head/crystal/test/inputs/json/misc/0a358.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/0a358.json/default/TopLevel.cr
new file mode 100644
index 0000000..dd11d60
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/0a358.json/default/TopLevel.cr
@@ -0,0 +1,41 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property facets : Facets
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property previous : Bool
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : String
+end
+
+class Facets
+  include JSON::Serializable
+
+end
+
+class Result
+  include JSON::Serializable
+
+  property code : String
+
+  property created_at : String
+
+  property id : Int32
+
+  property name : String
+
+  property updated_at : String
+
+  property uri : String
+end
diff --git a/head/crystal/test/inputs/json/misc/0a91a.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/0a91a.json/default/TopLevel.cr
new file mode 100644
index 0000000..6485dbc
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/0a91a.json/default/TopLevel.cr
@@ -0,0 +1,414 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property actor : Actor
+
+  property created_at : String
+
+  property id : String
+
+  property org : Actor?
+
+  property payload : Payload
+
+  property public : Bool
+
+  property repo : TopLevelRepo
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+end
+
+class Actor
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property display_login : String?
+
+  property gravatar_id : String
+
+  property id : Int32
+
+  property login : String
+
+  property url : String
+end
+
+class Payload
+  include JSON::Serializable
+
+  property action : String?
+
+  property before : String?
+
+  property commits : Array(Commit)?
+
+  property description : String?
+
+  property distinct_size : Int32?
+
+  property head : String?
+
+  property master_branch : String?
+
+  property number : Int32?
+
+  property pull_request : PullRequest?
+
+  property push_id : Int32?
+
+  property pusher_type : String?
+
+  property ref : String?
+
+  property ref_type : String?
+
+  property size : Int32?
+end
+
+class Commit
+  include JSON::Serializable
+
+  property author : Author
+
+  property distinct : Bool
+
+  property message : String
+
+  property sha : String
+
+  property url : String
+end
+
+class Author
+  include JSON::Serializable
+
+  property email : String
+
+  property name : String
+end
+
+class PullRequest
+  include JSON::Serializable
+
+  property additions : Int32
+
+  property assignee : Nil
+
+  property assignees : Array(JSON::Any?)
+
+  property base : Base
+
+  property body : String
+
+  property changed_files : Int32
+
+  property closed_at : String
+
+  property comments : Int32
+
+  property comments_url : String
+
+  property commits : Int32
+
+  property commits_url : String
+
+  property created_at : String
+
+  property deletions : Int32
+
+  property diff_url : String
+
+  property head : Base
+
+  property html_url : String
+
+  property id : Int32
+
+  property issue_url : String
+
+  @[JSON::Field(key: "_links")]
+  property links : Links
+
+  property locked : Bool
+
+  property maintainer_can_modify : Bool
+
+  property merge_commit_sha : String
+
+  property mergeable : Nil
+
+  property mergeable_state : String
+
+  property merged : Bool
+
+  property merged_at : String
+
+  property merged_by : MergedBy
+
+  property milestone : Nil
+
+  property number : Int32
+
+  property patch_url : String
+
+  property rebaseable : Nil
+
+  property requested_reviewers : Array(JSON::Any?)
+
+  property review_comment_url : String
+
+  property review_comments : Int32
+
+  property review_comments_url : String
+
+  property state : String
+
+  property statuses_url : String
+
+  property title : String
+
+  property updated_at : String
+
+  property url : String
+
+  property user : MergedBy
+end
+
+class Base
+  include JSON::Serializable
+
+  property label : String
+
+  property ref : String
+
+  property repo : BaseRepo
+
+  property sha : String
+
+  property user : MergedBy
+end
+
+class BaseRepo
+  include JSON::Serializable
+
+  property archive_url : String
+
+  property assignees_url : String
+
+  property blobs_url : String
+
+  property branches_url : String
+
+  property clone_url : String
+
+  property collaborators_url : String
+
+  property comments_url : String
+
+  property commits_url : String
+
+  property compare_url : String
+
+  property contents_url : String
+
+  property contributors_url : String
+
+  property created_at : String
+
+  property default_branch : String
+
+  property deployments_url : String
+
+  property description : Nil
+
+  property downloads_url : String
+
+  property events_url : String
+
+  property fork : Bool
+
+  property forks : Int32
+
+  property forks_count : Int32
+
+  property forks_url : String
+
+  property full_name : String
+
+  property git_commits_url : String
+
+  property git_refs_url : String
+
+  property git_tags_url : String
+
+  property git_url : String
+
+  property has_downloads : Bool
+
+  property has_issues : Bool
+
+  property has_pages : Bool
+
+  property has_projects : Bool
+
+  property has_wiki : Bool
+
+  property homepage : Nil
+
+  property hooks_url : String
+
+  property html_url : String
+
+  property id : Int32
+
+  property issue_comment_url : String
+
+  property issue_events_url : String
+
+  property issues_url : String
+
+  property keys_url : String
+
+  property labels_url : String
+
+  property language : String
+
+  property languages_url : String
+
+  property merges_url : String
+
+  property milestones_url : String
+
+  property mirror_url : Nil
+
+  property name : String
+
+  property notifications_url : String
+
+  property open_issues : Int32
+
+  property open_issues_count : Int32
+
+  property owner : MergedBy
+
+  property pulls_url : String
+
+  property pushed_at : String
+
+  property releases_url : String
+
+  @[JSON::Field(key: "private")]
+  property repo_private : Bool
+
+  property size : Int32
+
+  property ssh_url : String
+
+  property stargazers_count : Int32
+
+  property stargazers_url : String
+
+  property statuses_url : String
+
+  property subscribers_url : String
+
+  property subscription_url : String
+
+  property svn_url : String
+
+  property tags_url : String
+
+  property teams_url : String
+
+  property trees_url : String
+
+  property updated_at : String
+
+  property url : String
+
+  property watchers : Int32
+
+  property watchers_count : Int32
+end
+
+class MergedBy
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property events_url : String
+
+  property followers_url : String
+
+  property following_url : String
+
+  property gists_url : String
+
+  property gravatar_id : String
+
+  property html_url : String
+
+  property id : Int32
+
+  property login : String
+
+  @[JSON::Field(key: "type")]
+  property merged_by_type : String
+
+  property organizations_url : String
+
+  property received_events_url : String
+
+  property repos_url : String
+
+  property site_admin : Bool
+
+  property starred_url : String
+
+  property subscriptions_url : String
+
+  property url : String
+end
+
+class Links
+  include JSON::Serializable
+
+  property comments : Comments
+
+  property commits : Comments
+
+  property html : Comments
+
+  property issue : Comments
+
+  @[JSON::Field(key: "self")]
+  property links_self : Comments
+
+  property review_comment : Comments
+
+  property review_comments : Comments
+
+  property statuses : Comments
+end
+
+class Comments
+  include JSON::Serializable
+
+  property href : String
+end
+
+class TopLevelRepo
+  include JSON::Serializable
+
+  property id : Int32
+
+  property name : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/0b91a.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/0b91a.json/default/TopLevel.cr
new file mode 100644
index 0000000..94a8606
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/0b91a.json/default/TopLevel.cr
@@ -0,0 +1,80 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property metadata : Metadata
+
+  property results : Array(Result)
+end
+
+class Metadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "executionTime")]
+  property execution_time : Float64
+
+  @[JSON::Field(key: "responseInfo")]
+  property response_info : ResponseInfo
+
+  property resultset : Resultset
+end
+
+class ResponseInfo
+  include JSON::Serializable
+
+  @[JSON::Field(key: "developerMessage")]
+  property developer_message : String
+
+  property status : Int32
+end
+
+class Resultset
+  include JSON::Serializable
+
+  property count : Int32
+
+  property page : Int32
+
+  property pagesize : Int32
+end
+
+class Result
+  include JSON::Serializable
+
+  property attachment : Array(JSON::Any?)
+
+  property body : String
+
+  property changed : String
+
+  property component : Array(Component)
+
+  property created : String
+
+  property date : String
+
+  property image : Array(JSON::Any?)
+
+  property number : Nil
+
+  property teaser : Nil
+
+  property title : String
+
+  property topic : Array(JSON::Any?)
+
+  property url : String
+
+  property uuid : String
+
+  property vuuid : String
+end
+
+class Component
+  include JSON::Serializable
+
+  property name : String
+
+  property uuid : String
+end
diff --git a/head/crystal/test/inputs/json/misc/0cffa.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/0cffa.json/default/TopLevel.cr
new file mode 100644
index 0000000..88dfd89
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/0cffa.json/default/TopLevel.cr
@@ -0,0 +1,195 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  property meta : Meta
+
+  property pagination : Pagination
+end
+
+class Datum
+  include JSON::Serializable
+
+  property bitly_gif_url : String
+
+  property bitly_url : String
+
+  property content_url : String
+
+  @[JSON::Field(key: "type")]
+  property datum_type : String
+
+  property embed_url : String
+
+  property id : String
+
+  property images : Images
+
+  property import_datetime : String
+
+  property is_indexable : Int32
+
+  property rating : String
+
+  property slug : String
+
+  property source : String
+
+  property source_post_url : String
+
+  property source_tld : String
+
+  property trending_datetime : String
+
+  property url : String
+
+  property user : User?
+
+  property username : String
+end
+
+class Images
+  include JSON::Serializable
+
+  property downsized : Downsized
+
+  property downsized_large : Downsized
+
+  property downsized_medium : Downsized
+
+  property downsized_small : DownsizedSmall
+
+  property downsized_still : Downsized
+
+  property fixed_height : FixedHeight
+
+  property fixed_height_downsampled : FixedHeight
+
+  property fixed_height_small : FixedHeight
+
+  property fixed_height_small_still : Downsized
+
+  property fixed_height_still : Downsized
+
+  property fixed_width : FixedHeight
+
+  property fixed_width_downsampled : FixedHeight
+
+  property fixed_width_small : FixedHeight
+
+  property fixed_width_small_still : Downsized
+
+  property fixed_width_still : Downsized
+
+  property looping : Looping
+
+  property original : FixedHeight
+
+  property original_mp4 : DownsizedSmall
+
+  property original_still : Downsized
+
+  property preview : DownsizedSmall
+
+  property preview_gif : Downsized
+
+  property preview_webp : Downsized
+
+  @[JSON::Field(key: "480w_still")]
+  property the_480_w_still : Downsized?
+end
+
+class Downsized
+  include JSON::Serializable
+
+  property height : String
+
+  property size : String?
+
+  property url : String
+
+  property width : String
+end
+
+class DownsizedSmall
+  include JSON::Serializable
+
+  property height : String
+
+  property mp4 : String
+
+  property mp4_size : String
+
+  property width : String
+end
+
+class FixedHeight
+  include JSON::Serializable
+
+  property frames : String?
+
+  property hash : String?
+
+  property height : String
+
+  property mp4 : String?
+
+  property mp4_size : String?
+
+  property size : String
+
+  property url : String
+
+  property webp : String
+
+  property webp_size : String
+
+  property width : String
+end
+
+class Looping
+  include JSON::Serializable
+
+  property mp4 : String
+
+  property mp4_size : String
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property banner_url : String
+
+  property display_name : String
+
+  property profile_url : String
+
+  property twitter : String
+
+  property username : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property msg : String
+
+  property response_id : String
+
+  property status : Int32
+end
+
+class Pagination
+  include JSON::Serializable
+
+  property count : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/0e0c2.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/0e0c2.json/default/TopLevel.cr
new file mode 100644
index 0000000..b46c73c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/0e0c2.json/default/TopLevel.cr
@@ -0,0 +1,125 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property previous : Nil
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : String
+end
+
+class Result
+  include JSON::Serializable
+
+  property active_screens : Int32
+
+  property collections : Array(Collection)
+
+  property details : Array(Detail)
+
+  property duration : Int32
+
+  property fav_movie : FavMovie
+
+  property id : Int32
+
+  property images : Array(Image)
+
+  property language : String
+
+  property release_date : String
+
+  property stars : Int32
+
+  property tags : Array(JSON::Any?)?
+
+  property title : String
+
+  property videos : Array(Video)
+
+  property vote_score : VoteScore
+
+  property watches : Int32
+end
+
+class Collection
+  include JSON::Serializable
+
+  property id : Int32
+
+  property movies : Array(Int32)
+
+  property name : String
+
+  property slug : String
+end
+
+class Detail
+  include JSON::Serializable
+
+  property cast : String
+
+  property director : String
+
+  property id : Int32
+
+  property language : String
+
+  property storyline : String
+
+  property tagline : String
+
+  property title : String
+end
+
+class FavMovie
+  include JSON::Serializable
+
+  property follow : Bool
+
+  property star : Bool
+
+  property watched : Bool
+end
+
+class Image
+  include JSON::Serializable
+
+  property favs : Int32
+
+  property id : Int32
+
+  @[JSON::Field(key: "type")]
+  property image_type : String
+
+  property thumbnail : String
+
+  property url : String
+end
+
+class Video
+  include JSON::Serializable
+
+  property kind : String
+
+  property language : String
+
+  property source : String
+
+  property url : String
+end
+
+class VoteScore
+  include JSON::Serializable
+
+  property avg : Int32
+
+  property score : Int32
+
+  property total : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/0fecf.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/0fecf.json/default/TopLevel.cr
new file mode 100644
index 0000000..83f0e9a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/0fecf.json/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property countries : Array(String)
+end
diff --git a/head/crystal/test/inputs/json/misc/10be4.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/10be4.json/default/TopLevel.cr
new file mode 100644
index 0000000..6d7a453
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/10be4.json/default/TopLevel.cr
@@ -0,0 +1,57 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(OtherName)
+
+  property superseded_by : String?
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class OtherName
+  include JSON::Serializable
+
+  property name : String
+
+  property note : String?
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/112b5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/112b5.json/default/TopLevel.cr
new file mode 100644
index 0000000..ac985ae
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/112b5.json/default/TopLevel.cr
@@ -0,0 +1,34 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property args : Args
+
+  property headers : Headers
+
+  property origin : String
+
+  property url : String
+end
+
+class Args
+  include JSON::Serializable
+
+end
+
+class Headers
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Accept-Encoding")]
+  property accept_encoding : String
+
+  @[JSON::Field(key: "Connection")]
+  property connection : String
+
+  @[JSON::Field(key: "Host")]
+  property host : String
+
+  @[JSON::Field(key: "User-Agent")]
+  property user_agent : String
+end
diff --git a/head/crystal/test/inputs/json/misc/127a1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/127a1.json/default/TopLevel.cr
new file mode 100644
index 0000000..5d22178
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/127a1.json/default/TopLevel.cr
@@ -0,0 +1,195 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  property meta : Meta
+
+  property pagination : Pagination
+end
+
+class Datum
+  include JSON::Serializable
+
+  property bitly_gif_url : String
+
+  property bitly_url : String
+
+  property content_url : String
+
+  @[JSON::Field(key: "type")]
+  property datum_type : String
+
+  property embed_url : String
+
+  property id : String
+
+  property images : Images
+
+  property import_datetime : String
+
+  property is_indexable : Int32
+
+  property rating : String
+
+  property slug : String
+
+  property source : String
+
+  property source_post_url : String
+
+  property source_tld : String
+
+  property trending_datetime : String
+
+  property url : String
+
+  property user : User?
+
+  property username : String
+end
+
+class Images
+  include JSON::Serializable
+
+  property downsized : Downsized
+
+  property downsized_large : Downsized
+
+  property downsized_medium : Downsized
+
+  property downsized_small : DownsizedSmall
+
+  property downsized_still : Downsized
+
+  property fixed_height : FixedHeight
+
+  property fixed_height_downsampled : FixedHeight
+
+  property fixed_height_small : FixedHeight
+
+  property fixed_height_small_still : Downsized
+
+  property fixed_height_still : Downsized
+
+  property fixed_width : FixedHeight
+
+  property fixed_width_downsampled : FixedHeight
+
+  property fixed_width_small : FixedHeight
+
+  property fixed_width_small_still : Downsized
+
+  property fixed_width_still : Downsized
+
+  property looping : Looping
+
+  property original : FixedHeight
+
+  property original_mp4 : DownsizedSmall
+
+  property original_still : Downsized
+
+  property preview : DownsizedSmall
+
+  property preview_gif : Downsized
+
+  property preview_webp : Downsized
+
+  @[JSON::Field(key: "480w_still")]
+  property the_480_w_still : Downsized?
+end
+
+class Downsized
+  include JSON::Serializable
+
+  property height : String
+
+  property size : String?
+
+  property url : String
+
+  property width : String
+end
+
+class DownsizedSmall
+  include JSON::Serializable
+
+  property height : String
+
+  property mp4 : String
+
+  property mp4_size : String
+
+  property width : String
+end
+
+class FixedHeight
+  include JSON::Serializable
+
+  property frames : String?
+
+  property hash : String?
+
+  property height : String
+
+  property mp4 : String?
+
+  property mp4_size : String?
+
+  property size : String
+
+  property url : String
+
+  property webp : String
+
+  property webp_size : String
+
+  property width : String
+end
+
+class Looping
+  include JSON::Serializable
+
+  property mp4 : String
+
+  property mp4_size : String
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property banner_url : String
+
+  property display_name : String
+
+  property profile_url : String
+
+  property twitter : String?
+
+  property username : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property msg : String
+
+  property response_id : String
+
+  property status : Int32
+end
+
+class Pagination
+  include JSON::Serializable
+
+  property count : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/13d8d.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/13d8d.json/default/TopLevel.cr
new file mode 100644
index 0000000..c219957
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/13d8d.json/default/TopLevel.cr
@@ -0,0 +1,51 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property category : String
+
+  property context : String
+
+  property id : Int32
+
+  property location : Location
+
+  property location_subtype : String
+
+  property location_type : String
+
+  property month : String
+
+  property outcome_status : OutcomeStatus
+
+  property persistent_id : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property latitude : String
+
+  property longitude : String
+
+  property street : Street
+end
+
+class Street
+  include JSON::Serializable
+
+  property id : Int32
+
+  property name : String
+end
+
+class OutcomeStatus
+  include JSON::Serializable
+
+  property category : String
+
+  property date : String
+end
diff --git a/head/crystal/test/inputs/json/misc/14d38.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/14d38.json/default/TopLevel.cr
new file mode 100644
index 0000000..9494547
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/14d38.json/default/TopLevel.cr
@@ -0,0 +1,23 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property headers : Headers
+end
+
+class Headers
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Accept-Encoding")]
+  property accept_encoding : String
+
+  @[JSON::Field(key: "Connection")]
+  property connection : String
+
+  @[JSON::Field(key: "Host")]
+  property host : String
+
+  @[JSON::Field(key: "User-Agent")]
+  property user_agent : String
+end
diff --git a/head/crystal/test/inputs/json/misc/167d6.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/167d6.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/167d6.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/16bc5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/16bc5.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/16bc5.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/176f1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/176f1.json/default/TopLevel.cr
new file mode 100644
index 0000000..54f6724
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/176f1.json/default/TopLevel.cr
@@ -0,0 +1,53 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property message : Array(JSON::Any?)
+
+  @[JSON::Field(key: "responseTime")]
+  property response_time : Int32
+
+  @[JSON::Field(key: "Results")]
+  property results : Results
+
+  property status : String
+end
+
+class Results
+  include JSON::Serializable
+
+  property series : Array(Series)
+end
+
+class Series
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  @[JSON::Field(key: "seriesID")]
+  property series_id : String
+end
+
+class Datum
+  include JSON::Serializable
+
+  property footnotes : Array(Footnote)
+
+  property period : String
+
+  @[JSON::Field(key: "periodName")]
+  property period_name : String
+
+  property value : String
+
+  property year : String
+end
+
+class Footnote
+  include JSON::Serializable
+
+  property code : String?
+
+  property text : String?
+end
diff --git a/head/crystal/test/inputs/json/misc/1a7f5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/1a7f5.json/default/TopLevel.cr
new file mode 100644
index 0000000..943073c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/1a7f5.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property age : Int32
+
+  property country : String
+
+  property females : Int32
+
+  property males : Int32
+
+  property total : Int32
+
+  property year : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/1b28c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/1b28c.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/1b28c.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/1b409.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/1b409.json/default/TopLevel.cr
new file mode 100644
index 0000000..558cb8b
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/1b409.json/default/TopLevel.cr
@@ -0,0 +1,119 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property objects : Array(ObjectElement)
+end
+
+class Meta
+  include JSON::Serializable
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
+
+class ObjectElement
+  include JSON::Serializable
+
+  property caucus : Nil
+
+  property congress_numbers : Array(Int32)
+
+  property current : Bool
+
+  property description : String
+
+  property district : Int32
+
+  property enddate : String
+
+  property extra : Extra
+
+  property id : Int32
+
+  property leadership_title : String?
+
+  property party : String
+
+  property person : Person
+
+  property phone : String
+
+  property role_type : String
+
+  property role_type_label : String
+
+  property senator_class : Nil
+
+  property senator_rank : Nil
+
+  property startdate : String
+
+  property state : String
+
+  property title : String
+
+  property title_long : String
+
+  property website : String
+end
+
+class Extra
+  include JSON::Serializable
+
+  property address : String
+
+  property contact_form : String?
+
+  property fax : String?
+
+  property office : String
+
+  property rss_url : String?
+end
+
+class Person
+  include JSON::Serializable
+
+  property bioguideid : String
+
+  property birthday : String
+
+  property cspanid : Int32
+
+  property firstname : String
+
+  property gender : String
+
+  property gender_label : String
+
+  property id : Int32
+
+  property lastname : String
+
+  property link : String
+
+  property middlename : String
+
+  property name : String
+
+  property namemod : String
+
+  property nickname : String
+
+  property osid : String
+
+  property pvsid : String?
+
+  property sortname : String
+
+  property twitterid : String?
+
+  property youtubeid : String?
+end
diff --git a/head/crystal/test/inputs/json/misc/2465e.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/2465e.json/default/TopLevel.cr
new file mode 100644
index 0000000..d7b5851
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/2465e.json/default/TopLevel.cr
@@ -0,0 +1,138 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "basePath")]
+  property base_path : String
+
+  property definitions : Definitions
+
+  property host : String
+
+  property info : Info
+
+  property paths : Paths
+
+  property produces : Array(String)
+
+  property schemes : Array(String)
+
+  property swagger : String
+end
+
+class Definitions
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Taxonomy")]
+  property taxonomy : Taxonomy
+end
+
+class Taxonomy
+  include JSON::Serializable
+
+  property properties : Properties
+end
+
+class Properties
+  include JSON::Serializable
+
+  property annotations : Annotations
+
+  property datatype_properties : Annotations
+
+  property id : Annotations
+
+  property label : Annotations
+
+  @[JSON::Field(key: "type")]
+  property properties_type : Annotations
+
+  property sub_class_of : Annotations
+end
+
+class Annotations
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property annotations_type : String
+
+  property description : String
+end
+
+class Info
+  include JSON::Serializable
+
+  property description : String
+
+  property title : String
+
+  property version : String
+end
+
+class Paths
+  include JSON::Serializable
+
+  @[JSON::Field(key: "/ita_taxonomies/search")]
+  property ita_taxonomies_search : ItaTaxonomiesSearch
+end
+
+class ItaTaxonomiesSearch
+  include JSON::Serializable
+
+  property get : Get
+end
+
+class Get
+  include JSON::Serializable
+
+  property description : String
+
+  property parameters : Array(Parameter)
+
+  property responses : Responses
+
+  property summary : String
+
+  property tags : Array(String)
+end
+
+class Parameter
+  include JSON::Serializable
+
+  property description : String
+
+  property format : String
+
+  property name : String
+
+  @[JSON::Field(key: "in")]
+  property parameter_in : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : String
+
+  property required : Bool
+end
+
+class Responses
+  include JSON::Serializable
+
+  @[JSON::Field(key: "200")]
+  property the_200 : The200
+end
+
+class The200
+  include JSON::Serializable
+
+  property description : String
+
+  property schema : Schema
+end
+
+class Schema
+  include JSON::Serializable
+
+  @[JSON::Field(key: "$ref")]
+  property ref : String
+end
diff --git a/head/crystal/test/inputs/json/misc/24f52.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/24f52.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/24f52.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/262f0.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/262f0.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/262f0.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/26b49.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/26b49.json/default/TopLevel.cr
new file mode 100644
index 0000000..482e932
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/26b49.json/default/TopLevel.cr
@@ -0,0 +1,197 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  property meta : Meta
+
+  property pagination : Pagination
+end
+
+class Datum
+  include JSON::Serializable
+
+  property bitly_gif_url : String
+
+  property bitly_url : String
+
+  property content_url : String
+
+  @[JSON::Field(key: "type")]
+  property datum_type : String
+
+  property embed_url : String
+
+  property id : String
+
+  property images : Images
+
+  property import_datetime : String
+
+  property is_indexable : Int32
+
+  property rating : String
+
+  property slug : String
+
+  property source : String
+
+  property source_post_url : String
+
+  property source_tld : String
+
+  property trending_datetime : String
+
+  property url : String
+
+  property user : User?
+
+  property username : String
+end
+
+class Images
+  include JSON::Serializable
+
+  property downsized : Downsized
+
+  property downsized_large : Downsized
+
+  property downsized_medium : Downsized
+
+  property downsized_small : DownsizedSmall
+
+  property downsized_still : Downsized
+
+  property fixed_height : FixedHeight
+
+  property fixed_height_downsampled : FixedHeight
+
+  property fixed_height_small : FixedHeight
+
+  property fixed_height_small_still : Downsized
+
+  property fixed_height_still : Downsized
+
+  property fixed_width : FixedHeight
+
+  property fixed_width_downsampled : FixedHeight
+
+  property fixed_width_small : FixedHeight
+
+  property fixed_width_small_still : Downsized
+
+  property fixed_width_still : Downsized
+
+  property hd : DownsizedSmall?
+
+  property looping : Looping
+
+  property original : FixedHeight
+
+  property original_mp4 : DownsizedSmall
+
+  property original_still : Downsized
+
+  property preview : DownsizedSmall
+
+  property preview_gif : Downsized
+
+  property preview_webp : Downsized
+
+  @[JSON::Field(key: "480w_still")]
+  property the_480_w_still : Downsized?
+end
+
+class Downsized
+  include JSON::Serializable
+
+  property height : String
+
+  property size : String?
+
+  property url : String
+
+  property width : String
+end
+
+class DownsizedSmall
+  include JSON::Serializable
+
+  property height : String
+
+  property mp4 : String
+
+  property mp4_size : String
+
+  property width : String
+end
+
+class FixedHeight
+  include JSON::Serializable
+
+  property frames : String?
+
+  property hash : String?
+
+  property height : String
+
+  property mp4 : String?
+
+  property mp4_size : String?
+
+  property size : String
+
+  property url : String
+
+  property webp : String
+
+  property webp_size : String
+
+  property width : String
+end
+
+class Looping
+  include JSON::Serializable
+
+  property mp4 : String
+
+  property mp4_size : String
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property banner_url : String
+
+  property display_name : String
+
+  property profile_url : String
+
+  property twitter : String?
+
+  property username : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property msg : String
+
+  property response_id : String
+
+  property status : Int32
+end
+
+class Pagination
+  include JSON::Serializable
+
+  property count : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/26c9c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/26c9c.json/default/TopLevel.cr
new file mode 100644
index 0000000..ad77803
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/26c9c.json/default/TopLevel.cr
@@ -0,0 +1,400 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Array(Datum?))
+
+  property meta : Meta
+end
+
+class Meta
+  include JSON::Serializable
+
+  property view : View
+end
+
+class View
+  include JSON::Serializable
+
+  property attribution : String
+
+  @[JSON::Field(key: "attributionLink")]
+  property attribution_link : String
+
+  @[JSON::Field(key: "averageRating")]
+  property average_rating : Int32
+
+  property category : String
+
+  property columns : Array(Column)
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : Int32
+
+  property description : String
+
+  @[JSON::Field(key: "displayType")]
+  property display_type : String
+
+  @[JSON::Field(key: "downloadCount")]
+  property download_count : Int32
+
+  property flags : Array(String)
+
+  property grants : Array(Grant)
+
+  @[JSON::Field(key: "hideFromCatalog")]
+  property hide_from_catalog : Bool
+
+  @[JSON::Field(key: "hideFromDataJson")]
+  property hide_from_data_json : Bool
+
+  property id : String
+
+  @[JSON::Field(key: "indexUpdatedAt")]
+  property index_updated_at : Int32
+
+  property locale : String
+
+  property metadata : Metadata
+
+  property name : String
+
+  @[JSON::Field(key: "newBackend")]
+  property new_backend : Bool
+
+  @[JSON::Field(key: "numberOfComments")]
+  property number_of_comments : Int32
+
+  property oid : Int32
+
+  property owner : Owner
+
+  property provenance : String
+
+  @[JSON::Field(key: "publicationAppendEnabled")]
+  property publication_append_enabled : Bool
+
+  @[JSON::Field(key: "publicationDate")]
+  property publication_date : Int32
+
+  @[JSON::Field(key: "publicationGroup")]
+  property publication_group : Int32
+
+  @[JSON::Field(key: "publicationStage")]
+  property publication_stage : String
+
+  property query : Query
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "rowsUpdatedAt")]
+  property rows_updated_at : Int32
+
+  @[JSON::Field(key: "rowsUpdatedBy")]
+  property rows_updated_by : String
+
+  @[JSON::Field(key: "tableAuthor")]
+  property table_author : Owner
+
+  @[JSON::Field(key: "tableId")]
+  property table_id : Int32
+
+  property tags : Array(String)
+
+  @[JSON::Field(key: "totalTimesRated")]
+  property total_times_rated : Int32
+
+  @[JSON::Field(key: "viewCount")]
+  property view_count : Int32
+
+  @[JSON::Field(key: "viewLastModified")]
+  property view_last_modified : Int32
+
+  @[JSON::Field(key: "viewType")]
+  property view_type : String
+end
+
+class Column
+  include JSON::Serializable
+
+  @[JSON::Field(key: "cachedContents")]
+  property cached_contents : CachedContents?
+
+  @[JSON::Field(key: "dataTypeName")]
+  property data_type_name : String
+
+  @[JSON::Field(key: "fieldName")]
+  property field_name : String
+
+  property flags : Array(String)?
+
+  property format : Format
+
+  property id : Int32
+
+  property name : String
+
+  property position : Int32
+
+  @[JSON::Field(key: "renderTypeName")]
+  property render_type_name : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : Int32?
+
+  property width : Int32?
+end
+
+class CachedContents
+  include JSON::Serializable
+
+  property average : String?
+
+  property largest : String
+
+  property non_null : Int32
+
+  property null : Int32
+
+  property smallest : String
+
+  property sum : String?
+
+  property top : Array(Top)
+end
+
+class Top
+  include JSON::Serializable
+
+  property count : Int32
+
+  property item : String
+end
+
+class Format
+  include JSON::Serializable
+
+  property align : String?
+
+  @[JSON::Field(key: "noCommas")]
+  property no_commas : String?
+
+  @[JSON::Field(key: "precisionStyle")]
+  property precision_style : String?
+
+  property view : String?
+end
+
+class Grant
+  include JSON::Serializable
+
+  property flags : Array(String)
+
+  @[JSON::Field(key: "type")]
+  property grant_type : String
+
+  property inherited : Bool
+end
+
+class Metadata
+  include JSON::Serializable
+
+  property attachments : Array(Attachment)
+
+  @[JSON::Field(key: "availableDisplayTypes")]
+  property available_display_types : Array(String)
+
+  property custom_fields : CustomFields
+
+  @[JSON::Field(key: "jsonQuery")]
+  property json_query : JsonQuery
+
+  @[JSON::Field(key: "rdfSubject")]
+  property rdf_subject : String
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : RenderTypeConfig
+end
+
+class Attachment
+  include JSON::Serializable
+
+  @[JSON::Field(key: "assetId")]
+  property asset_id : String
+
+  @[JSON::Field(key: "blobId")]
+  property blob_id : String
+
+  property filename : String
+
+  property name : String
+end
+
+class CustomFields
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Additional Resources")]
+  property additional_resources : AdditionalResources
+
+  @[JSON::Field(key: "Common Core")]
+  property common_core : CommonCore
+
+  @[JSON::Field(key: "Dataset Information")]
+  property dataset_information : DatasetInformation
+
+  @[JSON::Field(key: "Dataset Summary")]
+  property dataset_summary : DatasetSummary
+
+  @[JSON::Field(key: "Notes")]
+  property notes : Notes
+end
+
+class AdditionalResources
+  include JSON::Serializable
+
+  @[JSON::Field(key: "See Also ")]
+  property additional_resources_see_also : String
+
+  @[JSON::Field(key: "See Also")]
+  property see_also : String
+end
+
+class CommonCore
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Contact Email")]
+  property contact_email : String
+
+  @[JSON::Field(key: "Contact Name")]
+  property contact_name : String
+
+  @[JSON::Field(key: "Publisher")]
+  property publisher : String
+end
+
+class DatasetInformation
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Agency")]
+  property agency : String
+end
+
+class DatasetSummary
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Contact Information")]
+  property contact_information : String
+
+  @[JSON::Field(key: "Coverage")]
+  property coverage : String
+
+  @[JSON::Field(key: "Data Frequency")]
+  property data_frequency : String
+
+  @[JSON::Field(key: "Dataset Owner")]
+  property dataset_owner : String
+
+  @[JSON::Field(key: "Granularity")]
+  property granularity : String
+
+  @[JSON::Field(key: "Organization")]
+  property organization : String
+
+  @[JSON::Field(key: "Posting Frequency")]
+  property posting_frequency : String
+
+  @[JSON::Field(key: "Time Period")]
+  property time_period : String
+
+  @[JSON::Field(key: "Units")]
+  property units : String
+end
+
+class Notes
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Notes")]
+  property notes : String
+end
+
+class JsonQuery
+  include JSON::Serializable
+
+  property order : Array(Order)
+end
+
+class Order
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String
+end
+
+class RenderTypeConfig
+  include JSON::Serializable
+
+  property visible : Visible
+end
+
+class Visible
+  include JSON::Serializable
+
+  property table : Bool
+end
+
+class Owner
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property id : String
+
+  @[JSON::Field(key: "profileImageUrlLarge")]
+  property profile_image_url_large : String
+
+  @[JSON::Field(key: "profileImageUrlMedium")]
+  property profile_image_url_medium : String
+
+  @[JSON::Field(key: "profileImageUrlSmall")]
+  property profile_image_url_small : String
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "roleName")]
+  property role_name : String
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
+
+class Query
+  include JSON::Serializable
+
+  @[JSON::Field(key: "orderBys")]
+  property order_bys : Array(OrderBy)
+end
+
+class OrderBy
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  property expression : Expression
+end
+
+class Expression
+  include JSON::Serializable
+
+  @[JSON::Field(key: "columnId")]
+  property column_id : Int32
+
+  @[JSON::Field(key: "type")]
+  property expression_type : String
+end
+
+alias Datum = Int32 | String
diff --git a/head/crystal/test/inputs/json/misc/27332.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/27332.json/default/TopLevel.cr
new file mode 100644
index 0000000..1f06a2e
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/27332.json/default/TopLevel.cr
@@ -0,0 +1,246 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : String?
+
+  property author_flair_text : String?
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Float64
+
+  property created_utc : Float64
+
+  property distinguished : String?
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Bool
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : String?
+
+  property link_flair_text : String?
+
+  property locked : Bool
+
+  property media : Media?
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property permalink : String
+
+  property post_hint : String?
+
+  property preview : Preview?
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Media?
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : String?
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : Nil
+
+  property thumbnail : String
+
+  property thumbnail_height : Int32?
+
+  property thumbnail_width : Int32?
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+end
+
+class Media
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property media_type : String
+
+  property oembed : Oembed
+end
+
+class Oembed
+  include JSON::Serializable
+
+  property description : String
+
+  property height : Int32
+
+  property html : String
+
+  @[JSON::Field(key: "type")]
+  property oembed_type : String
+
+  property provider_name : String
+
+  property provider_url : String
+
+  property thumbnail_height : Int32
+
+  property thumbnail_url : String
+
+  property thumbnail_width : Int32
+
+  property title : String
+
+  property version : String
+
+  property width : Int32
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+  property content : String?
+
+  property height : Int32?
+
+  property scrolling : Bool?
+
+  property width : Int32?
+end
+
+class Preview
+  include JSON::Serializable
+
+  property enabled : Bool
+
+  property images : Array(Image)
+end
+
+class Image
+  include JSON::Serializable
+
+  property id : String
+
+  property resolutions : Array(Source)
+
+  property source : Source
+
+  property variants : Variants
+end
+
+class Source
+  include JSON::Serializable
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
+
+class Variants
+  include JSON::Serializable
+
+end
diff --git a/head/crystal/test/inputs/json/misc/29f47.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/29f47.json/default/TopLevel.cr
new file mode 100644
index 0000000..2454248
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/29f47.json/default/TopLevel.cr
@@ -0,0 +1,214 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : Nil
+
+  property author_flair_text : Nil
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Float64
+
+  property created_utc : Float64
+
+  property distinguished : String
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Edited
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : Nil
+
+  property link_flair_text : Nil
+
+  property locked : Bool
+
+  property media : Nil
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property permalink : String
+
+  property post_hint : String?
+
+  property preview : Preview?
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Nil
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : String?
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : String?
+
+  property thumbnail : String
+
+  property thumbnail_height : Int32?
+
+  property thumbnail_width : Int32?
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+end
+
+class Preview
+  include JSON::Serializable
+
+  property enabled : Bool
+
+  property images : Array(Image)
+end
+
+class Image
+  include JSON::Serializable
+
+  property id : String
+
+  property resolutions : Array(Source)
+
+  property source : Source
+
+  property variants : Variants
+end
+
+class Source
+  include JSON::Serializable
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
+
+class Variants
+  include JSON::Serializable
+
+  property gif : Gif?
+
+  property mp4 : Gif?
+end
+
+class Gif
+  include JSON::Serializable
+
+  property resolutions : Array(Source)
+
+  property source : Source
+end
+
+alias Edited = Float64 | Bool
diff --git a/head/crystal/test/inputs/json/misc/2d4e2.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/2d4e2.json/default/TopLevel.cr
new file mode 100644
index 0000000..d33ecd2
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/2d4e2.json/default/TopLevel.cr
@@ -0,0 +1,123 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property objects : Array(ObjectElement)
+end
+
+class Meta
+  include JSON::Serializable
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
+
+class ObjectElement
+  include JSON::Serializable
+
+  property caucus : String?
+
+  property congress_numbers : Array(Int32)
+
+  property current : Bool
+
+  property description : String
+
+  property district : Nil
+
+  property enddate : String
+
+  property extra : Extra
+
+  property id : Int32
+
+  property leadership_title : String?
+
+  property party : String
+
+  property person : Person
+
+  property phone : String
+
+  property role_type : String
+
+  property role_type_label : String
+
+  property senator_class : String
+
+  property senator_class_label : String
+
+  property senator_rank : String
+
+  property senator_rank_label : String
+
+  property startdate : String
+
+  property state : String
+
+  property title : String
+
+  property title_long : String
+
+  property website : String
+end
+
+class Extra
+  include JSON::Serializable
+
+  property address : String
+
+  property contact_form : String
+
+  property fax : String?
+
+  property office : String
+
+  property rss_url : String?
+end
+
+class Person
+  include JSON::Serializable
+
+  property bioguideid : String
+
+  property birthday : String
+
+  property cspanid : Int32
+
+  property firstname : String
+
+  property gender : String
+
+  property gender_label : String
+
+  property id : Int32
+
+  property lastname : String
+
+  property link : String
+
+  property middlename : String
+
+  property name : String
+
+  property namemod : String
+
+  property nickname : String
+
+  property osid : String
+
+  property pvsid : String
+
+  property sortname : String
+
+  property twitterid : String?
+
+  property youtubeid : String?
+end
diff --git a/head/crystal/test/inputs/json/misc/2df80.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/2df80.json/default/TopLevel.cr
new file mode 100644
index 0000000..dd2a72b
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/2df80.json/default/TopLevel.cr
@@ -0,0 +1,53 @@
+require "json"
+
+alias TopLevel = Array(TopLevelUnion)
+
+class PurpleTopLevel
+  include JSON::Serializable
+
+  property adminregion : Adminregion
+
+  @[JSON::Field(key: "capitalCity")]
+  property capital_city : String
+
+  property id : String
+
+  @[JSON::Field(key: "incomeLevel")]
+  property income_level : Adminregion
+
+  @[JSON::Field(key: "iso2Code")]
+  property iso2_code : String
+
+  property latitude : String
+
+  @[JSON::Field(key: "lendingType")]
+  property lending_type : Adminregion
+
+  property longitude : String
+
+  property name : String
+
+  property region : Adminregion
+end
+
+class Adminregion
+  include JSON::Serializable
+
+  property id : String
+
+  property value : String
+end
+
+class FluffyTopLevel
+  include JSON::Serializable
+
+  property page : Int32
+
+  property pages : Int32
+
+  property per_page : String
+
+  property total : Int32
+end
+
+alias TopLevelUnion = FluffyTopLevel | Array(PurpleTopLevel)
diff --git a/head/crystal/test/inputs/json/misc/31189.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/31189.json/default/TopLevel.cr
new file mode 100644
index 0000000..eaeca63
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/31189.json/default/TopLevel.cr
@@ -0,0 +1,47 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property details : String
+
+  property rates : Array(Rate)
+
+  property version : Nil
+end
+
+class Rate
+  include JSON::Serializable
+
+  property code : String
+
+  property country_code : String
+
+  property name : String
+
+  property periods : Array(Period)
+end
+
+class Period
+  include JSON::Serializable
+
+  property effective_from : String
+
+  property rates : Rates
+end
+
+class Rates
+  include JSON::Serializable
+
+  property parking : Float64?
+
+  property reduced : Float64?
+
+  property reduced1 : Float64?
+
+  property reduced2 : Float64?
+
+  property standard : Float64
+
+  property super_reduced : Float64?
+end
diff --git a/head/crystal/test/inputs/json/misc/32431.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/32431.json/default/TopLevel.cr
new file mode 100644
index 0000000..6ebe445
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/32431.json/default/TopLevel.cr
@@ -0,0 +1,110 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property bbox : Array(Float64)
+
+  property features : Array(Feature)
+
+  property metadata : Metadata
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+end
+
+class Feature
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property feature_type : String
+
+  property geometry : Geometry
+
+  property id : String
+
+  property properties : Properties
+end
+
+class Geometry
+  include JSON::Serializable
+
+  property coordinates : Array(Float64)
+
+  @[JSON::Field(key: "type")]
+  property geometry_type : String
+end
+
+class Properties
+  include JSON::Serializable
+
+  property alert : Nil
+
+  property cdi : Nil
+
+  property code : String
+
+  property detail : String
+
+  property dmin : Float64?
+
+  property felt : Nil
+
+  property gap : Int32?
+
+  property ids : String
+
+  property mag : Float64
+
+  @[JSON::Field(key: "magType")]
+  property mag_type : String
+
+  property mmi : Nil
+
+  property net : String
+
+  property nst : Int32?
+
+  property place : String
+
+  @[JSON::Field(key: "type")]
+  property properties_type : String
+
+  property rms : Float64
+
+  property sig : Int32
+
+  property sources : String
+
+  property status : String
+
+  property time : Float64
+
+  property title : String
+
+  property tsunami : Int32
+
+  property types : String
+
+  property tz : Int32
+
+  property updated : Float64
+
+  property url : String
+end
+
+class Metadata
+  include JSON::Serializable
+
+  property api : String
+
+  property count : Int32
+
+  property generated : Float64
+
+  property status : Int32
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/32d5c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/32d5c.json/default/TopLevel.cr
new file mode 100644
index 0000000..6ef7d6e
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/32d5c.json/default/TopLevel.cr
@@ -0,0 +1,31 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  @[JSON::Field(key: "BirthDate")]
+  property birth_date : String?
+
+  @[JSON::Field(key: "BirthDateIsProtected")]
+  property birth_date_is_protected : Bool
+
+  @[JSON::Field(key: "GenderTypeID")]
+  property gender_type_id : Int32
+
+  @[JSON::Field(key: "Notes")]
+  property notes : String
+
+  @[JSON::Field(key: "ParliamentaryName")]
+  property parliamentary_name : String
+
+  @[JSON::Field(key: "PersonID")]
+  property person_id : Int32
+
+  @[JSON::Field(key: "PhotoURL")]
+  property photo_url : String
+
+  @[JSON::Field(key: "PreferredName")]
+  property preferred_name : String
+end
diff --git a/head/crystal/test/inputs/json/misc/337ed.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/337ed.json/default/TopLevel.cr
new file mode 100644
index 0000000..4a34ff2
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/337ed.json/default/TopLevel.cr
@@ -0,0 +1,116 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property facets : Facets
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property previous : Bool
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : String
+end
+
+class Facets
+  include JSON::Serializable
+
+end
+
+class Result
+  include JSON::Serializable
+
+  property cost_absolute : Int32?
+
+  property cost_max : Int32?
+
+  property cost_min : Int32?
+
+  property created_at : String
+
+  @[JSON::Field(key: "customIncomes")]
+  property custom_incomes : Array(CustomIncome)
+
+  property direct_rep_costs_max : Nil
+
+  property direct_rep_costs_min : Nil
+
+  property end_date : String
+
+  property eur_sources_grants : Int32
+
+  property eur_sources_grants_src : String?
+
+  property eur_sources_procurement : Int32
+
+  property eur_sources_procurement_src : String?
+
+  property id : String
+
+  property new_organisation : Nil
+
+  property no_clients : String?
+
+  property other_financial_information : String?
+
+  property other_sources_contributions : Int32?
+
+  property other_sources_donation : Int32?
+
+  property other_sources_total : Int32?
+
+  property public_financing_infranational : Int32?
+
+  property public_financing_national : Int32?
+
+  property public_financing_total : Int32?
+
+  property representative : String
+
+  @[JSON::Field(key: "type")]
+  property result_type : String
+
+  property start_date : String
+
+  property status : String
+
+  property total_budget : Int32?
+
+  property turnover_absolute : Int32?
+
+  property turnover_max : Int32?
+
+  property turnover_min : Int32?
+
+  property updated_at : String
+
+  property uri : String
+end
+
+class CustomIncome
+  include JSON::Serializable
+
+  property amount : Int32
+
+  property created_at : String
+
+  @[JSON::Field(key: "type")]
+  property custom_income_type : String
+
+  property id : String
+
+  property name : String
+
+  property status : String
+
+  property updated_at : String
+
+  property uri : String
+end
diff --git a/head/crystal/test/inputs/json/misc/33d2e.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/33d2e.json/default/TopLevel.cr
new file mode 100644
index 0000000..99f37ca
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/33d2e.json/default/TopLevel.cr
@@ -0,0 +1,72 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property laureates : Array(Laureate)
+end
+
+class Laureate
+  include JSON::Serializable
+
+  property born : String
+
+  @[JSON::Field(key: "bornCity")]
+  property born_city : String?
+
+  @[JSON::Field(key: "bornCountry")]
+  property born_country : String?
+
+  @[JSON::Field(key: "bornCountryCode")]
+  property born_country_code : String?
+
+  property died : String
+
+  @[JSON::Field(key: "diedCity")]
+  property died_city : String?
+
+  @[JSON::Field(key: "diedCountry")]
+  property died_country : String?
+
+  @[JSON::Field(key: "diedCountryCode")]
+  property died_country_code : String?
+
+  property firstname : String?
+
+  property gender : String
+
+  property id : String
+
+  property prizes : Array(Prize)
+
+  property surname : String?
+end
+
+class Prize
+  include JSON::Serializable
+
+  property affiliations : Array(AffiliationElement)
+
+  property category : String?
+
+  property motivation : String?
+
+  @[JSON::Field(key: "overallMotivation")]
+  property overall_motivation : String?
+
+  property share : String?
+
+  property year : String?
+end
+
+class AffiliationClass
+  include JSON::Serializable
+
+  property city : String?
+
+  property country : String?
+
+  property name : String?
+end
+
+alias AffiliationElement = AffiliationClass | Array(JSON::Any?)
diff --git a/head/crystal/test/inputs/json/misc/34702.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/34702.json/default/TopLevel.cr
new file mode 100644
index 0000000..be0ee39
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/34702.json/default/TopLevel.cr
@@ -0,0 +1,89 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property crs : Crs
+
+  property features : Array(Feature)
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+
+  @[JSON::Field(key: "totalFeatures")]
+  property total_features : Int32
+end
+
+class Crs
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property crs_type : String
+
+  property properties : CrsProperties
+end
+
+class CrsProperties
+  include JSON::Serializable
+
+  property name : String
+end
+
+class Feature
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property feature_type : String
+
+  property geometry : Geometry
+
+  property geometry_name : String
+
+  property id : String
+
+  property properties : FeatureProperties
+end
+
+class Geometry
+  include JSON::Serializable
+
+  property coordinates : Array(Float64)
+
+  @[JSON::Field(key: "type")]
+  property geometry_type : String
+end
+
+class FeatureProperties
+  include JSON::Serializable
+
+  property division : String
+
+  property fax : Float64
+
+  property fire_ban_r : String?
+
+  property latitude : Float64
+
+  property local_govt : String
+
+  property longitude : Float64
+
+  property no : String?
+
+  property phone : Float64
+
+  property postcode : Int32
+
+  @[JSON::Field(key: "type")]
+  property properties_type : String?
+
+  property psa : String
+
+  property region : String
+
+  property station : String
+
+  property street : String
+
+  property suburb : String
+end
diff --git a/head/crystal/test/inputs/json/misc/3536b.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/3536b.json/default/TopLevel.cr
new file mode 100644
index 0000000..2adb667
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/3536b.json/default/TopLevel.cr
@@ -0,0 +1,49 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(JSON::Any?)
+
+  property superseded_by : String
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/3659d.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/3659d.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/3659d.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/36d5d.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/36d5d.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/36d5d.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/3a6b3.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/3a6b3.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/3a6b3.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/3e9a3.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/3e9a3.json/default/TopLevel.cr
new file mode 100644
index 0000000..54f6724
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/3e9a3.json/default/TopLevel.cr
@@ -0,0 +1,53 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property message : Array(JSON::Any?)
+
+  @[JSON::Field(key: "responseTime")]
+  property response_time : Int32
+
+  @[JSON::Field(key: "Results")]
+  property results : Results
+
+  property status : String
+end
+
+class Results
+  include JSON::Serializable
+
+  property series : Array(Series)
+end
+
+class Series
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  @[JSON::Field(key: "seriesID")]
+  property series_id : String
+end
+
+class Datum
+  include JSON::Serializable
+
+  property footnotes : Array(Footnote)
+
+  property period : String
+
+  @[JSON::Field(key: "periodName")]
+  property period_name : String
+
+  property value : String
+
+  property year : String
+end
+
+class Footnote
+  include JSON::Serializable
+
+  property code : String?
+
+  property text : String?
+end
diff --git a/head/crystal/test/inputs/json/misc/3f1ce.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/3f1ce.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/3f1ce.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/421d4.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/421d4.json/default/TopLevel.cr
new file mode 100644
index 0000000..afe0e11
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/421d4.json/default/TopLevel.cr
@@ -0,0 +1,246 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Array(Datum?))
+
+  property meta : Meta
+end
+
+class Meta
+  include JSON::Serializable
+
+  property view : View
+end
+
+class View
+  include JSON::Serializable
+
+  property attribution : String
+
+  @[JSON::Field(key: "averageRating")]
+  property average_rating : Int32
+
+  property category : String
+
+  property columns : Array(Column)
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : Int32
+
+  property description : String
+
+  @[JSON::Field(key: "displayType")]
+  property display_type : String
+
+  @[JSON::Field(key: "downloadCount")]
+  property download_count : Int32
+
+  property flags : Array(String)
+
+  property grants : Array(Grant)
+
+  @[JSON::Field(key: "hideFromCatalog")]
+  property hide_from_catalog : Bool
+
+  @[JSON::Field(key: "hideFromDataJson")]
+  property hide_from_data_json : Bool
+
+  property id : String
+
+  @[JSON::Field(key: "indexUpdatedAt")]
+  property index_updated_at : Int32
+
+  property license : License
+
+  @[JSON::Field(key: "licenseId")]
+  property license_id : String
+
+  property locale : String
+
+  property metadata : Metadata
+
+  property name : String
+
+  @[JSON::Field(key: "newBackend")]
+  property new_backend : Bool
+
+  @[JSON::Field(key: "numberOfComments")]
+  property number_of_comments : Int32
+
+  property oid : Int32
+
+  property owner : Owner
+
+  property provenance : String
+
+  @[JSON::Field(key: "publicationAppendEnabled")]
+  property publication_append_enabled : Bool
+
+  @[JSON::Field(key: "publicationDate")]
+  property publication_date : Int32
+
+  @[JSON::Field(key: "publicationGroup")]
+  property publication_group : Int32
+
+  @[JSON::Field(key: "publicationStage")]
+  property publication_stage : String
+
+  property query : Query
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "rowClass")]
+  property row_class : String
+
+  @[JSON::Field(key: "rowsUpdatedAt")]
+  property rows_updated_at : Int32
+
+  @[JSON::Field(key: "rowsUpdatedBy")]
+  property rows_updated_by : String
+
+  @[JSON::Field(key: "tableAuthor")]
+  property table_author : Owner
+
+  @[JSON::Field(key: "tableId")]
+  property table_id : Int32
+
+  property tags : Array(String)
+
+  @[JSON::Field(key: "totalTimesRated")]
+  property total_times_rated : Int32
+
+  @[JSON::Field(key: "viewCount")]
+  property view_count : Int32
+
+  @[JSON::Field(key: "viewLastModified")]
+  property view_last_modified : Int32
+
+  @[JSON::Field(key: "viewType")]
+  property view_type : String
+end
+
+class Column
+  include JSON::Serializable
+
+  @[JSON::Field(key: "cachedContents")]
+  property cached_contents : CachedContents?
+
+  @[JSON::Field(key: "dataTypeName")]
+  property data_type_name : String
+
+  @[JSON::Field(key: "fieldName")]
+  property field_name : String
+
+  property flags : Array(String)?
+
+  property format : Query
+
+  property id : Int32
+
+  property name : String
+
+  property position : Int32
+
+  @[JSON::Field(key: "renderTypeName")]
+  property render_type_name : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : Int32?
+
+  property width : Int32?
+end
+
+class CachedContents
+  include JSON::Serializable
+
+  property average : String?
+
+  property largest : String?
+
+  property non_null : Int32
+
+  property null : Int32
+
+  property smallest : String?
+
+  property sum : String?
+
+  property top : Array(Top)?
+end
+
+class Top
+  include JSON::Serializable
+
+  property count : Int32
+
+  property item : String
+end
+
+class Query
+  include JSON::Serializable
+
+end
+
+class Grant
+  include JSON::Serializable
+
+  property flags : Array(String)
+
+  @[JSON::Field(key: "type")]
+  property grant_type : String
+
+  property inherited : Bool
+end
+
+class License
+  include JSON::Serializable
+
+  property name : String
+end
+
+class Metadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "availableDisplayTypes")]
+  property available_display_types : Array(String)
+
+  @[JSON::Field(key: "rdfClass")]
+  property rdf_class : String
+
+  @[JSON::Field(key: "rdfSubject")]
+  property rdf_subject : String
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : RenderTypeConfig
+
+  @[JSON::Field(key: "rowIdentifier")]
+  property row_identifier : String
+end
+
+class RenderTypeConfig
+  include JSON::Serializable
+
+  property visible : Visible
+end
+
+class Visible
+  include JSON::Serializable
+
+  property table : Bool
+end
+
+class Owner
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property id : String
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
+
+alias Datum = Int32 | String
diff --git a/head/crystal/test/inputs/json/misc/437e7.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/437e7.json/default/TopLevel.cr
new file mode 100644
index 0000000..b3db45e
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/437e7.json/default/TopLevel.cr
@@ -0,0 +1,190 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  property meta : Meta
+
+  property pagination : Pagination
+end
+
+class Datum
+  include JSON::Serializable
+
+  property bitly_gif_url : String
+
+  property bitly_url : String
+
+  property content_url : String
+
+  @[JSON::Field(key: "type")]
+  property datum_type : String
+
+  property embed_url : String
+
+  property id : String
+
+  property images : Images
+
+  property import_datetime : String
+
+  property is_indexable : Int32
+
+  property rating : String
+
+  property slug : String
+
+  property source : String
+
+  property source_post_url : String
+
+  property source_tld : String
+
+  property trending_datetime : String
+
+  property url : String
+
+  property user : User?
+
+  property username : String
+end
+
+class Images
+  include JSON::Serializable
+
+  property downsized : Downsized
+
+  property downsized_large : Downsized
+
+  property downsized_medium : Downsized
+
+  property downsized_small : DownsizedSmall
+
+  property downsized_still : Downsized
+
+  property fixed_height : FixedHeight
+
+  property fixed_height_downsampled : FixedHeight
+
+  property fixed_height_small : FixedHeight
+
+  property fixed_height_small_still : Downsized
+
+  property fixed_height_still : Downsized
+
+  property fixed_width : FixedHeight
+
+  property fixed_width_downsampled : FixedHeight
+
+  property fixed_width_small : FixedHeight
+
+  property fixed_width_small_still : Downsized
+
+  property fixed_width_still : Downsized
+
+  property looping : Looping
+
+  property original : FixedHeight
+
+  property original_mp4 : DownsizedSmall
+
+  property original_still : Downsized
+
+  property preview : DownsizedSmall
+
+  property preview_gif : Downsized
+
+  property preview_webp : Downsized
+end
+
+class Downsized
+  include JSON::Serializable
+
+  property height : String
+
+  property size : String?
+
+  property url : String
+
+  property width : String
+end
+
+class DownsizedSmall
+  include JSON::Serializable
+
+  property height : String
+
+  property mp4 : String
+
+  property mp4_size : String
+
+  property width : String
+end
+
+class FixedHeight
+  include JSON::Serializable
+
+  property frames : String?
+
+  property height : String
+
+  property mp4 : String?
+
+  property mp4_size : String?
+
+  property size : String
+
+  property url : String
+
+  property webp : String
+
+  property webp_size : String
+
+  property width : String
+end
+
+class Looping
+  include JSON::Serializable
+
+  property mp4 : String
+
+  property mp4_size : String
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property banner_url : String
+
+  property display_name : String
+
+  property profile_url : String
+
+  property twitter : String
+
+  property username : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property msg : String
+
+  property response_id : String
+
+  property status : Int32
+end
+
+class Pagination
+  include JSON::Serializable
+
+  property count : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/43970.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/43970.json/default/TopLevel.cr
new file mode 100644
index 0000000..fb78e63
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/43970.json/default/TopLevel.cr
@@ -0,0 +1,16 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  @[JSON::Field(key: "ID")]
+  property id : Int32
+
+  @[JSON::Field(key: "Name")]
+  property name : String
+
+  @[JSON::Field(key: "Notes")]
+  property notes : String
+end
diff --git a/head/crystal/test/inputs/json/misc/43eaf.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/43eaf.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/43eaf.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/458db.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/458db.json/default/TopLevel.cr
new file mode 100644
index 0000000..744e08e
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/458db.json/default/TopLevel.cr
@@ -0,0 +1,78 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property metadata : Metadata
+
+  property results : Array(Result)
+end
+
+class Metadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "executionTime")]
+  property execution_time : Float64
+
+  @[JSON::Field(key: "responseInfo")]
+  property response_info : ResponseInfo
+
+  property resultset : Resultset
+end
+
+class ResponseInfo
+  include JSON::Serializable
+
+  @[JSON::Field(key: "developerMessage")]
+  property developer_message : String
+
+  property status : Int32
+end
+
+class Resultset
+  include JSON::Serializable
+
+  property count : Int32
+
+  property page : Int32
+
+  property pagesize : Int32
+end
+
+class Result
+  include JSON::Serializable
+
+  property attachments : Array(JSON::Any?)
+
+  property body : String
+
+  property changed : String
+
+  property component : Array(Component)
+
+  property created : String
+
+  property date : String
+
+  property image : Array(JSON::Any?)
+
+  property teaser : Array(JSON::Any?)
+
+  property title : String
+
+  property topic : Array(JSON::Any?)
+
+  property url : String
+
+  property uuid : String
+
+  property vuuid : String
+end
+
+class Component
+  include JSON::Serializable
+
+  property name : String
+
+  property uuid : String
+end
diff --git a/head/crystal/test/inputs/json/misc/4961a.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/4961a.json/default/TopLevel.cr
new file mode 100644
index 0000000..117eadd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/4961a.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Float64
+end
diff --git a/head/crystal/test/inputs/json/misc/4a0d7.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/4a0d7.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/4a0d7.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/4a455.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/4a455.json/default/TopLevel.cr
new file mode 100644
index 0000000..b1eb97b
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/4a455.json/default/TopLevel.cr
@@ -0,0 +1,70 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property crs : Crs
+
+  property features : Array(Feature)
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+
+  @[JSON::Field(key: "totalFeatures")]
+  property total_features : Int32
+end
+
+class Crs
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property crs_type : String
+
+  property properties : CrsProperties
+end
+
+class CrsProperties
+  include JSON::Serializable
+
+  property name : String
+end
+
+class Feature
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property feature_type : String
+
+  property geometry : Geometry
+
+  property geometry_name : String
+
+  property id : String
+
+  property properties : FeatureProperties
+end
+
+class Geometry
+  include JSON::Serializable
+
+  property coordinates : Array(Array(Float64))
+
+  @[JSON::Field(key: "type")]
+  property geometry_type : String
+end
+
+class FeatureProperties
+  include JSON::Serializable
+
+  property facebookaccount : String
+
+  property frequencyfinderurl : String
+
+  property name : String
+
+  property siteurl : String
+
+  property streetaddress : String
+
+  property twitteraccount : String
+end
diff --git a/head/crystal/test/inputs/json/misc/4c547.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/4c547.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/4c547.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/4d6fb.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/4d6fb.json/default/TopLevel.cr
new file mode 100644
index 0000000..f94b063
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/4d6fb.json/default/TopLevel.cr
@@ -0,0 +1,248 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : Nil
+
+  property author_flair_text : Nil
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Float64
+
+  property created_utc : Float64
+
+  property distinguished : Nil
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Bool
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : Nil
+
+  property link_flair_text : Nil
+
+  property locked : Bool
+
+  property media : Media?
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property permalink : String
+
+  property post_hint : String?
+
+  property preview : Preview?
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Media?
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : Nil
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : Nil
+
+  property thumbnail : String
+
+  property thumbnail_height : Int32?
+
+  property thumbnail_width : Int32?
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+end
+
+class Media
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property media_type : String
+
+  property oembed : Oembed
+end
+
+class Oembed
+  include JSON::Serializable
+
+  property author_name : String
+
+  property author_url : String
+
+  property height : Int32
+
+  property html : String
+
+  @[JSON::Field(key: "type")]
+  property oembed_type : String
+
+  property provider_name : String
+
+  property provider_url : String
+
+  property thumbnail_height : Int32
+
+  property thumbnail_url : String
+
+  property thumbnail_width : Int32
+
+  property title : String
+
+  property version : String
+
+  property width : Int32
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+  property content : String?
+
+  property height : Int32?
+
+  property scrolling : Bool?
+
+  property width : Int32?
+end
+
+class Preview
+  include JSON::Serializable
+
+  property enabled : Bool
+
+  property images : Array(Image)
+end
+
+class Image
+  include JSON::Serializable
+
+  property id : String
+
+  property resolutions : Array(Source)
+
+  property source : Source
+
+  property variants : Variants
+end
+
+class Source
+  include JSON::Serializable
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
+
+class Variants
+  include JSON::Serializable
+
+end
diff --git a/head/crystal/test/inputs/json/misc/4e336.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/4e336.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/4e336.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/54147.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/54147.json/default/TopLevel.cr
new file mode 100644
index 0000000..948480f
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/54147.json/default/TopLevel.cr
@@ -0,0 +1,40 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property args : Args
+
+  property data : String
+
+  property files : Args
+
+  property form : Args
+
+  property headers : Headers
+
+  property origin : String
+
+  property url : String
+end
+
+class Args
+  include JSON::Serializable
+
+end
+
+class Headers
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Accept-Encoding")]
+  property accept_encoding : String
+
+  @[JSON::Field(key: "Connection")]
+  property connection : String
+
+  @[JSON::Field(key: "Host")]
+  property host : String
+
+  @[JSON::Field(key: "User-Agent")]
+  property user_agent : String
+end
diff --git a/head/crystal/test/inputs/json/misc/54d32.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/54d32.json/default/TopLevel.cr
new file mode 100644
index 0000000..c7bd2d7
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/54d32.json/default/TopLevel.cr
@@ -0,0 +1,41 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property facets : Facets
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property previous : Bool
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : String
+end
+
+class Facets
+  include JSON::Serializable
+
+end
+
+class Result
+  include JSON::Serializable
+
+  property created_at : String
+
+  property id : String
+
+  property person_id : String
+
+  property representative_id : String
+
+  property status : String
+
+  property updated_at : String
+end
diff --git a/head/crystal/test/inputs/json/misc/570ec.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/570ec.json/default/TopLevel.cr
new file mode 100644
index 0000000..05c134e
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/570ec.json/default/TopLevel.cr
@@ -0,0 +1,57 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(OtherName)
+
+  property superseded_by : Nil
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class OtherName
+  include JSON::Serializable
+
+  property name : String
+
+  property note : String?
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/5dd0d.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/5dd0d.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/5dd0d.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/5eae5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/5eae5.json/default/TopLevel.cr
new file mode 100644
index 0000000..a09600d
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/5eae5.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Date")]
+  property date : String
+
+  @[JSON::Field(key: "ID")]
+  property id : Int32
+
+  @[JSON::Field(key: "Sponsor")]
+  property sponsor : String
+
+  @[JSON::Field(key: "Title")]
+  property title : String
+end
diff --git a/head/crystal/test/inputs/json/misc/5eb20.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/5eb20.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/5eb20.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/5f3a1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/5f3a1.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/5f3a1.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/5f7fe.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/5f7fe.json/default/TopLevel.cr
new file mode 100644
index 0000000..e98a41a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/5f7fe.json/default/TopLevel.cr
@@ -0,0 +1,390 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Array(Datum?))
+
+  property meta : Meta
+end
+
+class Meta
+  include JSON::Serializable
+
+  property view : View
+end
+
+class View
+  include JSON::Serializable
+
+  property attribution : String
+
+  @[JSON::Field(key: "attributionLink")]
+  property attribution_link : String
+
+  @[JSON::Field(key: "averageRating")]
+  property average_rating : Int32
+
+  property category : String
+
+  property columns : Array(Column)
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : Int32
+
+  property description : String
+
+  @[JSON::Field(key: "displayType")]
+  property display_type : String
+
+  @[JSON::Field(key: "downloadCount")]
+  property download_count : Int32
+
+  property flags : Array(String)
+
+  property grants : Array(Grant)
+
+  @[JSON::Field(key: "hideFromCatalog")]
+  property hide_from_catalog : Bool
+
+  @[JSON::Field(key: "hideFromDataJson")]
+  property hide_from_data_json : Bool
+
+  property id : String
+
+  @[JSON::Field(key: "indexUpdatedAt")]
+  property index_updated_at : Int32
+
+  property locale : String
+
+  property metadata : Metadata
+
+  property name : String
+
+  @[JSON::Field(key: "newBackend")]
+  property new_backend : Bool
+
+  @[JSON::Field(key: "numberOfComments")]
+  property number_of_comments : Int32
+
+  property oid : Int32
+
+  property owner : Owner
+
+  property provenance : String
+
+  @[JSON::Field(key: "publicationAppendEnabled")]
+  property publication_append_enabled : Bool
+
+  @[JSON::Field(key: "publicationDate")]
+  property publication_date : Int32
+
+  @[JSON::Field(key: "publicationGroup")]
+  property publication_group : Int32
+
+  @[JSON::Field(key: "publicationStage")]
+  property publication_stage : String
+
+  property query : Query
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "rowsUpdatedAt")]
+  property rows_updated_at : Int32
+
+  @[JSON::Field(key: "rowsUpdatedBy")]
+  property rows_updated_by : String
+
+  @[JSON::Field(key: "tableAuthor")]
+  property table_author : Owner
+
+  @[JSON::Field(key: "tableId")]
+  property table_id : Int32
+
+  property tags : Array(String)
+
+  @[JSON::Field(key: "totalTimesRated")]
+  property total_times_rated : Int32
+
+  @[JSON::Field(key: "viewCount")]
+  property view_count : Int32
+
+  @[JSON::Field(key: "viewLastModified")]
+  property view_last_modified : Int32
+
+  @[JSON::Field(key: "viewType")]
+  property view_type : String
+end
+
+class Column
+  include JSON::Serializable
+
+  @[JSON::Field(key: "cachedContents")]
+  property cached_contents : CachedContents?
+
+  @[JSON::Field(key: "dataTypeName")]
+  property data_type_name : String
+
+  @[JSON::Field(key: "fieldName")]
+  property field_name : String
+
+  property flags : Array(String)?
+
+  property format : Format
+
+  property id : Int32
+
+  property name : String
+
+  property position : Int32
+
+  @[JSON::Field(key: "renderTypeName")]
+  property render_type_name : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : Int32?
+
+  property width : Int32?
+end
+
+class CachedContents
+  include JSON::Serializable
+
+  property largest : String
+
+  property non_null : Int32
+
+  property null : Int32
+
+  property smallest : String
+
+  property top : Array(Top)
+end
+
+class Top
+  include JSON::Serializable
+
+  property count : Int32
+
+  property item : String
+end
+
+class Format
+  include JSON::Serializable
+
+  property align : String?
+
+  property view : String?
+end
+
+class Grant
+  include JSON::Serializable
+
+  property flags : Array(String)
+
+  @[JSON::Field(key: "type")]
+  property grant_type : String
+
+  property inherited : Bool
+end
+
+class Metadata
+  include JSON::Serializable
+
+  property attachments : Array(Attachment)
+
+  @[JSON::Field(key: "availableDisplayTypes")]
+  property available_display_types : Array(String)
+
+  property custom_fields : CustomFields
+
+  @[JSON::Field(key: "jsonQuery")]
+  property json_query : JsonQuery
+
+  @[JSON::Field(key: "rdfSubject")]
+  property rdf_subject : String
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : RenderTypeConfig
+end
+
+class Attachment
+  include JSON::Serializable
+
+  @[JSON::Field(key: "assetId")]
+  property asset_id : String
+
+  @[JSON::Field(key: "blobId")]
+  property blob_id : String
+
+  property filename : String
+
+  property name : String
+end
+
+class CustomFields
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Additional Resources")]
+  property additional_resources : AdditionalResources
+
+  @[JSON::Field(key: "Common Core")]
+  property common_core : CommonCore
+
+  @[JSON::Field(key: "Dataset Information")]
+  property dataset_information : DatasetInformation
+
+  @[JSON::Field(key: "Dataset Summary")]
+  property dataset_summary : DatasetSummary
+
+  @[JSON::Field(key: "Notes")]
+  property notes : Notes
+end
+
+class AdditionalResources
+  include JSON::Serializable
+
+  @[JSON::Field(key: "See Also ")]
+  property additional_resources_see_also : String
+
+  @[JSON::Field(key: "See Also")]
+  property see_also : String
+end
+
+class CommonCore
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Contact Email")]
+  property contact_email : String
+
+  @[JSON::Field(key: "Contact Name")]
+  property contact_name : String
+
+  @[JSON::Field(key: "Publisher")]
+  property publisher : String
+end
+
+class DatasetInformation
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Agency")]
+  property agency : String
+end
+
+class DatasetSummary
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Contact Information")]
+  property contact_information : String
+
+  @[JSON::Field(key: "Coverage")]
+  property coverage : String
+
+  @[JSON::Field(key: "Data Frequency")]
+  property data_frequency : String
+
+  @[JSON::Field(key: "Dataset Owner")]
+  property dataset_owner : String
+
+  @[JSON::Field(key: "Granularity")]
+  property granularity : String
+
+  @[JSON::Field(key: "Organization")]
+  property organization : String
+
+  @[JSON::Field(key: "Posting Frequency")]
+  property posting_frequency : String
+
+  @[JSON::Field(key: "Time Period")]
+  property time_period : String
+
+  @[JSON::Field(key: "Units")]
+  property units : String
+end
+
+class Notes
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Notes")]
+  property notes : String
+end
+
+class JsonQuery
+  include JSON::Serializable
+
+  property order : Array(Order)
+end
+
+class Order
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String
+end
+
+class RenderTypeConfig
+  include JSON::Serializable
+
+  property visible : Visible
+end
+
+class Visible
+  include JSON::Serializable
+
+  property table : Bool
+end
+
+class Owner
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property id : String
+
+  @[JSON::Field(key: "profileImageUrlLarge")]
+  property profile_image_url_large : String
+
+  @[JSON::Field(key: "profileImageUrlMedium")]
+  property profile_image_url_medium : String
+
+  @[JSON::Field(key: "profileImageUrlSmall")]
+  property profile_image_url_small : String
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "roleName")]
+  property role_name : String
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
+
+class Query
+  include JSON::Serializable
+
+  @[JSON::Field(key: "orderBys")]
+  property order_bys : Array(OrderBy)
+end
+
+class OrderBy
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  property expression : Expression
+end
+
+class Expression
+  include JSON::Serializable
+
+  @[JSON::Field(key: "columnId")]
+  property column_id : Int32
+
+  @[JSON::Field(key: "type")]
+  property expression_type : String
+end
+
+alias Datum = Int32 | String
diff --git a/head/crystal/test/inputs/json/misc/617e8.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/617e8.json/default/TopLevel.cr
new file mode 100644
index 0000000..d71cfcd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/617e8.json/default/TopLevel.cr
@@ -0,0 +1,386 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Array(Datum?))
+
+  property meta : Meta
+end
+
+class Meta
+  include JSON::Serializable
+
+  property view : View
+end
+
+class View
+  include JSON::Serializable
+
+  property attribution : String
+
+  @[JSON::Field(key: "attributionLink")]
+  property attribution_link : String
+
+  @[JSON::Field(key: "averageRating")]
+  property average_rating : Int32
+
+  property category : String
+
+  property columns : Array(Column)
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : Int32
+
+  property description : String
+
+  @[JSON::Field(key: "displayType")]
+  property display_type : String
+
+  @[JSON::Field(key: "downloadCount")]
+  property download_count : Int32
+
+  property flags : Array(String)
+
+  property grants : Array(Grant)
+
+  @[JSON::Field(key: "hideFromCatalog")]
+  property hide_from_catalog : Bool
+
+  @[JSON::Field(key: "hideFromDataJson")]
+  property hide_from_data_json : Bool
+
+  property id : String
+
+  @[JSON::Field(key: "indexUpdatedAt")]
+  property index_updated_at : Int32
+
+  property locale : String
+
+  property metadata : Metadata
+
+  property name : String
+
+  @[JSON::Field(key: "newBackend")]
+  property new_backend : Bool
+
+  @[JSON::Field(key: "numberOfComments")]
+  property number_of_comments : Int32
+
+  property oid : Int32
+
+  property owner : Owner
+
+  property provenance : String
+
+  @[JSON::Field(key: "publicationAppendEnabled")]
+  property publication_append_enabled : Bool
+
+  @[JSON::Field(key: "publicationDate")]
+  property publication_date : Int32
+
+  @[JSON::Field(key: "publicationGroup")]
+  property publication_group : Int32
+
+  @[JSON::Field(key: "publicationStage")]
+  property publication_stage : String
+
+  property query : Query
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "rowsUpdatedAt")]
+  property rows_updated_at : Int32
+
+  @[JSON::Field(key: "rowsUpdatedBy")]
+  property rows_updated_by : String
+
+  @[JSON::Field(key: "tableAuthor")]
+  property table_author : Owner
+
+  @[JSON::Field(key: "tableId")]
+  property table_id : Int32
+
+  property tags : Array(String)
+
+  @[JSON::Field(key: "totalTimesRated")]
+  property total_times_rated : Int32
+
+  @[JSON::Field(key: "viewCount")]
+  property view_count : Int32
+
+  @[JSON::Field(key: "viewLastModified")]
+  property view_last_modified : Int32
+
+  @[JSON::Field(key: "viewType")]
+  property view_type : String
+end
+
+class Column
+  include JSON::Serializable
+
+  @[JSON::Field(key: "cachedContents")]
+  property cached_contents : CachedContents?
+
+  @[JSON::Field(key: "dataTypeName")]
+  property data_type_name : String
+
+  property description : String?
+
+  @[JSON::Field(key: "fieldName")]
+  property field_name : String
+
+  property flags : Array(String)?
+
+  property format : Format
+
+  property id : Int32
+
+  property name : String
+
+  property position : Int32
+
+  @[JSON::Field(key: "renderTypeName")]
+  property render_type_name : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : Int32?
+
+  property width : Int32?
+end
+
+class CachedContents
+  include JSON::Serializable
+
+  property largest : String
+
+  property non_null : Int32
+
+  property null : Int32
+
+  property smallest : String
+
+  property top : Array(Top)
+end
+
+class Top
+  include JSON::Serializable
+
+  property count : Int32
+
+  property item : String
+end
+
+class Format
+  include JSON::Serializable
+
+  property align : String?
+
+  property view : String?
+end
+
+class Grant
+  include JSON::Serializable
+
+  property flags : Array(String)
+
+  @[JSON::Field(key: "type")]
+  property grant_type : String
+
+  property inherited : Bool
+end
+
+class Metadata
+  include JSON::Serializable
+
+  property attachments : Array(Attachment)
+
+  @[JSON::Field(key: "availableDisplayTypes")]
+  property available_display_types : Array(String)
+
+  property custom_fields : CustomFields
+
+  @[JSON::Field(key: "jsonQuery")]
+  property json_query : JsonQuery
+
+  @[JSON::Field(key: "rdfSubject")]
+  property rdf_subject : String
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : RenderTypeConfig
+end
+
+class Attachment
+  include JSON::Serializable
+
+  @[JSON::Field(key: "assetId")]
+  property asset_id : String
+
+  @[JSON::Field(key: "blobId")]
+  property blob_id : String
+
+  property filename : String
+
+  property name : String
+end
+
+class CustomFields
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Additional Resources")]
+  property additional_resources : AdditionalResources
+
+  @[JSON::Field(key: "Common Core")]
+  property common_core : CommonCore
+
+  @[JSON::Field(key: "Dataset Information")]
+  property dataset_information : DatasetInformation
+
+  @[JSON::Field(key: "Dataset Summary")]
+  property dataset_summary : DatasetSummary
+
+  @[JSON::Field(key: "Notes")]
+  property notes : Notes
+end
+
+class AdditionalResources
+  include JSON::Serializable
+
+  @[JSON::Field(key: "See Also")]
+  property see_also : String
+end
+
+class CommonCore
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Contact Email")]
+  property contact_email : String
+
+  @[JSON::Field(key: "Contact Name")]
+  property contact_name : String
+
+  @[JSON::Field(key: "Publisher")]
+  property publisher : String
+end
+
+class DatasetInformation
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Agency")]
+  property agency : String
+end
+
+class DatasetSummary
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Contact Information")]
+  property contact_information : String
+
+  @[JSON::Field(key: "Coverage")]
+  property coverage : String
+
+  @[JSON::Field(key: "Data Frequency")]
+  property data_frequency : String
+
+  @[JSON::Field(key: "Dataset Owner")]
+  property dataset_owner : String
+
+  @[JSON::Field(key: "Granularity")]
+  property granularity : String
+
+  @[JSON::Field(key: "Organization")]
+  property organization : String
+
+  @[JSON::Field(key: "Posting Frequency")]
+  property posting_frequency : String
+
+  @[JSON::Field(key: "Time Period")]
+  property time_period : String
+end
+
+class Notes
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Notes")]
+  property notes : String
+end
+
+class JsonQuery
+  include JSON::Serializable
+
+  property order : Array(Order)
+end
+
+class Order
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String
+end
+
+class RenderTypeConfig
+  include JSON::Serializable
+
+  property visible : Visible
+end
+
+class Visible
+  include JSON::Serializable
+
+  property table : Bool
+end
+
+class Owner
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property id : String
+
+  @[JSON::Field(key: "profileImageUrlLarge")]
+  property profile_image_url_large : String
+
+  @[JSON::Field(key: "profileImageUrlMedium")]
+  property profile_image_url_medium : String
+
+  @[JSON::Field(key: "profileImageUrlSmall")]
+  property profile_image_url_small : String
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "roleName")]
+  property role_name : String
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
+
+class Query
+  include JSON::Serializable
+
+  @[JSON::Field(key: "orderBys")]
+  property order_bys : Array(OrderBy)
+end
+
+class OrderBy
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  property expression : Expression
+end
+
+class Expression
+  include JSON::Serializable
+
+  @[JSON::Field(key: "columnId")]
+  property column_id : Int32
+
+  @[JSON::Field(key: "type")]
+  property expression_type : String
+end
+
+alias Datum = Int32 | String
diff --git a/head/crystal/test/inputs/json/misc/61b66.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/61b66.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/61b66.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/6260a.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/6260a.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/6260a.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/65dec.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/65dec.json/default/TopLevel.cr
new file mode 100644
index 0000000..37b085f
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/65dec.json/default/TopLevel.cr
@@ -0,0 +1,114 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property booster : Array(String)
+
+  property border : String
+
+  property cards : Array(Card)
+
+  property code : String
+
+  @[JSON::Field(key: "gathererCode")]
+  property gatherer_code : String
+
+  @[JSON::Field(key: "magicCardsInfoCode")]
+  property magic_cards_info_code : String
+
+  property mkm_id : Int32
+
+  property mkm_name : String
+
+  property name : String
+
+  @[JSON::Field(key: "releaseDate")]
+  property release_date : String
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+end
+
+class Card
+  include JSON::Serializable
+
+  property artist : String
+
+  @[JSON::Field(key: "type")]
+  property card_type : String
+
+  property cmc : Int32
+
+  @[JSON::Field(key: "colorIdentity")]
+  property color_identity : Array(String)?
+
+  property colors : Array(String)?
+
+  property flavor : String?
+
+  property id : String
+
+  @[JSON::Field(key: "imageName")]
+  property image_name : String
+
+  property layout : String
+
+  property legalities : Array(LegalityElement)
+
+  @[JSON::Field(key: "manaCost")]
+  property mana_cost : String?
+
+  @[JSON::Field(key: "mciNumber")]
+  property mci_number : String?
+
+  property multiverseid : Int32
+
+  property name : String
+
+  @[JSON::Field(key: "originalText")]
+  property original_text : String
+
+  @[JSON::Field(key: "originalType")]
+  property original_type : String
+
+  property power : String?
+
+  property printings : Array(String)
+
+  property rarity : String
+
+  property reserved : Bool?
+
+  property rulings : Array(Ruling)?
+
+  property subtypes : Array(String)?
+
+  property supertypes : Array(String)?
+
+  property text : String?
+
+  property toughness : String?
+
+  property types : Array(String)
+
+  property variations : Array(Int32)?
+
+  property watermark : String?
+end
+
+class LegalityElement
+  include JSON::Serializable
+
+  property format : String
+
+  property legality : String
+end
+
+class Ruling
+  include JSON::Serializable
+
+  property date : String
+
+  property text : String
+end
diff --git a/head/crystal/test/inputs/json/misc/66121.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/66121.json/default/TopLevel.cr
new file mode 100644
index 0000000..a9162f1
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/66121.json/default/TopLevel.cr
@@ -0,0 +1,49 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(JSON::Any?)
+
+  property superseded_by : String?
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/6617c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/6617c.json/default/TopLevel.cr
new file mode 100644
index 0000000..6d78e68
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/6617c.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property iss_position : IssPosition
+
+  property message : String
+
+  property timestamp : Int32
+end
+
+class IssPosition
+  include JSON::Serializable
+
+  property latitude : String
+
+  property longitude : String
+end
diff --git a/head/crystal/test/inputs/json/misc/67c03.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/67c03.json/default/TopLevel.cr
new file mode 100644
index 0000000..af9f048
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/67c03.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property message : String
+
+  property number : Int32
+
+  property people : Array(Person)
+end
+
+class Person
+  include JSON::Serializable
+
+  property craft : String
+
+  property name : String
+end
diff --git a/head/crystal/test/inputs/json/misc/68c30.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/68c30.json/default/TopLevel.cr
new file mode 100644
index 0000000..2926e74
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/68c30.json/default/TopLevel.cr
@@ -0,0 +1,65 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property txs : Array(Tx)
+end
+
+class Tx
+  include JSON::Serializable
+
+  property double_spend : Bool
+
+  property hash : String
+
+  property inputs : Array(Input)
+
+  property lock_time : Int32
+
+  property relayed_by : String
+
+  property size : Int32
+
+  property time : Int32
+
+  property tx_index : Int32
+
+  @[JSON::Field(key: "out")]
+  property tx_out : Array(Out)
+
+  property ver : Int32
+
+  property vin_sz : Int32
+
+  property vout_sz : Int32
+end
+
+class Input
+  include JSON::Serializable
+
+  property prev_out : Out
+
+  property script : String
+
+  property sequence : Float64
+end
+
+class Out
+  include JSON::Serializable
+
+  property addr : String
+
+  property n : Int32
+
+  @[JSON::Field(key: "type")]
+  property out_type : Int32
+
+  property script : String
+
+  property spent : Bool
+
+  property tx_index : Int32
+
+  property value : Float64
+end
diff --git a/head/crystal/test/inputs/json/misc/6c155.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/6c155.json/default/TopLevel.cr
new file mode 100644
index 0000000..83eb74a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/6c155.json/default/TopLevel.cr
@@ -0,0 +1,104 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property metadata : Metadata
+
+  property results : Array(Result)
+end
+
+class Metadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "executionTime")]
+  property execution_time : Float64
+
+  @[JSON::Field(key: "responseInfo")]
+  property response_info : ResponseInfo
+
+  property resultset : Resultset
+end
+
+class ResponseInfo
+  include JSON::Serializable
+
+  @[JSON::Field(key: "developerMessage")]
+  property developer_message : String
+
+  property status : Int32
+end
+
+class Resultset
+  include JSON::Serializable
+
+  property count : Int32
+
+  property page : Int32
+
+  property pagesize : Int32
+end
+
+class Result
+  include JSON::Serializable
+
+  property attachment : Array(JSON::Any?)
+
+  property body : String
+
+  property changed : String
+
+  property component : Array(Component)
+
+  property created : String
+
+  property date : String
+
+  property image : Array(JSON::Any?)
+
+  property location : Location
+
+  property teaser : Nil
+
+  property title : String
+
+  property topic : Array(JSON::Any?)
+
+  property url : String
+
+  property uuid : String
+
+  property vuuid : String
+end
+
+class Component
+  include JSON::Serializable
+
+  property name : String
+
+  property uuid : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property administrative_area : String
+
+  property country : String
+
+  property fax_number : String
+
+  property locality : String
+
+  property mobile_number : String
+
+  property phone_number : String
+
+  property phone_number_extension : String
+
+  property postal_code : String
+
+  property sub_premise : Nil
+
+  property thoroughfare : String
+end
diff --git a/head/crystal/test/inputs/json/misc/6de06.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/6de06.json/default/TopLevel.cr
new file mode 100644
index 0000000..af0dc0e
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/6de06.json/default/TopLevel.cr
@@ -0,0 +1,261 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : String?
+
+  property author_flair_text : String?
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Float64
+
+  property created_utc : Float64
+
+  property distinguished : Nil
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Bool
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : String?
+
+  property link_flair_text : String?
+
+  property locked : Bool
+
+  property media : Media?
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property permalink : String
+
+  property post_hint : String?
+
+  property preview : Preview?
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Media?
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : String?
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : String?
+
+  property thumbnail : String
+
+  property thumbnail_height : Int32?
+
+  property thumbnail_width : Int32?
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+end
+
+class Media
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property media_type : String
+
+  property oembed : Oembed
+end
+
+class Oembed
+  include JSON::Serializable
+
+  property description : String
+
+  property height : Int32
+
+  property html : String
+
+  @[JSON::Field(key: "type")]
+  property oembed_type : String
+
+  property provider_name : String
+
+  property provider_url : String
+
+  property thumbnail_height : Int32
+
+  property thumbnail_url : String
+
+  property thumbnail_width : Int32
+
+  property title : String
+
+  property version : String
+
+  property width : Int32
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+  property content : String?
+
+  property height : Int32?
+
+  property scrolling : Bool?
+
+  property width : Int32?
+end
+
+class Preview
+  include JSON::Serializable
+
+  property enabled : Bool
+
+  property images : Array(Image)
+end
+
+class Image
+  include JSON::Serializable
+
+  property id : String
+
+  property resolutions : Array(Source)
+
+  property source : Source
+
+  property variants : Variants
+end
+
+class Source
+  include JSON::Serializable
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
+
+class Variants
+  include JSON::Serializable
+
+  property gif : Gif?
+
+  property mp4 : Gif?
+
+  property nsfw : Gif?
+
+  property obfuscated : Gif?
+end
+
+class Gif
+  include JSON::Serializable
+
+  property resolutions : Array(Source)
+
+  property source : Source
+end
diff --git a/head/crystal/test/inputs/json/misc/6dec6.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/6dec6.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/6dec6.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/6eb00.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/6eb00.json/default/TopLevel.cr
new file mode 100644
index 0000000..2f99e18
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/6eb00.json/default/TopLevel.cr
@@ -0,0 +1,16 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  @[JSON::Field(key: "DirectorateID")]
+  property directorate_id : Int32
+
+  @[JSON::Field(key: "Id")]
+  property id : Int32
+
+  @[JSON::Field(key: "Name")]
+  property name : String
+end
diff --git a/head/crystal/test/inputs/json/misc/70c77.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/70c77.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/70c77.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/734ad.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/734ad.json/default/TopLevel.cr
new file mode 100644
index 0000000..b103ca3
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/734ad.json/default/TopLevel.cr
@@ -0,0 +1,145 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property facets : Facets
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property previous : Bool
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : String
+end
+
+class Facets
+  include JSON::Serializable
+
+end
+
+class Result
+  include JSON::Serializable
+
+  property acronym : String?
+
+  property activity_consult_committees : String
+
+  property activity_eu_legislative : String
+
+  property activity_expert_groups : String
+
+  property activity_high_level_groups : String
+
+  property activity_industry_forums : String
+
+  property activity_inter_groups : String
+
+  property activity_other : String?
+
+  property activity_relevant_comm : String
+
+  property be_office_country : String?
+
+  property be_office_lat : Float64?
+
+  property be_office_lon : Float64?
+
+  property be_office_phone : String?
+
+  property be_office_post_code : String?
+
+  property be_office_postbox : String?
+
+  property be_office_street : String?
+
+  property be_office_town : String?
+
+  property code_of_conduct : String
+
+  property contact_country : Int32
+
+  property created_at : String
+
+  property entity : String
+
+  property goals : String
+
+  property head : String
+
+  property head_office_country : String
+
+  property head_office_lat : Float64?
+
+  property head_office_lon : Float64?
+
+  property head_office_phone : String
+
+  property head_office_post_code : String?
+
+  property head_office_postbox : String?
+
+  property head_office_street : String
+
+  property head_office_town : String
+
+  property id : String
+
+  property identification_code : String
+
+  property info_members : String
+
+  property last_update_date : String
+
+  property legal : String
+
+  property legal_status : String
+
+  property main_category : Int32
+
+  property main_category_title : String
+
+  property members : Int32
+
+  property members_100 : Int32?
+
+  property members_25 : Int32?
+
+  property members_50 : Int32?
+
+  property members_75 : Int32?
+
+  property members_fte : Float64
+
+  property name : String
+
+  property native_name : Nil
+
+  property networking : String?
+
+  property number_of_natural_persons : Int32?
+
+  property other_code_of_conduct : String?
+
+  property registration_date : String
+
+  property status : String
+
+  property structure_members : String
+
+  property sub_category : Int32
+
+  property sub_category_title : String
+
+  property updated_at : String
+
+  property uri : String
+
+  property web_site_url : String?
+end
diff --git a/head/crystal/test/inputs/json/misc/75912.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/75912.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/75912.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/7681c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/7681c.json/default/TopLevel.cr
new file mode 100644
index 0000000..5d22178
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/7681c.json/default/TopLevel.cr
@@ -0,0 +1,195 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  property meta : Meta
+
+  property pagination : Pagination
+end
+
+class Datum
+  include JSON::Serializable
+
+  property bitly_gif_url : String
+
+  property bitly_url : String
+
+  property content_url : String
+
+  @[JSON::Field(key: "type")]
+  property datum_type : String
+
+  property embed_url : String
+
+  property id : String
+
+  property images : Images
+
+  property import_datetime : String
+
+  property is_indexable : Int32
+
+  property rating : String
+
+  property slug : String
+
+  property source : String
+
+  property source_post_url : String
+
+  property source_tld : String
+
+  property trending_datetime : String
+
+  property url : String
+
+  property user : User?
+
+  property username : String
+end
+
+class Images
+  include JSON::Serializable
+
+  property downsized : Downsized
+
+  property downsized_large : Downsized
+
+  property downsized_medium : Downsized
+
+  property downsized_small : DownsizedSmall
+
+  property downsized_still : Downsized
+
+  property fixed_height : FixedHeight
+
+  property fixed_height_downsampled : FixedHeight
+
+  property fixed_height_small : FixedHeight
+
+  property fixed_height_small_still : Downsized
+
+  property fixed_height_still : Downsized
+
+  property fixed_width : FixedHeight
+
+  property fixed_width_downsampled : FixedHeight
+
+  property fixed_width_small : FixedHeight
+
+  property fixed_width_small_still : Downsized
+
+  property fixed_width_still : Downsized
+
+  property looping : Looping
+
+  property original : FixedHeight
+
+  property original_mp4 : DownsizedSmall
+
+  property original_still : Downsized
+
+  property preview : DownsizedSmall
+
+  property preview_gif : Downsized
+
+  property preview_webp : Downsized
+
+  @[JSON::Field(key: "480w_still")]
+  property the_480_w_still : Downsized?
+end
+
+class Downsized
+  include JSON::Serializable
+
+  property height : String
+
+  property size : String?
+
+  property url : String
+
+  property width : String
+end
+
+class DownsizedSmall
+  include JSON::Serializable
+
+  property height : String
+
+  property mp4 : String
+
+  property mp4_size : String
+
+  property width : String
+end
+
+class FixedHeight
+  include JSON::Serializable
+
+  property frames : String?
+
+  property hash : String?
+
+  property height : String
+
+  property mp4 : String?
+
+  property mp4_size : String?
+
+  property size : String
+
+  property url : String
+
+  property webp : String
+
+  property webp_size : String
+
+  property width : String
+end
+
+class Looping
+  include JSON::Serializable
+
+  property mp4 : String
+
+  property mp4_size : String
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property banner_url : String
+
+  property display_name : String
+
+  property profile_url : String
+
+  property twitter : String?
+
+  property username : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property msg : String
+
+  property response_id : String
+
+  property status : Int32
+end
+
+class Pagination
+  include JSON::Serializable
+
+  property count : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/76ae1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/76ae1.json/default/TopLevel.cr
new file mode 100644
index 0000000..120b306
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/76ae1.json/default/TopLevel.cr
@@ -0,0 +1,344 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "basePath")]
+  property base_path : String
+
+  property definitions : Definitions
+
+  property host : String
+
+  property info : Info
+
+  property paths : Paths
+
+  property produces : Array(String)
+
+  property schemes : Array(String)
+
+  property swagger : String
+end
+
+class Definitions
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Article")]
+  property article : Article
+
+  @[JSON::Field(key: "Center")]
+  property center : Article
+
+  @[JSON::Field(key: "DeMinimis")]
+  property de_minimis : DeMinimis
+
+  @[JSON::Field(key: "Event")]
+  property event : Article
+
+  @[JSON::Field(key: "FAQ")]
+  property faq : Faq
+
+  @[JSON::Field(key: "Lead")]
+  property lead : Article
+
+  @[JSON::Field(key: "List")]
+  property list : Article
+
+  @[JSON::Field(key: "MarketIntelligence")]
+  property market_intelligence : Article
+
+  @[JSON::Field(key: "Office")]
+  property office : Article
+
+  @[JSON::Field(key: "Provider")]
+  property provider : Article
+
+  @[JSON::Field(key: "Rate")]
+  property rate : Article
+
+  @[JSON::Field(key: "Report")]
+  property report : Report
+
+  @[JSON::Field(key: "Taxonomy")]
+  property taxonomy : Taxonomy
+end
+
+class Article
+  include JSON::Serializable
+
+  property properties : Hash(String, Property)
+end
+
+class Property
+  include JSON::Serializable
+
+  property description : String
+
+  @[JSON::Field(key: "type")]
+  property property_type : String
+end
+
+class DeMinimis
+  include JSON::Serializable
+
+  property properties : DeMinimisProperties
+end
+
+class DeMinimisProperties
+  include JSON::Serializable
+
+  property countries : Property
+
+  property country : Property
+
+  property de_minimis_currency : Property
+
+  property de_minimis_value : Property
+
+  property notes : Property
+
+  property vat_amount : Property
+
+  property vat_currency : Property
+end
+
+class Faq
+  include JSON::Serializable
+
+  property properties : FaqProperties
+end
+
+class FaqProperties
+  include JSON::Serializable
+
+  property answer : Property
+
+  property countries : Property
+
+  property first_published_date : Property
+
+  property id : Property
+
+  property industries : Property
+
+  property last_published_date : Property
+
+  property question : Property
+
+  property topics : Property
+
+  property trade_regions : Property
+
+  property url : Property
+
+  property world_regions : Property
+end
+
+class Report
+  include JSON::Serializable
+
+  property properties : ReportProperties
+end
+
+class ReportProperties
+  include JSON::Serializable
+
+  property countries : Property
+
+  property description : Property
+
+  property expiration_date : Property
+
+  property id : Property
+
+  property industries : Property
+
+  property ita_industries : Property
+
+  property report_type : Property
+
+  property title : Property
+
+  property url : Property
+end
+
+class Taxonomy
+  include JSON::Serializable
+
+  property properties : TaxonomyProperties
+end
+
+class TaxonomyProperties
+  include JSON::Serializable
+
+  property annotations : Property
+
+  property datatype_properties : Property
+
+  property id : Property
+
+  property label : Property
+
+  @[JSON::Field(key: "type")]
+  property properties_type : Property
+
+  property sub_class_of : Property
+end
+
+class Info
+  include JSON::Serializable
+
+  property description : String
+
+  property title : String
+
+  property version : String
+end
+
+class Paths
+  include JSON::Serializable
+
+  @[JSON::Field(key: "/business_service_providers/search")]
+  property business_service_providers_search : BusinessServiceProvidersSearchClass
+
+  @[JSON::Field(key: "/consolidated_screening_list/search")]
+  property consolidated_screening_list_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/de_minimis/search")]
+  property de_minimis_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/ita_faqs/search")]
+  property ita_faqs_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/ita_office_locations/search")]
+  property ita_office_locations_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/ita_taxonomies/search")]
+  property ita_taxonomies_search : BusinessServiceProvidersSearchClass
+
+  @[JSON::Field(key: "/ita_zipcode_to_post/search")]
+  property ita_zipcode_to_post_search : BusinessServiceProvidersSearchClass
+
+  @[JSON::Field(key: "/market_intelligence/search")]
+  property market_intelligence_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/market_research_library/search")]
+  property market_research_library_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/tariff_rates/search")]
+  property tariff_rates_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/trade_articles/search")]
+  property trade_articles_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/trade_events/search")]
+  property trade_events_search : ConsolidatedScreeningListSearchClass
+
+  @[JSON::Field(key: "/trade_leads/search")]
+  property trade_leads_search : ConsolidatedScreeningListSearchClass
+end
+
+class BusinessServiceProvidersSearchClass
+  include JSON::Serializable
+
+  property get : BusinessServiceProvidersSearchGet
+end
+
+class BusinessServiceProvidersSearchGet
+  include JSON::Serializable
+
+  property description : String
+
+  property parameters : Array(Parameter)
+
+  property responses : PurpleResponses
+
+  property summary : String
+
+  property tags : Array(String)
+end
+
+class Parameter
+  include JSON::Serializable
+
+  property description : String
+
+  property format : String
+
+  property name : String
+
+  @[JSON::Field(key: "in")]
+  property parameter_in : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : String
+
+  property required : Bool
+end
+
+class PurpleResponses
+  include JSON::Serializable
+
+  @[JSON::Field(key: "200")]
+  property the_200 : Purple200
+end
+
+class Purple200
+  include JSON::Serializable
+
+  property description : String
+
+  property schema : ItemsClass
+end
+
+class ItemsClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "$ref")]
+  property ref : String
+end
+
+class ConsolidatedScreeningListSearchClass
+  include JSON::Serializable
+
+  property get : ConsolidatedScreeningListSearchGet
+end
+
+class ConsolidatedScreeningListSearchGet
+  include JSON::Serializable
+
+  property description : String
+
+  property parameters : Array(Parameter)
+
+  property responses : FluffyResponses
+
+  property summary : String
+
+  property tags : Array(String)
+end
+
+class FluffyResponses
+  include JSON::Serializable
+
+  @[JSON::Field(key: "200")]
+  property the_200 : Fluffy200
+end
+
+class Fluffy200
+  include JSON::Serializable
+
+  property description : String
+
+  property schema : PurpleSchema
+end
+
+class PurpleSchema
+  include JSON::Serializable
+
+  property items : ItemsClass
+
+  @[JSON::Field(key: "type")]
+  property schema_type : String
+end
diff --git a/head/crystal/test/inputs/json/misc/77392.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/77392.json/default/TopLevel.cr
new file mode 100644
index 0000000..0cbe7bb
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/77392.json/default/TopLevel.cr
@@ -0,0 +1,27 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property designation : String
+
+  property discovery_date : String
+
+  property h_mag : String?
+
+  property i_deg : String
+
+  property moid_au : String
+
+  property orbit_class : String
+
+  property period_yr : String?
+
+  property pha : String
+
+  property q_au_1 : String
+
+  property q_au_2 : String?
+end
diff --git a/head/crystal/test/inputs/json/misc/7d397.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/7d397.json/default/TopLevel.cr
new file mode 100644
index 0000000..38979ce
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/7d397.json/default/TopLevel.cr
@@ -0,0 +1,130 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "basePath")]
+  property base_path : String
+
+  property definitions : Definitions
+
+  property host : String
+
+  property info : Info
+
+  property paths : Paths
+
+  property produces : Array(String)
+
+  property schemes : Array(String)
+
+  property swagger : String
+end
+
+class Definitions
+  include JSON::Serializable
+
+  @[JSON::Field(key: "List")]
+  property list : List
+end
+
+class List
+  include JSON::Serializable
+
+  property properties : Hash(String, Property)
+end
+
+class Property
+  include JSON::Serializable
+
+  property description : String
+
+  @[JSON::Field(key: "type")]
+  property property_type : String
+end
+
+class Info
+  include JSON::Serializable
+
+  property description : String
+
+  property title : String
+
+  property version : String
+end
+
+class Paths
+  include JSON::Serializable
+
+  @[JSON::Field(key: "/consolidated_screening_list/search")]
+  property consolidated_screening_list_search : ConsolidatedScreeningListSearch
+end
+
+class ConsolidatedScreeningListSearch
+  include JSON::Serializable
+
+  property get : Get
+end
+
+class Get
+  include JSON::Serializable
+
+  property description : String
+
+  property parameters : Array(Parameter)
+
+  property responses : Responses
+
+  property summary : String
+
+  property tags : Array(String)
+end
+
+class Parameter
+  include JSON::Serializable
+
+  property description : String
+
+  property format : String
+
+  property name : String
+
+  @[JSON::Field(key: "in")]
+  property parameter_in : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : String
+
+  property required : Bool
+end
+
+class Responses
+  include JSON::Serializable
+
+  @[JSON::Field(key: "200")]
+  property the_200 : The200
+end
+
+class The200
+  include JSON::Serializable
+
+  property description : String
+
+  property schema : Schema
+end
+
+class Schema
+  include JSON::Serializable
+
+  property items : Items
+
+  @[JSON::Field(key: "type")]
+  property schema_type : String
+end
+
+class Items
+  include JSON::Serializable
+
+  @[JSON::Field(key: "$ref")]
+  property ref : String
+end
diff --git a/head/crystal/test/inputs/json/misc/7d722.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/7d722.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/7d722.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/7df41.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/7df41.json/default/TopLevel.cr
new file mode 100644
index 0000000..3466f8f
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/7df41.json/default/TopLevel.cr
@@ -0,0 +1,25 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property pc : Pc
+
+  property ps3 : Pc
+
+  property ps4 : Pc
+
+  property xbox : Pc
+
+  property xone : Pc
+end
+
+class Pc
+  include JSON::Serializable
+
+  property count : Int32
+
+  property label : String
+
+  property peak24 : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/7dfa6.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/7dfa6.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/7dfa6.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/7eb30.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/7eb30.json/default/TopLevel.cr
new file mode 100644
index 0000000..0a8e444
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/7eb30.json/default/TopLevel.cr
@@ -0,0 +1,58 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property previous : Nil
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : String
+end
+
+class Result
+  include JSON::Serializable
+
+  property code : String
+
+  property english : String
+
+  property fav : Bool
+
+  property group : Group
+
+  property location : String
+
+  property min_screens : Int32
+
+  property num_views : Int32
+
+  property showtimes : String
+
+  property stars : Int32
+
+  property tel : String
+
+  property thai : String
+
+  property today_screens : Int32
+
+  property url : String
+
+  property website : String
+end
+
+class Group
+  include JSON::Serializable
+
+  property code : String
+
+  property english : String
+
+  property thai : String
+
+  property website : String
+end
diff --git a/head/crystal/test/inputs/json/misc/7f568.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/7f568.json/default/TopLevel.cr
new file mode 100644
index 0000000..fda80c1
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/7f568.json/default/TopLevel.cr
@@ -0,0 +1,54 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property comments : Int32
+
+  property comments_url : String
+
+  property commits_url : String
+
+  property created_at : String
+
+  property description : String?
+
+  property files : Hash(String, FileValue)
+
+  property forks_url : String
+
+  property git_pull_url : String
+
+  property git_push_url : String
+
+  property html_url : String
+
+  property id : String
+
+  property public : Bool
+
+  property truncated : Bool
+
+  property updated_at : String
+
+  property url : String
+
+  property user : Nil
+end
+
+class FileValue
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property file_type : String
+
+  property filename : String
+
+  property language : String?
+
+  property raw_url : String
+
+  property size : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/7fbfb.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/7fbfb.json/default/TopLevel.cr
new file mode 100644
index 0000000..4b9cdae
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/7fbfb.json/default/TopLevel.cr
@@ -0,0 +1,39 @@
+require "json"
+
+alias TopLevel = Array(TopLevelUnion)
+
+class PurpleTopLevel
+  include JSON::Serializable
+
+  property country : Country
+
+  property date : String
+
+  property decimal : String
+
+  property indicator : Country
+
+  property value : String
+end
+
+class Country
+  include JSON::Serializable
+
+  property id : String
+
+  property value : String
+end
+
+class FluffyTopLevel
+  include JSON::Serializable
+
+  property page : Int32
+
+  property pages : Int32
+
+  property per_page : String
+
+  property total : Int32
+end
+
+alias TopLevelUnion = FluffyTopLevel | Array(PurpleTopLevel)
diff --git a/head/crystal/test/inputs/json/misc/80aff.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/80aff.json/default/TopLevel.cr
new file mode 100644
index 0000000..8ee8d53
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/80aff.json/default/TopLevel.cr
@@ -0,0 +1,43 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property facets : Facets
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property previous : Bool
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : Bool
+end
+
+class Facets
+  include JSON::Serializable
+
+end
+
+class Result
+  include JSON::Serializable
+
+  property created_at : String
+
+  property id : Int32
+
+  property items : Int32
+
+  property name : String
+
+  property parent : Int32?
+
+  property updated_at : String
+
+  property uri : String
+end
diff --git a/head/crystal/test/inputs/json/misc/82509.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/82509.json/default/TopLevel.cr
new file mode 100644
index 0000000..7a83695
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/82509.json/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property origin : String
+end
diff --git a/head/crystal/test/inputs/json/misc/8592b.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/8592b.json/default/TopLevel.cr
new file mode 100644
index 0000000..f3a2b58
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/8592b.json/default/TopLevel.cr
@@ -0,0 +1,196 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : String?
+
+  property author_flair_text : String?
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Float64
+
+  property created_utc : Float64
+
+  property distinguished : Nil
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Bool
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : String
+
+  property link_flair_text : String
+
+  property locked : Bool
+
+  property media : Nil
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property permalink : String
+
+  property post_hint : String?
+
+  property preview : Preview?
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Nil
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : String?
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : String
+
+  property thumbnail : String
+
+  property thumbnail_height : Int32?
+
+  property thumbnail_width : Int32?
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+end
+
+class Preview
+  include JSON::Serializable
+
+  property enabled : Bool
+
+  property images : Array(Image)
+end
+
+class Image
+  include JSON::Serializable
+
+  property id : String
+
+  property resolutions : Array(Source)
+
+  property source : Source
+
+  property variants : MediaEmbed
+end
+
+class Source
+  include JSON::Serializable
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/88130.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/88130.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/88130.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/8a62c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/8a62c.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/8a62c.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/908db.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/908db.json/default/TopLevel.cr
new file mode 100644
index 0000000..168ec54
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/908db.json/default/TopLevel.cr
@@ -0,0 +1,21 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property states : Array(StateElement)
+end
+
+class StateElement
+  include JSON::Serializable
+
+  property state : StateState
+end
+
+class StateState
+  include JSON::Serializable
+
+  property state_id : String
+
+  property state_name : String
+end
diff --git a/head/crystal/test/inputs/json/misc/9617f.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/9617f.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/9617f.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/96f7c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/96f7c.json/default/TopLevel.cr
new file mode 100644
index 0000000..037d956
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/96f7c.json/default/TopLevel.cr
@@ -0,0 +1,17 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property git : Array(String)
+
+  property github_services_sha : String
+
+  property hooks : Array(String)
+
+  property importer : Array(String)
+
+  property pages : Array(String)
+
+  property verifiable_password_authentication : Bool
+end
diff --git a/head/crystal/test/inputs/json/misc/9847b.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/9847b.json/default/TopLevel.cr
new file mode 100644
index 0000000..cf510c3
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/9847b.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property name : String
+end
diff --git a/head/crystal/test/inputs/json/misc/9929c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/9929c.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/9929c.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/996bd.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/996bd.json/default/TopLevel.cr
new file mode 100644
index 0000000..a9162f1
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/996bd.json/default/TopLevel.cr
@@ -0,0 +1,49 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(JSON::Any?)
+
+  property superseded_by : String?
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/9a503.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/9a503.json/default/TopLevel.cr
new file mode 100644
index 0000000..8c033c5
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/9a503.json/default/TopLevel.cr
@@ -0,0 +1,49 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(JSON::Any?)
+
+  property superseded_by : Nil
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/9ac3b.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/9ac3b.json/default/TopLevel.cr
new file mode 100644
index 0000000..3f3576a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/9ac3b.json/default/TopLevel.cr
@@ -0,0 +1,51 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property facets : Facets
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property previous : Bool
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : String
+end
+
+class Facets
+  include JSON::Serializable
+
+end
+
+class Result
+  include JSON::Serializable
+
+  property created_at : String
+
+  property entity : String
+
+  property first_name : String
+
+  property id : String
+
+  property last_name : String
+
+  property name : String
+
+  property position : String?
+
+  property status : String
+
+  property title : String?
+
+  property updated_at : String
+
+  property uri : String
+end
diff --git a/head/crystal/test/inputs/json/misc/9eed5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/9eed5.json/default/TopLevel.cr
new file mode 100644
index 0000000..8c033c5
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/9eed5.json/default/TopLevel.cr
@@ -0,0 +1,49 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(JSON::Any?)
+
+  property superseded_by : Nil
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/a0496.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/a0496.json/default/TopLevel.cr
new file mode 100644
index 0000000..dc23647
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/a0496.json/default/TopLevel.cr
@@ -0,0 +1,47 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property code : String
+
+  property column_names : Array(String)
+
+  property data : Array(Array(Datum?))
+
+  property description : String
+
+  property display_url : String
+
+  property errors : Errors
+
+  property frequency : String
+
+  property from_date : String
+
+  property id : Int32
+
+  property name : String
+
+  property premium : Bool
+
+  property source_code : String
+
+  property source_name : String
+
+  property to_date : String
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+
+  property updated_at : String
+
+  property urlize_name : String
+end
+
+class Errors
+  include JSON::Serializable
+
+end
+
+alias Datum = Float64 | String
diff --git a/head/crystal/test/inputs/json/misc/a1eca.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/a1eca.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/a1eca.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/a3d8c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/a3d8c.json/default/TopLevel.cr
new file mode 100644
index 0000000..3b8f41c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/a3d8c.json/default/TopLevel.cr
@@ -0,0 +1,240 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Array(Datum?))
+
+  property meta : Meta
+end
+
+class Meta
+  include JSON::Serializable
+
+  property view : View
+end
+
+class View
+  include JSON::Serializable
+
+  @[JSON::Field(key: "averageRating")]
+  property average_rating : Int32
+
+  property category : String
+
+  property columns : Array(Column)
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : Int32
+
+  @[JSON::Field(key: "displayType")]
+  property display_type : String
+
+  @[JSON::Field(key: "downloadCount")]
+  property download_count : Int32
+
+  property flags : Array(String)
+
+  property grants : Array(Grant)
+
+  @[JSON::Field(key: "hideFromCatalog")]
+  property hide_from_catalog : Bool
+
+  @[JSON::Field(key: "hideFromDataJson")]
+  property hide_from_data_json : Bool
+
+  property id : String
+
+  @[JSON::Field(key: "indexUpdatedAt")]
+  property index_updated_at : Int32
+
+  property license : License
+
+  @[JSON::Field(key: "licenseId")]
+  property license_id : String
+
+  property locale : String
+
+  property metadata : Metadata
+
+  property name : String
+
+  @[JSON::Field(key: "newBackend")]
+  property new_backend : Bool
+
+  @[JSON::Field(key: "numberOfComments")]
+  property number_of_comments : Int32
+
+  property oid : Int32
+
+  property owner : Owner
+
+  property provenance : String
+
+  @[JSON::Field(key: "publicationAppendEnabled")]
+  property publication_append_enabled : Bool
+
+  @[JSON::Field(key: "publicationDate")]
+  property publication_date : Int32
+
+  @[JSON::Field(key: "publicationGroup")]
+  property publication_group : Int32
+
+  @[JSON::Field(key: "publicationStage")]
+  property publication_stage : String
+
+  property query : Query
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "rowClass")]
+  property row_class : String
+
+  @[JSON::Field(key: "rowsUpdatedAt")]
+  property rows_updated_at : Int32
+
+  @[JSON::Field(key: "rowsUpdatedBy")]
+  property rows_updated_by : String
+
+  @[JSON::Field(key: "tableAuthor")]
+  property table_author : Owner
+
+  @[JSON::Field(key: "tableId")]
+  property table_id : Int32
+
+  @[JSON::Field(key: "totalTimesRated")]
+  property total_times_rated : Int32
+
+  @[JSON::Field(key: "viewCount")]
+  property view_count : Int32
+
+  @[JSON::Field(key: "viewLastModified")]
+  property view_last_modified : Int32
+
+  @[JSON::Field(key: "viewType")]
+  property view_type : String
+end
+
+class Column
+  include JSON::Serializable
+
+  @[JSON::Field(key: "cachedContents")]
+  property cached_contents : CachedContents?
+
+  @[JSON::Field(key: "dataTypeName")]
+  property data_type_name : String
+
+  @[JSON::Field(key: "fieldName")]
+  property field_name : String
+
+  property flags : Array(String)?
+
+  property format : Query
+
+  property id : Int32
+
+  property name : String
+
+  property position : Int32
+
+  @[JSON::Field(key: "renderTypeName")]
+  property render_type_name : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : Int32?
+
+  property width : Int32?
+end
+
+class CachedContents
+  include JSON::Serializable
+
+  property average : String?
+
+  property largest : String
+
+  property non_null : Int32
+
+  property null : Int32
+
+  property smallest : String
+
+  property sum : String?
+
+  property top : Array(Top)
+end
+
+class Top
+  include JSON::Serializable
+
+  property count : Int32
+
+  property item : String
+end
+
+class Query
+  include JSON::Serializable
+
+end
+
+class Grant
+  include JSON::Serializable
+
+  property flags : Array(String)
+
+  @[JSON::Field(key: "type")]
+  property grant_type : String
+
+  property inherited : Bool
+end
+
+class License
+  include JSON::Serializable
+
+  property name : String
+end
+
+class Metadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "availableDisplayTypes")]
+  property available_display_types : Array(String)
+
+  @[JSON::Field(key: "rdfClass")]
+  property rdf_class : String
+
+  @[JSON::Field(key: "rdfSubject")]
+  property rdf_subject : String
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : RenderTypeConfig
+
+  @[JSON::Field(key: "rowIdentifier")]
+  property row_identifier : String
+end
+
+class RenderTypeConfig
+  include JSON::Serializable
+
+  property visible : Visible
+end
+
+class Visible
+  include JSON::Serializable
+
+  property table : Bool
+end
+
+class Owner
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property id : String
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
+
+alias Datum = Int32 | String
diff --git a/head/crystal/test/inputs/json/misc/a45b0.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/a45b0.json/default/TopLevel.cr
new file mode 100644
index 0000000..943073c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/a45b0.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property age : Int32
+
+  property country : String
+
+  property females : Int32
+
+  property males : Int32
+
+  property total : Int32
+
+  property year : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/a71df.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/a71df.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/a71df.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/a9691.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/a9691.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/a9691.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/ab0d1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/ab0d1.json/default/TopLevel.cr
new file mode 100644
index 0000000..943073c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/ab0d1.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property age : Int32
+
+  property country : String
+
+  property females : Int32
+
+  property males : Int32
+
+  property total : Int32
+
+  property year : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/abb4b.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/abb4b.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/abb4b.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/ac944.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/ac944.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/ac944.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/ad8be.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/ad8be.json/default/TopLevel.cr
new file mode 100644
index 0000000..6d7a453
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/ad8be.json/default/TopLevel.cr
@@ -0,0 +1,57 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property identifiers : Array(Identifier)
+
+  property keywords : Array(String)
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property other_names : Array(OtherName)
+
+  property superseded_by : String?
+
+  property text : Array(Text)
+end
+
+class Identifier
+  include JSON::Serializable
+
+  property identifier : String
+
+  property scheme : String
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property note : String
+
+  property url : String
+end
+
+class OtherName
+  include JSON::Serializable
+
+  property name : String
+
+  property note : String?
+end
+
+class Text
+  include JSON::Serializable
+
+  property media_type : String
+
+  property title : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/ae7f0.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/ae7f0.json/default/TopLevel.cr
new file mode 100644
index 0000000..0631809
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/ae7f0.json/default/TopLevel.cr
@@ -0,0 +1,158 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : Nil
+
+  property author_flair_text : Nil
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Float64
+
+  property created_utc : Float64
+
+  property distinguished : Nil
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Bool
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : String?
+
+  property link_flair_text : String?
+
+  property locked : Bool
+
+  property media : Nil
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property permalink : String
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Nil
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : Nil
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : Nil
+
+  property thumbnail : String
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+end
diff --git a/head/crystal/test/inputs/json/misc/ae9ca.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/ae9ca.json/default/TopLevel.cr
new file mode 100644
index 0000000..1a891b3
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/ae9ca.json/default/TopLevel.cr
@@ -0,0 +1,64 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property features : Array(Feature)
+
+  property name : String
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+end
+
+class Feature
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property feature_type : String
+
+  property geometry : Geometry
+
+  property properties : Properties
+end
+
+class Geometry
+  include JSON::Serializable
+
+  property coordinates : Array(Float64)
+
+  @[JSON::Field(key: "type")]
+  property geometry_type : String
+end
+
+class Properties
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Area")]
+  property area : String
+
+  @[JSON::Field(key: "Central Asset ID")]
+  property central_asset_id : String
+
+  @[JSON::Field(key: "Feature Location")]
+  property feature_location : String
+
+  property lat : String
+
+  property long : String
+
+  @[JSON::Field(key: "Maintaining Authority")]
+  property maintaining_authority : String
+
+  @[JSON::Field(key: "Number")]
+  property number : String
+
+  @[JSON::Field(key: "Class")]
+  property properties_class : String
+
+  @[JSON::Field(key: "Site Name")]
+  property site_name : String
+
+  @[JSON::Field(key: "Ward")]
+  property ward : String
+end
diff --git a/head/crystal/test/inputs/json/misc/af2d1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/af2d1.json/default/TopLevel.cr
new file mode 100644
index 0000000..c2972b4
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/af2d1.json/default/TopLevel.cr
@@ -0,0 +1,136 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property crs : Crs
+
+  property features : Array(Feature)
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+
+  @[JSON::Field(key: "totalFeatures")]
+  property total_features : Int32
+end
+
+class Crs
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property crs_type : String
+
+  property properties : CrsProperties
+end
+
+class CrsProperties
+  include JSON::Serializable
+
+  property name : String
+end
+
+class Feature
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property feature_type : String
+
+  property geometry : Geometry
+
+  property geometry_name : String
+
+  property id : String
+
+  property properties : FeatureProperties
+end
+
+class Geometry
+  include JSON::Serializable
+
+  property coordinates : Array(Array(Array(Array(Float64))))
+
+  @[JSON::Field(key: "type")]
+  property geometry_type : String
+end
+
+class FeatureProperties
+  include JSON::Serializable
+
+  property add_improv : String?
+
+  @[JSON::Field(key: "area_")]
+  property area : Float64
+
+  property asset_numb : String
+
+  property comments : String?
+
+  property condition : Int32
+
+  property constructi : String?
+
+  property createdate : Nil
+
+  property createuser : Nil
+
+  property disposal_d : Nil
+
+  property documents : String
+
+  property drawing_nu : String?
+
+  property file_numbe : String?
+
+  property folder_num : String?
+
+  property funding_ba : String?
+
+  property historic_c : Int32
+
+  property inspection : String
+
+  property inspectors : Nil
+
+  property last_updat : Nil
+
+  property level_accu : String?
+
+  property material : String
+
+  property mi_prinx : Int32
+
+  property mi_symbolo : String
+
+  property number_lan : Int32
+
+  property owner : String?
+
+  property positional : String?
+
+  property project_nu : String?
+
+  @[JSON::Field(key: "type")]
+  property properties_type : String?
+
+  property rec_id : Int32
+
+  property record_cre : String?
+
+  property shape_area : Float64
+
+  property shape_leng : Float64
+
+  property status : String
+
+  property survey_num : Nil
+
+  property toe_rl : Float64
+
+  property top_rl : Float64
+
+  property update_dat : String
+
+  property updatedate : Nil
+
+  property updateuser : Nil
+end
diff --git a/head/crystal/test/inputs/json/misc/b4865.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/b4865.json/default/TopLevel.cr
new file mode 100644
index 0000000..86d80cd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/b4865.json/default/TopLevel.cr
@@ -0,0 +1,42 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  @[JSON::Field(key: ":@computed_region_cbhk_fwbd")]
+  property computed_region_cbhk_fwbd : String?
+
+  @[JSON::Field(key: ":@computed_region_nnqa_25f4")]
+  property computed_region_nnqa_25_f4 : String?
+
+  property fall : String
+
+  property geolocation : Geolocation?
+
+  property id : String
+
+  property mass : String?
+
+  property name : String
+
+  property nametype : String
+
+  property recclass : String
+
+  property reclat : String?
+
+  property reclong : String?
+
+  property year : String?
+end
+
+class Geolocation
+  include JSON::Serializable
+
+  property coordinates : Array(Float64)
+
+  @[JSON::Field(key: "type")]
+  property geolocation_type : String
+end
diff --git a/head/crystal/test/inputs/json/misc/b6f2c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/b6f2c.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/b6f2c.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/b6fe5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/b6fe5.json/default/TopLevel.cr
new file mode 100644
index 0000000..109e0b3
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/b6fe5.json/default/TopLevel.cr
@@ -0,0 +1,14 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property group : String
+
+  property movie : String
+
+  @[JSON::Field(key: "movie-image")]
+  property movie_image : String
+
+  property theater : String
+end
diff --git a/head/crystal/test/inputs/json/misc/b9f64.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/b9f64.json/default/TopLevel.cr
new file mode 100644
index 0000000..1c7ed5f
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/b9f64.json/default/TopLevel.cr
@@ -0,0 +1,8 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "user-agent")]
+  property user_agent : String
+end
diff --git a/head/crystal/test/inputs/json/misc/bb1ec.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/bb1ec.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/bb1ec.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/be234.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/be234.json/default/TopLevel.cr
new file mode 100644
index 0000000..e034510
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/be234.json/default/TopLevel.cr
@@ -0,0 +1,257 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : Nil
+
+  property author_flair_text : String?
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Float64
+
+  property created_utc : Float64
+
+  property distinguished : String?
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Bool
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : Nil
+
+  property link_flair_text : Nil
+
+  property locked : Bool
+
+  property media : Media?
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property permalink : String
+
+  property post_hint : String
+
+  property preview : Preview
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Media?
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : Nil
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : Nil
+
+  property thumbnail : String
+
+  property thumbnail_height : Int32
+
+  property thumbnail_width : Int32
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+end
+
+class Media
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property media_type : String
+
+  property oembed : Oembed
+end
+
+class Oembed
+  include JSON::Serializable
+
+  property description : String
+
+  property height : Int32
+
+  property html : String
+
+  @[JSON::Field(key: "type")]
+  property oembed_type : String
+
+  property provider_name : String
+
+  property provider_url : String
+
+  property thumbnail_height : Int32
+
+  property thumbnail_url : String
+
+  property thumbnail_width : Int32
+
+  property title : String
+
+  property version : String
+
+  property width : Int32
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+  property content : String?
+
+  property height : Int32?
+
+  property scrolling : Bool?
+
+  property width : Int32?
+end
+
+class Preview
+  include JSON::Serializable
+
+  property enabled : Bool
+
+  property images : Array(Image)
+end
+
+class Image
+  include JSON::Serializable
+
+  property id : String
+
+  property resolutions : Array(Source)
+
+  property source : Source
+
+  property variants : Variants
+end
+
+class Source
+  include JSON::Serializable
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
+
+class Variants
+  include JSON::Serializable
+
+  property gif : Gif?
+
+  property mp4 : Gif?
+end
+
+class Gif
+  include JSON::Serializable
+
+  property resolutions : Array(Source)
+
+  property source : Source
+end
diff --git a/head/crystal/test/inputs/json/misc/c0356.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/c0356.json/default/TopLevel.cr
new file mode 100644
index 0000000..99bce73
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/c0356.json/default/TopLevel.cr
@@ -0,0 +1,27 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Hash(String, Datum)
+
+  property description : Description
+end
+
+class Datum
+  include JSON::Serializable
+
+  property anomaly : String
+
+  property value : String
+end
+
+class Description
+  include JSON::Serializable
+
+  property base_period : String
+
+  property missing : Int32
+
+  property title : String
+end
diff --git a/head/crystal/test/inputs/json/misc/c0a3a.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/c0a3a.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/c0a3a.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/c3303.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/c3303.json/default/TopLevel.cr
new file mode 100644
index 0000000..a2df6d1
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/c3303.json/default/TopLevel.cr
@@ -0,0 +1,193 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  property meta : Meta
+
+  property pagination : Pagination
+end
+
+class Datum
+  include JSON::Serializable
+
+  property bitly_gif_url : String
+
+  property bitly_url : String
+
+  property content_url : String
+
+  @[JSON::Field(key: "type")]
+  property datum_type : String
+
+  property embed_url : String
+
+  property id : String
+
+  property images : Images
+
+  property import_datetime : String
+
+  property is_indexable : Int32
+
+  property rating : String
+
+  property slug : String
+
+  property source : String
+
+  property source_post_url : String
+
+  property source_tld : String
+
+  property trending_datetime : String
+
+  property url : String
+
+  property user : User?
+
+  property username : String
+end
+
+class Images
+  include JSON::Serializable
+
+  property downsized : Downsized
+
+  property downsized_large : Downsized
+
+  property downsized_medium : Downsized
+
+  property downsized_small : DownsizedSmall
+
+  property downsized_still : Downsized
+
+  property fixed_height : FixedHeight
+
+  property fixed_height_downsampled : FixedHeight
+
+  property fixed_height_small : FixedHeight
+
+  property fixed_height_small_still : Downsized
+
+  property fixed_height_still : Downsized
+
+  property fixed_width : FixedHeight
+
+  property fixed_width_downsampled : FixedHeight
+
+  property fixed_width_small : FixedHeight
+
+  property fixed_width_small_still : Downsized
+
+  property fixed_width_still : Downsized
+
+  property looping : Looping
+
+  property original : FixedHeight
+
+  property original_mp4 : DownsizedSmall
+
+  property original_still : Downsized
+
+  property preview : DownsizedSmall
+
+  property preview_gif : Downsized
+
+  property preview_webp : Downsized
+
+  @[JSON::Field(key: "480w_still")]
+  property the_480_w_still : Downsized?
+end
+
+class Downsized
+  include JSON::Serializable
+
+  property height : String
+
+  property size : String?
+
+  property url : String
+
+  property width : String
+end
+
+class DownsizedSmall
+  include JSON::Serializable
+
+  property height : String
+
+  property mp4 : String
+
+  property mp4_size : String
+
+  property width : String
+end
+
+class FixedHeight
+  include JSON::Serializable
+
+  property frames : String?
+
+  property hash : String?
+
+  property height : String
+
+  property mp4 : String?
+
+  property mp4_size : String?
+
+  property size : String
+
+  property url : String
+
+  property webp : String
+
+  property webp_size : String
+
+  property width : String
+end
+
+class Looping
+  include JSON::Serializable
+
+  property mp4 : String
+
+  property mp4_size : String
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property banner_url : String
+
+  property display_name : String
+
+  property profile_url : String
+
+  property username : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property msg : String
+
+  property response_id : String
+
+  property status : Int32
+end
+
+class Pagination
+  include JSON::Serializable
+
+  property count : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/c6cfd.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/c6cfd.json/default/TopLevel.cr
new file mode 100644
index 0000000..2f6ffdc
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/c6cfd.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property countries : Array(Country)
+end
+
+class Country
+  include JSON::Serializable
+
+  property code : String
+
+  property name : String
+end
diff --git a/head/crystal/test/inputs/json/misc/c8c7e.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/c8c7e.json/default/TopLevel.cr
new file mode 100644
index 0000000..4b9cdae
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/c8c7e.json/default/TopLevel.cr
@@ -0,0 +1,39 @@
+require "json"
+
+alias TopLevel = Array(TopLevelUnion)
+
+class PurpleTopLevel
+  include JSON::Serializable
+
+  property country : Country
+
+  property date : String
+
+  property decimal : String
+
+  property indicator : Country
+
+  property value : String
+end
+
+class Country
+  include JSON::Serializable
+
+  property id : String
+
+  property value : String
+end
+
+class FluffyTopLevel
+  include JSON::Serializable
+
+  property page : Int32
+
+  property pages : Int32
+
+  property per_page : String
+
+  property total : Int32
+end
+
+alias TopLevelUnion = FluffyTopLevel | Array(PurpleTopLevel)
diff --git a/head/crystal/test/inputs/json/misc/cb0cc.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/cb0cc.json/default/TopLevel.cr
new file mode 100644
index 0000000..e6017ac
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/cb0cc.json/default/TopLevel.cr
@@ -0,0 +1,34 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property prizes : Array(Prize)
+end
+
+class Prize
+  include JSON::Serializable
+
+  property category : String
+
+  property laureates : Array(Laureate)
+
+  @[JSON::Field(key: "overallMotivation")]
+  property overall_motivation : String?
+
+  property year : String
+end
+
+class Laureate
+  include JSON::Serializable
+
+  property firstname : String
+
+  property id : String
+
+  property motivation : String?
+
+  property share : String
+
+  property surname : String
+end
diff --git a/head/crystal/test/inputs/json/misc/cb81e.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/cb81e.json/default/TopLevel.cr
new file mode 100644
index 0000000..ebb5fbf
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/cb81e.json/default/TopLevel.cr
@@ -0,0 +1,21 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Hash(String, String)
+
+  property description : Description
+end
+
+class Description
+  include JSON::Serializable
+
+  property base_period : String
+
+  property missing : String
+
+  property title : String
+
+  property units : String
+end
diff --git a/head/crystal/test/inputs/json/misc/ccd18.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/ccd18.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/ccd18.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/cd238.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/cd238.json/default/TopLevel.cr
new file mode 100644
index 0000000..54b90dd
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/cd238.json/default/TopLevel.cr
@@ -0,0 +1,76 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property city : String
+
+  property delay : String
+
+  @[JSON::Field(key: "IATA")]
+  property iata : String
+
+  @[JSON::Field(key: "ICAO")]
+  property icao : String
+
+  property name : String
+
+  property state : String
+
+  property status : Status
+
+  property weather : Weather
+end
+
+class Status
+  include JSON::Serializable
+
+  @[JSON::Field(key: "avgDelay")]
+  property avg_delay : String
+
+  @[JSON::Field(key: "closureBegin")]
+  property closure_begin : String
+
+  @[JSON::Field(key: "closureEnd")]
+  property closure_end : String
+
+  @[JSON::Field(key: "endTime")]
+  property end_time : String
+
+  @[JSON::Field(key: "maxDelay")]
+  property max_delay : String
+
+  @[JSON::Field(key: "minDelay")]
+  property min_delay : String
+
+  property reason : String
+
+  @[JSON::Field(key: "type")]
+  property status_type : String
+
+  property trend : String
+end
+
+class Weather
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property temp : String
+
+  property visibility : Float64
+
+  property weather : String
+
+  property wind : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property credit : String
+
+  property updated : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/misc/cd463.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/cd463.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/cd463.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/cda6c.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/cda6c.json/default/TopLevel.cr
new file mode 100644
index 0000000..4b9cdae
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/cda6c.json/default/TopLevel.cr
@@ -0,0 +1,39 @@
+require "json"
+
+alias TopLevel = Array(TopLevelUnion)
+
+class PurpleTopLevel
+  include JSON::Serializable
+
+  property country : Country
+
+  property date : String
+
+  property decimal : String
+
+  property indicator : Country
+
+  property value : String
+end
+
+class Country
+  include JSON::Serializable
+
+  property id : String
+
+  property value : String
+end
+
+class FluffyTopLevel
+  include JSON::Serializable
+
+  property page : Int32
+
+  property pages : Int32
+
+  property per_page : String
+
+  property total : Int32
+end
+
+alias TopLevelUnion = FluffyTopLevel | Array(PurpleTopLevel)
diff --git a/head/crystal/test/inputs/json/misc/cf0d8.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/cf0d8.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/cf0d8.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/cfbce.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/cfbce.json/default/TopLevel.cr
new file mode 100644
index 0000000..f6aac04
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/cfbce.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property base : String
+
+  property date : String
+
+  property rates : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/misc/d0908.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/d0908.json/default/TopLevel.cr
new file mode 100644
index 0000000..6e324be
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/d0908.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property total_population : Array(TotalPopulation)
+end
+
+class TotalPopulation
+  include JSON::Serializable
+
+  property date : String
+
+  property population : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/d23d5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/d23d5.json/default/TopLevel.cr
new file mode 100644
index 0000000..fa049c2
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/d23d5.json/default/TopLevel.cr
@@ -0,0 +1,41 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property count : Int32
+
+  property facets : Facets
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property previous : Bool
+
+  property results : Array(Result)
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : String
+end
+
+class Facets
+  include JSON::Serializable
+
+end
+
+class Result
+  include JSON::Serializable
+
+  property acronym : String?
+
+  property created_at : String
+
+  property id : String
+
+  property name : String
+
+  property updated_at : String
+
+  property uri : String
+end
diff --git a/head/crystal/test/inputs/json/misc/dbfb3.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/dbfb3.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/dbfb3.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/dc44f.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/dc44f.json/default/TopLevel.cr
new file mode 100644
index 0000000..98c7de0
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/dc44f.json/default/TopLevel.cr
@@ -0,0 +1,112 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property booster : Array(String)
+
+  property border : String
+
+  property cards : Array(Card)
+
+  property code : String
+
+  @[JSON::Field(key: "gathererCode")]
+  property gatherer_code : String
+
+  @[JSON::Field(key: "magicCardsInfoCode")]
+  property magic_cards_info_code : String
+
+  property mkm_id : Int32
+
+  property mkm_name : String
+
+  property name : String
+
+  @[JSON::Field(key: "releaseDate")]
+  property release_date : String
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+end
+
+class Card
+  include JSON::Serializable
+
+  property artist : String
+
+  @[JSON::Field(key: "type")]
+  property card_type : String
+
+  property cmc : Int32
+
+  @[JSON::Field(key: "colorIdentity")]
+  property color_identity : Array(String)?
+
+  property colors : Array(String)?
+
+  property flavor : String?
+
+  property id : String
+
+  @[JSON::Field(key: "imageName")]
+  property image_name : String
+
+  property layout : String
+
+  property legalities : Array(LegalityElement)
+
+  @[JSON::Field(key: "manaCost")]
+  property mana_cost : String?
+
+  @[JSON::Field(key: "mciNumber")]
+  property mci_number : String?
+
+  property multiverseid : Int32
+
+  property name : String
+
+  @[JSON::Field(key: "originalText")]
+  property original_text : String?
+
+  @[JSON::Field(key: "originalType")]
+  property original_type : String
+
+  property power : String?
+
+  property printings : Array(String)
+
+  property rarity : String
+
+  property reserved : Bool?
+
+  property rulings : Array(Ruling)?
+
+  property subtypes : Array(String)?
+
+  property supertypes : Array(String)?
+
+  property text : String?
+
+  property toughness : String?
+
+  property types : Array(String)
+
+  property variations : Array(Int32)?
+end
+
+class LegalityElement
+  include JSON::Serializable
+
+  property format : String
+
+  property legality : String
+end
+
+class Ruling
+  include JSON::Serializable
+
+  property date : String
+
+  property text : String
+end
diff --git a/head/crystal/test/inputs/json/misc/dd1ce.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/dd1ce.json/default/TopLevel.cr
new file mode 100644
index 0000000..98c7de0
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/dd1ce.json/default/TopLevel.cr
@@ -0,0 +1,112 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property booster : Array(String)
+
+  property border : String
+
+  property cards : Array(Card)
+
+  property code : String
+
+  @[JSON::Field(key: "gathererCode")]
+  property gatherer_code : String
+
+  @[JSON::Field(key: "magicCardsInfoCode")]
+  property magic_cards_info_code : String
+
+  property mkm_id : Int32
+
+  property mkm_name : String
+
+  property name : String
+
+  @[JSON::Field(key: "releaseDate")]
+  property release_date : String
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+end
+
+class Card
+  include JSON::Serializable
+
+  property artist : String
+
+  @[JSON::Field(key: "type")]
+  property card_type : String
+
+  property cmc : Int32
+
+  @[JSON::Field(key: "colorIdentity")]
+  property color_identity : Array(String)?
+
+  property colors : Array(String)?
+
+  property flavor : String?
+
+  property id : String
+
+  @[JSON::Field(key: "imageName")]
+  property image_name : String
+
+  property layout : String
+
+  property legalities : Array(LegalityElement)
+
+  @[JSON::Field(key: "manaCost")]
+  property mana_cost : String?
+
+  @[JSON::Field(key: "mciNumber")]
+  property mci_number : String?
+
+  property multiverseid : Int32
+
+  property name : String
+
+  @[JSON::Field(key: "originalText")]
+  property original_text : String?
+
+  @[JSON::Field(key: "originalType")]
+  property original_type : String
+
+  property power : String?
+
+  property printings : Array(String)
+
+  property rarity : String
+
+  property reserved : Bool?
+
+  property rulings : Array(Ruling)?
+
+  property subtypes : Array(String)?
+
+  property supertypes : Array(String)?
+
+  property text : String?
+
+  property toughness : String?
+
+  property types : Array(String)
+
+  property variations : Array(Int32)?
+end
+
+class LegalityElement
+  include JSON::Serializable
+
+  property format : String
+
+  property legality : String
+end
+
+class Ruling
+  include JSON::Serializable
+
+  property date : String
+
+  property text : String
+end
diff --git a/head/crystal/test/inputs/json/misc/dec3a.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/dec3a.json/default/TopLevel.cr
new file mode 100644
index 0000000..da3dfd6
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/dec3a.json/default/TopLevel.cr
@@ -0,0 +1,120 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property metadata : Metadata
+
+  property results : Array(Result)
+end
+
+class Metadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "executionTime")]
+  property execution_time : Float64
+
+  @[JSON::Field(key: "responseInfo")]
+  property response_info : ResponseInfo
+
+  property resultset : Resultset
+end
+
+class ResponseInfo
+  include JSON::Serializable
+
+  @[JSON::Field(key: "developerMessage")]
+  property developer_message : String
+
+  property status : Int32
+end
+
+class Resultset
+  include JSON::Serializable
+
+  property count : Int32
+
+  property page : Int32
+
+  property pagesize : Int32
+end
+
+class Result
+  include JSON::Serializable
+
+  property about_office : String
+
+  property application_process : String
+
+  property body : String
+
+  property changed : String
+
+  property created : String
+
+  property deadline : Nil
+
+  property hiring_office : Nil
+
+  property hiring_org : HiringOrg
+
+  property job_id : Nil
+
+  property language : String
+
+  property location : Location
+
+  property num_positions : String
+
+  property position : String
+
+  property practice_area : String
+
+  property qualifications : String
+
+  property relocation_expenses : Nil
+
+  property salary : String
+
+  property title : String
+
+  property travel : String
+
+  property url : String
+
+  property uuid : String
+
+  property vuuid : String
+end
+
+class HiringOrg
+  include JSON::Serializable
+
+  property name : String
+
+  property uuid : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property administrative_area : String
+
+  property country : String
+
+  property fax_number : String
+
+  property locality : String
+
+  property mobile_number : String
+
+  property phone_number : String
+
+  property phone_number_extension : String
+
+  property postal_code : String
+
+  property sub_premise : Nil
+
+  property thoroughfare : String
+end
diff --git a/head/crystal/test/inputs/json/misc/df957.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/df957.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/df957.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/e0ac7.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/e0ac7.json/default/TopLevel.cr
new file mode 100644
index 0000000..4971ae8
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/e0ac7.json/default/TopLevel.cr
@@ -0,0 +1,194 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  property meta : Meta
+
+  property pagination : Pagination
+end
+
+class Datum
+  include JSON::Serializable
+
+  property bitly_gif_url : String
+
+  property bitly_url : String
+
+  property content_url : String
+
+  @[JSON::Field(key: "type")]
+  property datum_type : String
+
+  property embed_url : String
+
+  property id : String
+
+  property images : Images
+
+  property import_datetime : String
+
+  property is_indexable : Int32
+
+  property rating : String
+
+  property slug : String
+
+  property source : String
+
+  property source_post_url : String
+
+  property source_tld : String
+
+  property trending_datetime : String
+
+  property url : String
+
+  property user : User?
+
+  property username : String
+end
+
+class Images
+  include JSON::Serializable
+
+  property downsized : Downsized
+
+  property downsized_large : Downsized
+
+  property downsized_medium : Downsized
+
+  property downsized_small : DownsizedSmall
+
+  property downsized_still : Downsized
+
+  property fixed_height : FixedHeight
+
+  property fixed_height_downsampled : FixedHeight
+
+  property fixed_height_small : FixedHeight
+
+  property fixed_height_small_still : Downsized
+
+  property fixed_height_still : Downsized
+
+  property fixed_width : FixedHeight
+
+  property fixed_width_downsampled : FixedHeight
+
+  property fixed_width_small : FixedHeight
+
+  property fixed_width_small_still : Downsized
+
+  property fixed_width_still : Downsized
+
+  property hd : DownsizedSmall?
+
+  property looping : Looping
+
+  property original : FixedHeight
+
+  property original_mp4 : DownsizedSmall
+
+  property original_still : Downsized
+
+  property preview : DownsizedSmall
+
+  property preview_gif : Downsized
+
+  property preview_webp : Downsized
+end
+
+class Downsized
+  include JSON::Serializable
+
+  property height : String
+
+  property size : String?
+
+  property url : String
+
+  property width : String
+end
+
+class DownsizedSmall
+  include JSON::Serializable
+
+  property height : String
+
+  property mp4 : String
+
+  property mp4_size : String
+
+  property width : String
+end
+
+class FixedHeight
+  include JSON::Serializable
+
+  property frames : String?
+
+  property hash : String?
+
+  property height : String
+
+  property mp4 : String?
+
+  property mp4_size : String?
+
+  property size : String
+
+  property url : String
+
+  property webp : String
+
+  property webp_size : String
+
+  property width : String
+end
+
+class Looping
+  include JSON::Serializable
+
+  property mp4 : String
+
+  property mp4_size : String
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property banner_url : String
+
+  property display_name : String
+
+  property profile_url : String
+
+  property twitter : String
+
+  property username : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property msg : String
+
+  property response_id : String
+
+  property status : Int32
+end
+
+class Pagination
+  include JSON::Serializable
+
+  property count : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/e2915.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/e2915.json/default/TopLevel.cr
new file mode 100644
index 0000000..e8c4b6a
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/e2915.json/default/TopLevel.cr
@@ -0,0 +1,180 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property query : Query
+end
+
+class Query
+  include JSON::Serializable
+
+  property count : Int32
+
+  property created : String
+
+  property lang : String
+
+  property results : Results
+end
+
+class Results
+  include JSON::Serializable
+
+  property channel : ChannelClass
+end
+
+class ChannelClass
+  include JSON::Serializable
+
+  property astronomy : Astronomy
+
+  property atmosphere : Atmosphere
+
+  property description : String
+
+  property image : Image
+
+  property item : Item
+
+  property language : String
+
+  @[JSON::Field(key: "lastBuildDate")]
+  property last_build_date : String
+
+  property link : String
+
+  property location : Location
+
+  property title : String
+
+  property ttl : String
+
+  property units : Units
+
+  property wind : Wind
+end
+
+class Astronomy
+  include JSON::Serializable
+
+  property sunrise : String
+
+  property sunset : String
+end
+
+class Atmosphere
+  include JSON::Serializable
+
+  property humidity : String
+
+  property pressure : String
+
+  property rising : String
+
+  property visibility : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : String
+
+  property link : String
+
+  property title : String
+
+  property url : String
+
+  property width : String
+end
+
+class Item
+  include JSON::Serializable
+
+  property condition : Condition
+
+  property description : String
+
+  property forecast : Array(Forecast)
+
+  property guid : Guid
+
+  property lat : String
+
+  property link : String
+
+  property long : String
+
+  @[JSON::Field(key: "pubDate")]
+  property pub_date : String
+
+  property title : String
+end
+
+class Condition
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property temp : String
+
+  property text : String
+end
+
+class Forecast
+  include JSON::Serializable
+
+  property code : String
+
+  property date : String
+
+  property day : String
+
+  property high : String
+
+  property low : String
+
+  property text : String
+end
+
+class Guid
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPermaLink")]
+  property is_perma_link : String
+end
+
+class Location
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property region : String
+end
+
+class Units
+  include JSON::Serializable
+
+  property distance : String
+
+  property pressure : String
+
+  property speed : String
+
+  property temperature : String
+end
+
+class Wind
+  include JSON::Serializable
+
+  property chill : String
+
+  property direction : String
+
+  property speed : String
+end
diff --git a/head/crystal/test/inputs/json/misc/e2a58.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/e2a58.json/default/TopLevel.cr
new file mode 100644
index 0000000..512b351
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/e2a58.json/default/TopLevel.cr
@@ -0,0 +1,12 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property date : String
+
+  @[JSON::Field(key: "stop-and-search")]
+  property stop_and_search : Array(String)
+end
diff --git a/head/crystal/test/inputs/json/misc/e324e.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/e324e.json/default/TopLevel.cr
new file mode 100644
index 0000000..6c40d40
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/e324e.json/default/TopLevel.cr
@@ -0,0 +1,130 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "basePath")]
+  property base_path : String
+
+  property definitions : Definitions
+
+  property host : String
+
+  property info : Info
+
+  property paths : Paths
+
+  property produces : Array(String)
+
+  property schemes : Array(String)
+
+  property swagger : String
+end
+
+class Definitions
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Rate")]
+  property rate : Rate
+end
+
+class Rate
+  include JSON::Serializable
+
+  property properties : Hash(String, Property)
+end
+
+class Property
+  include JSON::Serializable
+
+  property description : String
+
+  @[JSON::Field(key: "type")]
+  property property_type : String
+end
+
+class Info
+  include JSON::Serializable
+
+  property description : String
+
+  property title : String
+
+  property version : String
+end
+
+class Paths
+  include JSON::Serializable
+
+  @[JSON::Field(key: "/tariff_rates/search")]
+  property tariff_rates_search : TariffRatesSearch
+end
+
+class TariffRatesSearch
+  include JSON::Serializable
+
+  property get : Get
+end
+
+class Get
+  include JSON::Serializable
+
+  property description : String
+
+  property parameters : Array(Parameter)
+
+  property responses : Responses
+
+  property summary : String
+
+  property tags : Array(String)
+end
+
+class Parameter
+  include JSON::Serializable
+
+  property description : String
+
+  property format : String
+
+  property name : String
+
+  @[JSON::Field(key: "in")]
+  property parameter_in : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : String
+
+  property required : Bool
+end
+
+class Responses
+  include JSON::Serializable
+
+  @[JSON::Field(key: "200")]
+  property the_200 : The200
+end
+
+class The200
+  include JSON::Serializable
+
+  property description : String
+
+  property schema : Schema
+end
+
+class Schema
+  include JSON::Serializable
+
+  property items : Items
+
+  @[JSON::Field(key: "type")]
+  property schema_type : String
+end
+
+class Items
+  include JSON::Serializable
+
+  @[JSON::Field(key: "$ref")]
+  property ref : String
+end
diff --git a/head/crystal/test/inputs/json/misc/e53b5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/e53b5.json/default/TopLevel.cr
new file mode 100644
index 0000000..4b9cdae
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/e53b5.json/default/TopLevel.cr
@@ -0,0 +1,39 @@
+require "json"
+
+alias TopLevel = Array(TopLevelUnion)
+
+class PurpleTopLevel
+  include JSON::Serializable
+
+  property country : Country
+
+  property date : String
+
+  property decimal : String
+
+  property indicator : Country
+
+  property value : String
+end
+
+class Country
+  include JSON::Serializable
+
+  property id : String
+
+  property value : String
+end
+
+class FluffyTopLevel
+  include JSON::Serializable
+
+  property page : Int32
+
+  property pages : Int32
+
+  property per_page : String
+
+  property total : Int32
+end
+
+alias TopLevelUnion = FluffyTopLevel | Array(PurpleTopLevel)
diff --git a/head/crystal/test/inputs/json/misc/e64a0.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/e64a0.json/default/TopLevel.cr
new file mode 100644
index 0000000..ac985ae
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/e64a0.json/default/TopLevel.cr
@@ -0,0 +1,34 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property args : Args
+
+  property headers : Headers
+
+  property origin : String
+
+  property url : String
+end
+
+class Args
+  include JSON::Serializable
+
+end
+
+class Headers
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Accept-Encoding")]
+  property accept_encoding : String
+
+  @[JSON::Field(key: "Connection")]
+  property connection : String
+
+  @[JSON::Field(key: "Host")]
+  property host : String
+
+  @[JSON::Field(key: "User-Agent")]
+  property user_agent : String
+end
diff --git a/head/crystal/test/inputs/json/misc/e8a0b.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/e8a0b.json/default/TopLevel.cr
new file mode 100644
index 0000000..943073c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/e8a0b.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property age : Int32
+
+  property country : String
+
+  property females : Int32
+
+  property males : Int32
+
+  property total : Int32
+
+  property year : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/e8b04.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/e8b04.json/default/TopLevel.cr
new file mode 100644
index 0000000..2981ea7
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/e8b04.json/default/TopLevel.cr
@@ -0,0 +1,404 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  @[JSON::Field(key: "averageRating")]
+  property average_rating : Int32
+
+  property category : String?
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : Int32
+
+  property description : String?
+
+  @[JSON::Field(key: "displayType")]
+  property display_type : String?
+
+  @[JSON::Field(key: "downloadCount")]
+  property download_count : Int32
+
+  property flags : Array(String)?
+
+  property grants : Array(Grant)
+
+  @[JSON::Field(key: "hideFromCatalog")]
+  property hide_from_catalog : Bool
+
+  @[JSON::Field(key: "hideFromDataJson")]
+  property hide_from_data_json : Bool
+
+  property id : String
+
+  @[JSON::Field(key: "indexUpdatedAt")]
+  property index_updated_at : Int32?
+
+  property locale : String
+
+  property metadata : TopLevelMetadata
+
+  @[JSON::Field(key: "moderationStatus")]
+  property moderation_status : Bool?
+
+  @[JSON::Field(key: "modifyingViewUid")]
+  property modifying_view_uid : String?
+
+  property name : String
+
+  @[JSON::Field(key: "newBackend")]
+  property new_backend : Bool
+
+  @[JSON::Field(key: "numberOfComments")]
+  property number_of_comments : Int32
+
+  property oid : Int32
+
+  property owner : Owner
+
+  property provenance : String
+
+  @[JSON::Field(key: "publicationAppendEnabled")]
+  property publication_append_enabled : Bool
+
+  @[JSON::Field(key: "publicationDate")]
+  property publication_date : Int32?
+
+  @[JSON::Field(key: "publicationGroup")]
+  property publication_group : Int32
+
+  @[JSON::Field(key: "publicationStage")]
+  property publication_stage : String
+
+  property ratings : Ratings?
+
+  @[JSON::Field(key: "resourceName")]
+  property resource_name : String?
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "rowClass")]
+  property row_class : String?
+
+  @[JSON::Field(key: "rowIdentifierColumnId")]
+  property row_identifier_column_id : Int32?
+
+  @[JSON::Field(key: "rowsUpdatedAt")]
+  property rows_updated_at : Int32?
+
+  @[JSON::Field(key: "rowsUpdatedBy")]
+  property rows_updated_by : String?
+
+  @[JSON::Field(key: "tableAuthor")]
+  property table_author : TableAuthor
+
+  @[JSON::Field(key: "tableId")]
+  property table_id : Int32
+
+  property tags : Array(String)?
+
+  @[JSON::Field(key: "totalTimesRated")]
+  property total_times_rated : Int32
+
+  @[JSON::Field(key: "viewCount")]
+  property view_count : Int32
+
+  @[JSON::Field(key: "viewLastModified")]
+  property view_last_modified : Int32
+
+  @[JSON::Field(key: "viewType")]
+  property view_type : String
+end
+
+class Grant
+  include JSON::Serializable
+
+  property flags : Array(String)
+
+  @[JSON::Field(key: "type")]
+  property grant_type : String
+
+  property inherited : Bool
+end
+
+class TopLevelMetadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "availableDisplayTypes")]
+  property available_display_types : Array(String)?
+
+  property custom_fields : CustomFields?
+
+  @[JSON::Field(key: "jsonQuery")]
+  property json_query : JsonQuery?
+
+  @[JSON::Field(key: "rdfClass")]
+  property rdf_class : String?
+
+  @[JSON::Field(key: "rdfSubject")]
+  property rdf_subject : String?
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : MetadataRenderTypeConfig?
+
+  @[JSON::Field(key: "richRendererConfigs")]
+  property rich_renderer_configs : RichRendererConfigs?
+
+  @[JSON::Field(key: "rowIdentifier")]
+  property row_identifier : String?
+
+  @[JSON::Field(key: "rowLabel")]
+  property row_label : String?
+
+  property v1_archived_properties : V1ArchivedProperties?
+end
+
+class CustomFields
+  include JSON::Serializable
+
+  @[JSON::Field(key: "TEST")]
+  property test : Test
+end
+
+class Test
+  include JSON::Serializable
+
+  @[JSON::Field(key: "CFPB1")]
+  property cfpb1 : String
+end
+
+class JsonQuery
+  include JSON::Serializable
+
+  property group : Array(Group)?
+
+  @[JSON::Field(key: "select")]
+  property json_query_select : Array(Select)?
+
+  property order : Array(Order)?
+
+  property where : Where?
+end
+
+class Group
+  include JSON::Serializable
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String
+end
+
+class Select
+  include JSON::Serializable
+
+  property aggregate : String?
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String
+end
+
+class Order
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String
+end
+
+class Where
+  include JSON::Serializable
+
+  property children : Array(Child)?
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String?
+
+  property metadata : ChildMetadata?
+
+  property operator : String
+
+  property value : String?
+end
+
+class Child
+  include JSON::Serializable
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String
+
+  property metadata : ChildMetadata?
+
+  property operator : String
+
+  property value : String?
+end
+
+class ChildMetadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "customValues")]
+  property custom_values : Array(String)?
+
+  property freeform : Bool?
+
+  @[JSON::Field(key: "includeAuto")]
+  property include_auto : Int32?
+
+  property operator : String?
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : TableColumnId?
+
+  @[JSON::Field(key: "unifiedVersion")]
+  property unified_version : Int32?
+end
+
+class TableColumnId
+  include JSON::Serializable
+
+  @[JSON::Field(key: "2819740")]
+  property the_2819740 : Int32
+end
+
+class MetadataRenderTypeConfig
+  include JSON::Serializable
+
+  property visible : PurpleVisible
+end
+
+class PurpleVisible
+  include JSON::Serializable
+
+  property fatrow : Bool?
+
+  property table : Bool?
+end
+
+class RichRendererConfigs
+  include JSON::Serializable
+
+  @[JSON::Field(key: "fatRow")]
+  property fat_row : FatRow
+end
+
+class FatRow
+  include JSON::Serializable
+
+  property columns : Array(Column)
+end
+
+class Column
+  include JSON::Serializable
+
+  property rows : Array(Row)
+
+  property styles : Styles
+end
+
+class Row
+  include JSON::Serializable
+
+  property fields : Array(Field)
+end
+
+class Field
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property field_type : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : Int32
+end
+
+class Styles
+  include JSON::Serializable
+
+  property width : String
+end
+
+class V1ArchivedProperties
+  include JSON::Serializable
+
+  @[JSON::Field(key: "accessPoints")]
+  property access_points : AccessPoints
+
+  property blist_id : String
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : V1ArchivedPropertiesRenderTypeConfig
+end
+
+class AccessPoints
+  include JSON::Serializable
+
+  property new_view : String
+end
+
+class V1ArchivedPropertiesRenderTypeConfig
+  include JSON::Serializable
+
+  property visible : FluffyVisible
+end
+
+class FluffyVisible
+  include JSON::Serializable
+
+  property href : Bool
+end
+
+class Owner
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property flags : Array(String)?
+
+  property id : String
+
+  @[JSON::Field(key: "lastNotificationSeenAt")]
+  property last_notification_seen_at : Int32?
+
+  @[JSON::Field(key: "profileImageUrlLarge")]
+  property profile_image_url_large : String?
+
+  @[JSON::Field(key: "profileImageUrlMedium")]
+  property profile_image_url_medium : String?
+
+  @[JSON::Field(key: "profileImageUrlSmall")]
+  property profile_image_url_small : String?
+
+  property rights : Array(String)?
+
+  @[JSON::Field(key: "roleName")]
+  property role_name : String?
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
+
+class Ratings
+  include JSON::Serializable
+
+  property rating : Int32
+end
+
+class TableAuthor
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property id : String
+
+  property rights : Array(String)?
+
+  @[JSON::Field(key: "roleName")]
+  property role_name : String?
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
diff --git a/head/crystal/test/inputs/json/misc/ed095.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/ed095.json/default/TopLevel.cr
new file mode 100644
index 0000000..2a8e123
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/ed095.json/default/TopLevel.cr
@@ -0,0 +1,3 @@
+require "json"
+
+alias TopLevel = Hash(String, String)
diff --git a/head/crystal/test/inputs/json/misc/f22f5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/f22f5.json/default/TopLevel.cr
new file mode 100644
index 0000000..0631809
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/f22f5.json/default/TopLevel.cr
@@ -0,0 +1,158 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : Nil
+
+  property author_flair_text : Nil
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Float64
+
+  property created_utc : Float64
+
+  property distinguished : Nil
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Bool
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : String?
+
+  property link_flair_text : String?
+
+  property locked : Bool
+
+  property media : Nil
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property permalink : String
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Nil
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : Nil
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : Nil
+
+  property thumbnail : String
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+end
diff --git a/head/crystal/test/inputs/json/misc/f3139.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/f3139.json/default/TopLevel.cr
new file mode 100644
index 0000000..cf510c3
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/f3139.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property id : String
+
+  property name : String
+end
diff --git a/head/crystal/test/inputs/json/misc/f3edf.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/f3edf.json/default/TopLevel.cr
new file mode 100644
index 0000000..943073c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/f3edf.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property age : Int32
+
+  property country : String
+
+  property females : Int32
+
+  property males : Int32
+
+  property total : Int32
+
+  property year : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/f466a.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/f466a.json/default/TopLevel.cr
new file mode 100644
index 0000000..943073c
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/f466a.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property age : Int32
+
+  property country : String
+
+  property females : Int32
+
+  property males : Int32
+
+  property total : Int32
+
+  property year : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/f6a65.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/f6a65.json/default/TopLevel.cr
new file mode 100644
index 0000000..88dfd89
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/f6a65.json/default/TopLevel.cr
@@ -0,0 +1,195 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Datum)
+
+  property meta : Meta
+
+  property pagination : Pagination
+end
+
+class Datum
+  include JSON::Serializable
+
+  property bitly_gif_url : String
+
+  property bitly_url : String
+
+  property content_url : String
+
+  @[JSON::Field(key: "type")]
+  property datum_type : String
+
+  property embed_url : String
+
+  property id : String
+
+  property images : Images
+
+  property import_datetime : String
+
+  property is_indexable : Int32
+
+  property rating : String
+
+  property slug : String
+
+  property source : String
+
+  property source_post_url : String
+
+  property source_tld : String
+
+  property trending_datetime : String
+
+  property url : String
+
+  property user : User?
+
+  property username : String
+end
+
+class Images
+  include JSON::Serializable
+
+  property downsized : Downsized
+
+  property downsized_large : Downsized
+
+  property downsized_medium : Downsized
+
+  property downsized_small : DownsizedSmall
+
+  property downsized_still : Downsized
+
+  property fixed_height : FixedHeight
+
+  property fixed_height_downsampled : FixedHeight
+
+  property fixed_height_small : FixedHeight
+
+  property fixed_height_small_still : Downsized
+
+  property fixed_height_still : Downsized
+
+  property fixed_width : FixedHeight
+
+  property fixed_width_downsampled : FixedHeight
+
+  property fixed_width_small : FixedHeight
+
+  property fixed_width_small_still : Downsized
+
+  property fixed_width_still : Downsized
+
+  property looping : Looping
+
+  property original : FixedHeight
+
+  property original_mp4 : DownsizedSmall
+
+  property original_still : Downsized
+
+  property preview : DownsizedSmall
+
+  property preview_gif : Downsized
+
+  property preview_webp : Downsized
+
+  @[JSON::Field(key: "480w_still")]
+  property the_480_w_still : Downsized?
+end
+
+class Downsized
+  include JSON::Serializable
+
+  property height : String
+
+  property size : String?
+
+  property url : String
+
+  property width : String
+end
+
+class DownsizedSmall
+  include JSON::Serializable
+
+  property height : String
+
+  property mp4 : String
+
+  property mp4_size : String
+
+  property width : String
+end
+
+class FixedHeight
+  include JSON::Serializable
+
+  property frames : String?
+
+  property hash : String?
+
+  property height : String
+
+  property mp4 : String?
+
+  property mp4_size : String?
+
+  property size : String
+
+  property url : String
+
+  property webp : String
+
+  property webp_size : String
+
+  property width : String
+end
+
+class Looping
+  include JSON::Serializable
+
+  property mp4 : String
+
+  property mp4_size : String
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property banner_url : String
+
+  property display_name : String
+
+  property profile_url : String
+
+  property twitter : String
+
+  property username : String
+end
+
+class Meta
+  include JSON::Serializable
+
+  property msg : String
+
+  property response_id : String
+
+  property status : Int32
+end
+
+class Pagination
+  include JSON::Serializable
+
+  property count : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/f74d5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/f74d5.json/default/TopLevel.cr
new file mode 100644
index 0000000..d63234b
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/f74d5.json/default/TopLevel.cr
@@ -0,0 +1,240 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Array(Datum))
+
+  property meta : Meta
+end
+
+class Meta
+  include JSON::Serializable
+
+  property view : View
+end
+
+class View
+  include JSON::Serializable
+
+  @[JSON::Field(key: "averageRating")]
+  property average_rating : Int32
+
+  property category : String
+
+  property columns : Array(Column)
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : Int32
+
+  @[JSON::Field(key: "displayType")]
+  property display_type : String
+
+  @[JSON::Field(key: "downloadCount")]
+  property download_count : Int32
+
+  property flags : Array(String)
+
+  property grants : Array(Grant)
+
+  @[JSON::Field(key: "hideFromCatalog")]
+  property hide_from_catalog : Bool
+
+  @[JSON::Field(key: "hideFromDataJson")]
+  property hide_from_data_json : Bool
+
+  property id : String
+
+  @[JSON::Field(key: "indexUpdatedAt")]
+  property index_updated_at : Int32
+
+  property license : License
+
+  @[JSON::Field(key: "licenseId")]
+  property license_id : String
+
+  property locale : String
+
+  property metadata : Metadata
+
+  property name : String
+
+  @[JSON::Field(key: "newBackend")]
+  property new_backend : Bool
+
+  @[JSON::Field(key: "numberOfComments")]
+  property number_of_comments : Int32
+
+  property oid : Int32
+
+  property owner : Owner
+
+  property provenance : String
+
+  @[JSON::Field(key: "publicationAppendEnabled")]
+  property publication_append_enabled : Bool
+
+  @[JSON::Field(key: "publicationDate")]
+  property publication_date : Int32
+
+  @[JSON::Field(key: "publicationGroup")]
+  property publication_group : Int32
+
+  @[JSON::Field(key: "publicationStage")]
+  property publication_stage : String
+
+  property query : Query
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "rowClass")]
+  property row_class : String
+
+  @[JSON::Field(key: "rowsUpdatedAt")]
+  property rows_updated_at : Int32
+
+  @[JSON::Field(key: "rowsUpdatedBy")]
+  property rows_updated_by : String
+
+  @[JSON::Field(key: "tableAuthor")]
+  property table_author : Owner
+
+  @[JSON::Field(key: "tableId")]
+  property table_id : Int32
+
+  @[JSON::Field(key: "totalTimesRated")]
+  property total_times_rated : Int32
+
+  @[JSON::Field(key: "viewCount")]
+  property view_count : Int32
+
+  @[JSON::Field(key: "viewLastModified")]
+  property view_last_modified : Int32
+
+  @[JSON::Field(key: "viewType")]
+  property view_type : String
+end
+
+class Column
+  include JSON::Serializable
+
+  @[JSON::Field(key: "cachedContents")]
+  property cached_contents : CachedContents?
+
+  @[JSON::Field(key: "dataTypeName")]
+  property data_type_name : String
+
+  @[JSON::Field(key: "fieldName")]
+  property field_name : String
+
+  property flags : Array(String)?
+
+  property format : Query
+
+  property id : Int32
+
+  property name : String
+
+  property position : Int32
+
+  @[JSON::Field(key: "renderTypeName")]
+  property render_type_name : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : Int32?
+
+  property width : Int32?
+end
+
+class CachedContents
+  include JSON::Serializable
+
+  property average : String?
+
+  property largest : String
+
+  property non_null : Int32
+
+  property null : Int32
+
+  property smallest : String
+
+  property sum : String?
+
+  property top : Array(Top)
+end
+
+class Top
+  include JSON::Serializable
+
+  property count : Int32
+
+  property item : String
+end
+
+class Query
+  include JSON::Serializable
+
+end
+
+class Grant
+  include JSON::Serializable
+
+  property flags : Array(String)
+
+  @[JSON::Field(key: "type")]
+  property grant_type : String
+
+  property inherited : Bool
+end
+
+class License
+  include JSON::Serializable
+
+  property name : String
+end
+
+class Metadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "availableDisplayTypes")]
+  property available_display_types : Array(String)
+
+  @[JSON::Field(key: "rdfClass")]
+  property rdf_class : String
+
+  @[JSON::Field(key: "rdfSubject")]
+  property rdf_subject : String
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : RenderTypeConfig
+
+  @[JSON::Field(key: "rowIdentifier")]
+  property row_identifier : String
+end
+
+class RenderTypeConfig
+  include JSON::Serializable
+
+  property visible : Visible
+end
+
+class Visible
+  include JSON::Serializable
+
+  property table : Bool
+end
+
+class Owner
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property id : String
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
+
+alias Datum = Int32 | String
diff --git a/head/crystal/test/inputs/json/misc/f82d9.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/f82d9.json/default/TopLevel.cr
new file mode 100644
index 0000000..b2b66ba
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/f82d9.json/default/TopLevel.cr
@@ -0,0 +1,154 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "basePath")]
+  property base_path : String
+
+  property definitions : Definitions
+
+  property host : String
+
+  property info : Info
+
+  property paths : Paths
+
+  property produces : Array(String)
+
+  property schemes : Array(String)
+
+  property swagger : String
+end
+
+class Definitions
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Report")]
+  property report : Report
+end
+
+class Report
+  include JSON::Serializable
+
+  property properties : Properties
+end
+
+class Properties
+  include JSON::Serializable
+
+  property click_url : ClickUrl
+
+  property country : ClickUrl
+
+  property description : ClickUrl
+
+  property expiration_date : ClickUrl
+
+  property id : ClickUrl
+
+  property industry : ClickUrl
+
+  property report_type : ClickUrl
+
+  property source_industry : ClickUrl
+
+  property title : ClickUrl
+
+  property url : ClickUrl
+end
+
+class ClickUrl
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property click_url_type : String
+
+  property description : String
+end
+
+class Info
+  include JSON::Serializable
+
+  property description : String
+
+  property title : String
+
+  property version : String
+end
+
+class Paths
+  include JSON::Serializable
+
+  @[JSON::Field(key: "/market_research_library/search")]
+  property market_research_library_search : MarketResearchLibrarySearch
+end
+
+class MarketResearchLibrarySearch
+  include JSON::Serializable
+
+  property get : Get
+end
+
+class Get
+  include JSON::Serializable
+
+  property description : String
+
+  property parameters : Array(Parameter)
+
+  property responses : Responses
+
+  property summary : String
+
+  property tags : Array(String)
+end
+
+class Parameter
+  include JSON::Serializable
+
+  property description : String
+
+  property format : String
+
+  property name : String
+
+  @[JSON::Field(key: "in")]
+  property parameter_in : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : String
+
+  property required : Bool
+end
+
+class Responses
+  include JSON::Serializable
+
+  @[JSON::Field(key: "200")]
+  property the_200 : The200
+end
+
+class The200
+  include JSON::Serializable
+
+  property description : String
+
+  property schema : Schema
+end
+
+class Schema
+  include JSON::Serializable
+
+  property items : Items
+
+  @[JSON::Field(key: "type")]
+  property schema_type : String
+end
+
+class Items
+  include JSON::Serializable
+
+  @[JSON::Field(key: "$ref")]
+  property ref : String
+end
diff --git a/head/crystal/test/inputs/json/misc/f974d.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/f974d.json/default/TopLevel.cr
new file mode 100644
index 0000000..2ae8da8
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/f974d.json/default/TopLevel.cr
@@ -0,0 +1,16 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property block_index : Int32
+
+  property hash : String
+
+  property height : Int32
+
+  property time : Int32
+
+  @[JSON::Field(key: "txIndexes")]
+  property tx_indexes : Array(Int32)
+end
diff --git a/head/crystal/test/inputs/json/misc/faff5.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/faff5.json/default/TopLevel.cr
new file mode 100644
index 0000000..8b60597
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/faff5.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property response : Response
+end
+
+class Response
+  include JSON::Serializable
+
+  property player_count : Int32
+
+  property result : Int32
+end
diff --git a/head/crystal/test/inputs/json/misc/fcca3.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/fcca3.json/default/TopLevel.cr
new file mode 100644
index 0000000..e643dbb
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/fcca3.json/default/TopLevel.cr
@@ -0,0 +1,436 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Array(Array(Datum?))
+
+  property meta : Meta
+end
+
+class Meta
+  include JSON::Serializable
+
+  property view : View
+end
+
+class View
+  include JSON::Serializable
+
+  property attribution : String
+
+  @[JSON::Field(key: "averageRating")]
+  property average_rating : Int32
+
+  property category : String
+
+  property columns : Array(Column)
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : Int32
+
+  property description : String
+
+  @[JSON::Field(key: "displayType")]
+  property display_type : String
+
+  @[JSON::Field(key: "downloadCount")]
+  property download_count : Int32
+
+  property flags : Array(String)
+
+  property grants : Array(Grant)
+
+  @[JSON::Field(key: "hideFromCatalog")]
+  property hide_from_catalog : Bool
+
+  @[JSON::Field(key: "hideFromDataJson")]
+  property hide_from_data_json : Bool
+
+  property id : String
+
+  @[JSON::Field(key: "indexUpdatedAt")]
+  property index_updated_at : Int32
+
+  property locale : String
+
+  property metadata : ViewMetadata
+
+  property name : String
+
+  @[JSON::Field(key: "newBackend")]
+  property new_backend : Bool
+
+  @[JSON::Field(key: "numberOfComments")]
+  property number_of_comments : Int32
+
+  property oid : Int32
+
+  property owner : Owner
+
+  property provenance : String
+
+  @[JSON::Field(key: "publicationAppendEnabled")]
+  property publication_append_enabled : Bool
+
+  @[JSON::Field(key: "publicationDate")]
+  property publication_date : Int32
+
+  @[JSON::Field(key: "publicationGroup")]
+  property publication_group : Int32
+
+  @[JSON::Field(key: "publicationStage")]
+  property publication_stage : String
+
+  property query : Query
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "rowsUpdatedAt")]
+  property rows_updated_at : Int32
+
+  @[JSON::Field(key: "rowsUpdatedBy")]
+  property rows_updated_by : String
+
+  @[JSON::Field(key: "tableAuthor")]
+  property table_author : Owner
+
+  @[JSON::Field(key: "tableId")]
+  property table_id : Int32
+
+  property tags : Array(String)
+
+  @[JSON::Field(key: "totalTimesRated")]
+  property total_times_rated : Int32
+
+  @[JSON::Field(key: "viewCount")]
+  property view_count : Int32
+
+  @[JSON::Field(key: "viewLastModified")]
+  property view_last_modified : Int32
+
+  @[JSON::Field(key: "viewType")]
+  property view_type : String
+end
+
+class Column
+  include JSON::Serializable
+
+  @[JSON::Field(key: "cachedContents")]
+  property cached_contents : CachedContents?
+
+  @[JSON::Field(key: "dataTypeName")]
+  property data_type_name : String
+
+  property description : String?
+
+  @[JSON::Field(key: "fieldName")]
+  property field_name : String
+
+  property flags : Array(String)?
+
+  property format : Format
+
+  property id : Int32
+
+  property name : String
+
+  property position : Int32
+
+  @[JSON::Field(key: "renderTypeName")]
+  property render_type_name : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : Int32?
+
+  property width : Int32?
+end
+
+class CachedContents
+  include JSON::Serializable
+
+  property average : String?
+
+  property largest : String
+
+  property non_null : Int32
+
+  property null : Int32
+
+  property smallest : String
+
+  property sum : String?
+
+  property top : Array(Top)
+end
+
+class Top
+  include JSON::Serializable
+
+  property count : Int32
+
+  property item : String
+end
+
+class Format
+  include JSON::Serializable
+
+  property align : String?
+
+  @[JSON::Field(key: "noCommas")]
+  property no_commas : String?
+
+  @[JSON::Field(key: "precisionStyle")]
+  property precision_style : String?
+end
+
+class Grant
+  include JSON::Serializable
+
+  property flags : Array(String)
+
+  @[JSON::Field(key: "type")]
+  property grant_type : String
+
+  property inherited : Bool
+end
+
+class ViewMetadata
+  include JSON::Serializable
+
+  property attachments : Array(Attachment)
+
+  @[JSON::Field(key: "availableDisplayTypes")]
+  property available_display_types : Array(String)
+
+  property custom_fields : CustomFields
+
+  @[JSON::Field(key: "filterCondition")]
+  property filter_condition : FilterCondition
+
+  @[JSON::Field(key: "jsonQuery")]
+  property json_query : JsonQuery
+
+  @[JSON::Field(key: "rdfSubject")]
+  property rdf_subject : String
+
+  @[JSON::Field(key: "renderTypeConfig")]
+  property render_type_config : RenderTypeConfig
+
+  @[JSON::Field(key: "rowLabel")]
+  property row_label : String
+end
+
+class Attachment
+  include JSON::Serializable
+
+  @[JSON::Field(key: "assetId")]
+  property asset_id : String
+
+  @[JSON::Field(key: "blobId")]
+  property blob_id : String
+
+  property filename : String
+
+  property name : String
+end
+
+class CustomFields
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Common Core")]
+  property common_core : CommonCore
+
+  @[JSON::Field(key: "Dataset Information")]
+  property dataset_information : DatasetInformation
+
+  @[JSON::Field(key: "Dataset Summary")]
+  property dataset_summary : DatasetSummary
+
+  @[JSON::Field(key: "Disclaimers")]
+  property disclaimers : Disclaimers
+end
+
+class CommonCore
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Contact Email")]
+  property contact_email : String
+
+  @[JSON::Field(key: "Contact Name")]
+  property contact_name : String
+
+  @[JSON::Field(key: "Publisher")]
+  property publisher : String
+end
+
+class DatasetInformation
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Agency")]
+  property agency : String
+end
+
+class DatasetSummary
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Contact Information")]
+  property contact_information : String
+
+  @[JSON::Field(key: "Coverage")]
+  property coverage : String
+
+  @[JSON::Field(key: "Granularity")]
+  property granularity : String
+
+  @[JSON::Field(key: "Organization")]
+  property organization : String
+
+  @[JSON::Field(key: "Posting Frequency")]
+  property posting_frequency : String
+
+  @[JSON::Field(key: "Time Period")]
+  property time_period : String
+end
+
+class Disclaimers
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Disclaimer")]
+  property disclaimer : String
+
+  @[JSON::Field(key: "Limitations")]
+  property limitations : String
+end
+
+class FilterCondition
+  include JSON::Serializable
+
+  property children : Array(Child)
+
+  @[JSON::Field(key: "type")]
+  property filter_condition_type : String
+
+  property metadata : FilterConditionMetadata
+
+  property value : String
+end
+
+class Child
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property child_type : String
+
+  property metadata : ChildMetadata
+
+  property value : String
+end
+
+class ChildMetadata
+  include JSON::Serializable
+
+  @[JSON::Field(key: "customValues")]
+  property custom_values : Array(Array(String))
+
+  property operator : String
+
+  @[JSON::Field(key: "tableColumnId")]
+  property table_column_id : TableColumnId
+end
+
+class TableColumnId
+  include JSON::Serializable
+
+  @[JSON::Field(key: "703610")]
+  property the_703610 : Int32
+end
+
+class FilterConditionMetadata
+  include JSON::Serializable
+
+  property advanced : Bool
+
+  @[JSON::Field(key: "unifiedVersion")]
+  property unified_version : Int32
+end
+
+class JsonQuery
+  include JSON::Serializable
+
+  property order : Array(Order)
+end
+
+class Order
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  @[JSON::Field(key: "columnFieldName")]
+  property column_field_name : String
+end
+
+class RenderTypeConfig
+  include JSON::Serializable
+
+  property visible : Visible
+end
+
+class Visible
+  include JSON::Serializable
+
+  property table : Bool
+end
+
+class Owner
+  include JSON::Serializable
+
+  @[JSON::Field(key: "displayName")]
+  property display_name : String
+
+  property id : String
+
+  @[JSON::Field(key: "profileImageUrlLarge")]
+  property profile_image_url_large : String
+
+  @[JSON::Field(key: "profileImageUrlMedium")]
+  property profile_image_url_medium : String
+
+  @[JSON::Field(key: "profileImageUrlSmall")]
+  property profile_image_url_small : String
+
+  property rights : Array(String)
+
+  @[JSON::Field(key: "roleName")]
+  property role_name : String
+
+  @[JSON::Field(key: "screenName")]
+  property screen_name : String
+end
+
+class Query
+  include JSON::Serializable
+
+  @[JSON::Field(key: "orderBys")]
+  property order_bys : Array(OrderBy)
+end
+
+class OrderBy
+  include JSON::Serializable
+
+  property ascending : Bool
+
+  property expression : Expression
+end
+
+class Expression
+  include JSON::Serializable
+
+  @[JSON::Field(key: "columnId")]
+  property column_id : Int32
+
+  @[JSON::Field(key: "type")]
+  property expression_type : String
+end
+
+alias Datum = Int32 | String
diff --git a/head/crystal/test/inputs/json/misc/fd329.json/default/TopLevel.cr b/head/crystal/test/inputs/json/misc/fd329.json/default/TopLevel.cr
new file mode 100644
index 0000000..c658019
--- /dev/null
+++ b/head/crystal/test/inputs/json/misc/fd329.json/default/TopLevel.cr
@@ -0,0 +1,29 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Hash(String, Datum)
+
+  property description : Description
+end
+
+class Datum
+  include JSON::Serializable
+
+  property anomaly : String
+
+  property value : String
+end
+
+class Description
+  include JSON::Serializable
+
+  property base_period : String
+
+  property missing : Int32
+
+  property title : String
+
+  property units : String
+end
diff --git a/head/crystal/test/inputs/json/priority/bug2037.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug2037.json/default/TopLevel.cr
new file mode 100644
index 0000000..e592444
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug2037.json/default/TopLevel.cr
@@ -0,0 +1,24 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property mission_specs : Hash(String, MissionSpec)
+end
+
+class MissionSpec
+  include JSON::Serializable
+
+  property objectives : Hash(String, Objective)
+end
+
+class Objective
+  include JSON::Serializable
+
+  property rewards : Hash(String, Reward)
+end
+
+class Reward
+  include JSON::Serializable
+
+end
diff --git a/head/crystal/test/inputs/json/priority/bug2521.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug2521.json/default/TopLevel.cr
new file mode 100644
index 0000000..52882f4
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug2521.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property options : Array(Option)
+end
+
+class Option
+  include JSON::Serializable
+
+  property foo : String?
+
+  property name : String
+end
diff --git a/head/crystal/test/inputs/json/priority/bug2590.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug2590.json/default/TopLevel.cr
new file mode 100644
index 0000000..90aedb3
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug2590.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property invoices : Array(Invoice)
+end
+
+class Invoice
+  include JSON::Serializable
+
+  @[JSON::Field(key: "createdAt")]
+  property created_at : String?
+
+  @[JSON::Field(key: "dueDate")]
+  property due_date : String?
+
+  property name : String
+end
diff --git a/head/crystal/test/inputs/json/priority/bug2663.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug2663.json/default/TopLevel.cr
new file mode 100644
index 0000000..a3b5dee
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug2663.json/default/TopLevel.cr
@@ -0,0 +1,12 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property birthday : String
+
+  @[JSON::Field(key: "lastSeen")]
+  property last_seen : String
+
+  property name : String
+end
diff --git a/head/crystal/test/inputs/json/priority/bug2793.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug2793.json/default/TopLevel.cr
new file mode 100644
index 0000000..054e471
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug2793.json/default/TopLevel.cr
@@ -0,0 +1,26 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Hints")]
+  property hints : Hints
+
+  @[JSON::Field(key: "Labels")]
+  property labels : Labels
+end
+
+class Hints
+  include JSON::Serializable
+
+end
+
+class Labels
+  include JSON::Serializable
+
+  @[JSON::Field(key: "AccountNumber")]
+  property account_number : String
+
+  @[JSON::Field(key: "Title")]
+  property title : String
+end
diff --git a/head/crystal/test/inputs/json/priority/bug427.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug427.json/default/TopLevel.cr
new file mode 100644
index 0000000..ba3b34e
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug427.json/default/TopLevel.cr
@@ -0,0 +1,1104 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property children : Array(TopLevelChild)
+
+  property flags : GetSignatureFlags
+
+  property groups : Array(Group)
+
+  property id : Int32
+
+  property kind : Int32
+
+  property name : String
+end
+
+class TopLevelChild
+  include JSON::Serializable
+
+  property children : Array(PurpleChild)
+
+  property comment : IndecentComment?
+
+  property flags : PurpleFlags
+
+  property groups : Array(Group)
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  @[JSON::Field(key: "originalName")]
+  property original_name : String
+
+  property sources : Array(Source)
+end
+
+class PurpleChild
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property child_type : MagentaType?
+
+  property children : Array(FluffyChild)?
+
+  property comment : StickyComment?
+
+  @[JSON::Field(key: "defaultValue")]
+  property default_value : String?
+
+  @[JSON::Field(key: "extendedBy")]
+  property extended_by : Array(ExtendedBy)?
+
+  @[JSON::Field(key: "extendedTypes")]
+  property extended_types : Array(ExtendedBy)?
+
+  property flags : IndecentFlags
+
+  property groups : Array(Group)?
+
+  property id : Int32
+
+  @[JSON::Field(key: "implementedBy")]
+  property implemented_by : Array(ExtendedBy)?
+
+  @[JSON::Field(key: "implementedTypes")]
+  property implemented_types : Array(ExtendedBy)?
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property signatures : Array(StickySignature)?
+
+  property sources : Array(Source)
+
+  @[JSON::Field(key: "typeParameter")]
+  property type_parameter : Array(TypeParameter)?
+end
+
+class MagentaType
+  include JSON::Serializable
+
+  property declaration : StickyDeclaration?
+
+  @[JSON::Field(key: "elementType")]
+  property element_type : ElementType?
+
+  property elements : Array(ExtendedBy)?
+
+  property id : Int32?
+
+  property name : String?
+
+  @[JSON::Field(key: "typeArguments")]
+  property type_arguments : Array(FluffyTypeArgument)?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+
+  property types : Array(ExtendedBy)?
+
+  property value : String?
+end
+
+class StickyDeclaration
+  include JSON::Serializable
+
+  property children : Array(IndecentChild)?
+
+  property flags : GetSignatureFlags
+
+  property groups : Array(Group)?
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property signatures : Array(GetSignature)
+
+  property sources : Array(Source)
+end
+
+class IndecentChild
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property child_type : FriskyType
+
+  property flags : FluffyFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property sources : Array(Source)
+end
+
+class FriskyType
+  include JSON::Serializable
+
+  property declaration : IndigoDeclaration?
+
+  property name : String?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class IndigoDeclaration
+  include JSON::Serializable
+
+  property children : Array(GetSignature)?
+
+  property flags : GetSignatureFlags
+
+  property groups : Array(Group)?
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property signatures : Array(GetSignature)?
+
+  property sources : Array(Source)
+end
+
+class IndexSignatureElement
+  include JSON::Serializable
+
+  property flags : GetSignatureFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property parameters : Array(GetSignature)?
+
+  @[JSON::Field(key: "type")]
+  property signature_type : PurpleType
+end
+
+class GetSignatureType
+  include JSON::Serializable
+
+  property declaration : GetSignature?
+
+  @[JSON::Field(key: "elementType")]
+  property element_type : ElementType?
+
+  property name : String?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class PurpleType
+  include JSON::Serializable
+
+  property declaration : GetSignature?
+
+  property name : String?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class GetSignatureChild
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property child_type : PurpleType
+
+  property comment : FluffyComment?
+
+  property flags : FluffyFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property sources : Array(Source)
+end
+
+class GetSignature
+  include JSON::Serializable
+
+  property children : Array(GetSignatureChild)?
+
+  property comment : GetSignatureComment?
+
+  @[JSON::Field(key: "defaultValue")]
+  property default_value : String?
+
+  property flags : GetSignatureFlags
+
+  @[JSON::Field(key: "type")]
+  property get_signature_type : GetSignatureType?
+
+  property groups : Array(Group)?
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property parameters : Array(GetSignatureParameter)?
+
+  property signatures : Array(IndexSignatureElement)?
+
+  property sources : Array(Source)?
+
+  @[JSON::Field(key: "typeParameter")]
+  property type_parameter : Array(GetSignature)?
+end
+
+class GetSignatureFlags
+  include JSON::Serializable
+
+end
+
+class ElementType
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property element_type_type : String
+
+  property name : String
+end
+
+class FluffyComment
+  include JSON::Serializable
+
+  property text : String
+end
+
+class FluffyFlags
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isOptional")]
+  property is_optional : Bool?
+end
+
+class Source
+  include JSON::Serializable
+
+  property character : Int32
+
+  @[JSON::Field(key: "fileName")]
+  property file_name : String
+
+  property line : Int32
+end
+
+class GetSignatureComment
+  include JSON::Serializable
+
+  property returns : String?
+
+  @[JSON::Field(key: "shortText")]
+  property short_text : String?
+
+  property text : String?
+end
+
+class Group
+  include JSON::Serializable
+
+  property children : Array(Int32)
+
+  property kind : Int32
+
+  property title : String
+end
+
+class GetSignatureParameter
+  include JSON::Serializable
+
+  property comment : TentacledComment?
+
+  property flags : TentacledFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : FluffyType
+end
+
+class TentacledComment
+  include JSON::Serializable
+
+  @[JSON::Field(key: "shortText")]
+  property short_text : String?
+
+  property text : String?
+end
+
+class TentacledFlags
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isOptional")]
+  property is_optional : Bool?
+
+  @[JSON::Field(key: "isRest")]
+  property is_rest : Bool?
+end
+
+class FluffyType
+  include JSON::Serializable
+
+  @[JSON::Field(key: "elementType")]
+  property element_type : ElementType?
+
+  property name : String?
+
+  @[JSON::Field(key: "typeArguments")]
+  property type_arguments : Array(ElementType)?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class ExtendedBy
+  include JSON::Serializable
+
+  property constraint : ExtendedBy?
+
+  @[JSON::Field(key: "type")]
+  property extended_by_type : String
+
+  property id : Int32?
+
+  property name : String
+
+  @[JSON::Field(key: "typeArguments")]
+  property type_arguments : Array(ExtendedBy)?
+end
+
+class FluffyTypeArgument
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property type_argument_type : String
+
+  property types : Array(ElementType)
+end
+
+class FluffyChild
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property child_type : IndigoType?
+
+  property children : Array(TentacledChild)?
+
+  property comment : StickyComment?
+
+  @[JSON::Field(key: "defaultValue")]
+  property default_value : String?
+
+  property flags : StickyFlags
+
+  @[JSON::Field(key: "getSignature")]
+  property get_signature : GetSignature?
+
+  property groups : Array(Group)?
+
+  property id : Int32
+
+  @[JSON::Field(key: "implementationOf")]
+  property implementation_of : ExtendedBy?
+
+  @[JSON::Field(key: "inheritedFrom")]
+  property inherited_from : ExtendedBy?
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property overwrites : ExtendedBy?
+
+  @[JSON::Field(key: "setSignature")]
+  property set_signature : GetSignature?
+
+  property signatures : Array(FluffySignature)?
+
+  property sources : Array(Source)
+end
+
+class IndigoType
+  include JSON::Serializable
+
+  property constraint : ExtendedBy?
+
+  property declaration : FluffyDeclaration?
+
+  @[JSON::Field(key: "elementType")]
+  property element_type : ExtendedBy?
+
+  property elements : Array(ElementType)?
+
+  property id : Int32?
+
+  property name : String?
+
+  @[JSON::Field(key: "typeArguments")]
+  property type_arguments : Array(PurpleTypeArgument)?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+
+  property types : Array(TypeElement)?
+end
+
+class FluffyDeclaration
+  include JSON::Serializable
+
+  property children : Array(StickyChild)?
+
+  property flags : GetSignatureFlags
+
+  property groups : Array(Group)?
+
+  property id : Int32
+
+  @[JSON::Field(key: "indexSignature")]
+  property index_signature : IndexSignature?
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property signatures : Array(TentacledSignature)?
+
+  property sources : Array(Source)
+end
+
+class StickyChild
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property child_type : IndecentType
+
+  property comment : FluffyComment?
+
+  property flags : FluffyFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property sources : Array(Source)
+end
+
+class IndecentType
+  include JSON::Serializable
+
+  property declaration : GetSignature?
+
+  property elements : Array(ElementType)?
+
+  property name : String?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class IndexSignature
+  include JSON::Serializable
+
+  property flags : GetSignatureFlags
+
+  property id : Int32
+
+  @[JSON::Field(key: "type")]
+  property index_signature_type : HilariousType
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property parameters : Array(GetSignature)
+end
+
+class HilariousType
+  include JSON::Serializable
+
+  property declaration : GetSignature
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class TentacledSignature
+  include JSON::Serializable
+
+  property comment : IndigoComment?
+
+  property flags : GetSignatureFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property parameters : Array(FluffyParameter)?
+
+  @[JSON::Field(key: "type")]
+  property signature_type : ExtendedBy
+end
+
+class IndigoComment
+  include JSON::Serializable
+
+  property returns : String
+
+  @[JSON::Field(key: "shortText")]
+  property short_text : String
+end
+
+class FluffyParameter
+  include JSON::Serializable
+
+  property comment : FluffyComment?
+
+  property flags : FluffyFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : ElementType
+
+  property sources : Array(Source)?
+end
+
+class PurpleTypeArgument
+  include JSON::Serializable
+
+  property operator : String
+
+  property target : ElementType
+
+  @[JSON::Field(key: "type")]
+  property type_argument_type : String
+end
+
+class TypeElement
+  include JSON::Serializable
+
+  property declaration : GetSignature?
+
+  @[JSON::Field(key: "elementType")]
+  property element_type : ElementType?
+
+  property name : String?
+
+  @[JSON::Field(key: "typeArguments")]
+  property type_arguments : Array(ElementType)?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class TentacledChild
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property child_type : TypeElement?
+
+  property comment : PurpleComment?
+
+  @[JSON::Field(key: "defaultValue")]
+  property default_value : String?
+
+  property flags : PurpleFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property signatures : Array(PurpleSignature)?
+
+  property sources : Array(Source)
+end
+
+class PurpleComment
+  include JSON::Serializable
+
+  @[JSON::Field(key: "shortText")]
+  property short_text : String
+end
+
+class PurpleFlags
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isExported")]
+  property is_exported : Bool?
+end
+
+class PurpleSignature
+  include JSON::Serializable
+
+  property comment : PurpleComment?
+
+  property flags : GetSignatureFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property parameters : Array(GetSignature)
+
+  @[JSON::Field(key: "type")]
+  property signature_type : TentacledType
+end
+
+class TentacledType
+  include JSON::Serializable
+
+  property declaration : PurpleDeclaration?
+
+  property name : String?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class PurpleDeclaration
+  include JSON::Serializable
+
+  property children : Array(GetSignature)
+
+  property flags : GetSignatureFlags
+
+  property groups : Array(Group)
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+end
+
+class StickyComment
+  include JSON::Serializable
+
+  property returns : String?
+
+  @[JSON::Field(key: "shortText")]
+  property short_text : String?
+
+  property tags : Array(Tag)?
+
+  property text : String?
+end
+
+class Tag
+  include JSON::Serializable
+
+  property tag : String
+
+  property text : String
+end
+
+class StickyFlags
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isAbstract")]
+  property is_abstract : Bool?
+
+  @[JSON::Field(key: "isConstructorProperty")]
+  property is_constructor_property : Bool?
+
+  @[JSON::Field(key: "isExported")]
+  property is_exported : Bool?
+
+  @[JSON::Field(key: "isPrivate")]
+  property is_private : Bool?
+
+  @[JSON::Field(key: "isProtected")]
+  property is_protected : Bool?
+
+  @[JSON::Field(key: "isPublic")]
+  property is_public : Bool?
+
+  @[JSON::Field(key: "isStatic")]
+  property is_static : Bool?
+end
+
+class FluffySignature
+  include JSON::Serializable
+
+  property comment : StickyComment?
+
+  property flags : IndigoFlags
+
+  property id : Int32
+
+  @[JSON::Field(key: "implementationOf")]
+  property implementation_of : ExtendedBy?
+
+  @[JSON::Field(key: "inheritedFrom")]
+  property inherited_from : ExtendedBy?
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property overwrites : ExtendedBy?
+
+  property parameters : Array(PurpleParameter)?
+
+  @[JSON::Field(key: "type")]
+  property signature_type : ExtendedBy
+
+  @[JSON::Field(key: "typeParameter")]
+  property type_parameter : Array(GetSignature)?
+end
+
+class IndigoFlags
+  include JSON::Serializable
+
+  @[JSON::Field(key: "isPrivate")]
+  property is_private : Bool?
+
+  @[JSON::Field(key: "isProtected")]
+  property is_protected : Bool?
+end
+
+class PurpleParameter
+  include JSON::Serializable
+
+  property comment : TentacledComment?
+
+  property flags : GetSignatureFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : StickyType
+end
+
+class StickyType
+  include JSON::Serializable
+
+  property constraint : ExtendedBy?
+
+  property declaration : GetSignature?
+
+  @[JSON::Field(key: "elementType")]
+  property element_type : ElementType?
+
+  property elements : Array(ExtendedBy)?
+
+  property id : Int32?
+
+  property name : String?
+
+  @[JSON::Field(key: "typeArguments")]
+  property type_arguments : Array(ElementType)?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class IndecentFlags
+  include JSON::Serializable
+
+  @[JSON::Field(key: "hasExportAssignment")]
+  property has_export_assignment : Bool?
+
+  @[JSON::Field(key: "isAbstract")]
+  property is_abstract : Bool?
+
+  @[JSON::Field(key: "isConst")]
+  property is_const : Bool?
+
+  @[JSON::Field(key: "isExported")]
+  property is_exported : Bool?
+
+  @[JSON::Field(key: "isLet")]
+  property is_let : Bool?
+
+  @[JSON::Field(key: "isPrivate")]
+  property is_private : Bool?
+
+  @[JSON::Field(key: "isProtected")]
+  property is_protected : Bool?
+end
+
+class StickySignature
+  include JSON::Serializable
+
+  property comment : StickyComment
+
+  property flags : IndigoFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property parameters : Array(TentacledParameter)?
+
+  @[JSON::Field(key: "type")]
+  property signature_type : CunningType
+
+  @[JSON::Field(key: "typeParameter")]
+  property type_parameter : Array(GetSignature)?
+end
+
+class TentacledParameter
+  include JSON::Serializable
+
+  property comment : TentacledComment?
+
+  @[JSON::Field(key: "defaultValue")]
+  property default_value : String?
+
+  property flags : TentacledFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  @[JSON::Field(key: "originalName")]
+  property original_name : String?
+
+  @[JSON::Field(key: "type")]
+  property parameter_type : AmbitiousType
+end
+
+class AmbitiousType
+  include JSON::Serializable
+
+  property declaration : TentacledDeclaration?
+
+  @[JSON::Field(key: "elementType")]
+  property element_type : ElementType?
+
+  property id : Int32?
+
+  property name : String?
+
+  @[JSON::Field(key: "typeArguments")]
+  property type_arguments : Array(ElementType)?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class TentacledDeclaration
+  include JSON::Serializable
+
+  property children : Array(IndigoChild)?
+
+  property flags : GetSignatureFlags
+
+  property groups : Array(Group)?
+
+  property id : Int32
+
+  @[JSON::Field(key: "indexSignature")]
+  property index_signature : IndexSignatureElement?
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property signatures : Array(GetSignature)?
+
+  property sources : Array(Source)
+end
+
+class IndigoChild
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property child_type : IndecentType
+
+  property comment : FluffyComment
+
+  @[JSON::Field(key: "defaultValue")]
+  property default_value : String?
+
+  property flags : FluffyFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  property sources : Array(Source)
+end
+
+class CunningType
+  include JSON::Serializable
+
+  property declaration : PurpleDeclaration?
+
+  property id : Int32?
+
+  property name : String?
+
+  @[JSON::Field(key: "typeArguments")]
+  property type_arguments : Array(ElementType)?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class TypeParameter
+  include JSON::Serializable
+
+  property comment : PurpleComment?
+
+  property flags : GetSignatureFlags
+
+  property id : Int32
+
+  property kind : Int32
+
+  @[JSON::Field(key: "kindString")]
+  property kind_string : String
+
+  property name : String
+
+  @[JSON::Field(key: "type")]
+  property type_parameter_type : MischievousType?
+end
+
+class MischievousType
+  include JSON::Serializable
+
+  property id : Int32?
+
+  property name : String?
+
+  property operator : String?
+
+  property target : ElementType?
+
+  @[JSON::Field(key: "type")]
+  property type_type : String
+end
+
+class IndecentComment
+  include JSON::Serializable
+
+  @[JSON::Field(key: "shortText")]
+  property short_text : String
+
+  property tags : Array(Tag)?
+end
diff --git a/head/crystal/test/inputs/json/priority/bug790.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug790.json/default/TopLevel.cr
new file mode 100644
index 0000000..f5490a1
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug790.json/default/TopLevel.cr
@@ -0,0 +1,21 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property children_data : Array(TopLevel)
+
+  property id : Int32
+
+  property is_active : Bool
+
+  property level : Int32
+
+  property name : String
+
+  property parent_id : Int32
+
+  property position : Int32
+
+  property product_count : Int32
+end
diff --git a/head/crystal/test/inputs/json/priority/bug855-short.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug855-short.json/default/TopLevel.cr
new file mode 100644
index 0000000..6098c97
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug855-short.json/default/TopLevel.cr
@@ -0,0 +1,17 @@
+require "json"
+
+alias TopLevel = Hash(String, TopLevelValue)
+
+class TopLevelValue
+  include JSON::Serializable
+
+  property channel_id : String
+
+  property channel_name : String
+
+  property code : String
+
+  property emoticon_set : Int32
+
+  property id : Int32
+end
diff --git a/head/crystal/test/inputs/json/priority/bug863.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/bug863.json/default/TopLevel.cr
new file mode 100644
index 0000000..ba4994c
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/bug863.json/default/TopLevel.cr
@@ -0,0 +1,169 @@
+require "json"
+
+alias TopLevel = Array(TopLevelUnion)
+
+class PurpleTopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "ALIENLEVEL")]
+  property alienlevel : Int32
+
+  @[JSON::Field(key: "BREED")]
+  property breed : String
+
+  @[JSON::Field(key: "CHAR_DIMENSION")]
+  property char_dimension : Int32
+
+  @[JSON::Field(key: "CHAR_INSTANCE")]
+  property char_instance : Int32
+
+  @[JSON::Field(key: "DEFENDER_RANK_TITLE")]
+  property defender_rank_title : String
+
+  @[JSON::Field(key: "FIRSTNAME")]
+  property firstname : String
+
+  @[JSON::Field(key: "HEADID")]
+  property headid : Int32
+
+  @[JSON::Field(key: "LASTNAME")]
+  property lastname : String
+
+  @[JSON::Field(key: "LEVELX")]
+  property levelx : Int32
+
+  @[JSON::Field(key: "NAME")]
+  property name : String
+
+  @[JSON::Field(key: "PROF")]
+  property prof : String
+
+  @[JSON::Field(key: "PROF_TITLE")]
+  property prof_title : String
+
+  @[JSON::Field(key: "PVPRATING")]
+  property pvprating : Int32
+
+  @[JSON::Field(key: "PVPTITLE")]
+  property pvptitle : String
+
+  @[JSON::Field(key: "RANK")]
+  property rank : Int32
+
+  @[JSON::Field(key: "RANK_TITLE")]
+  property rank_title : String
+
+  @[JSON::Field(key: "SEX")]
+  property sex : String
+end
+
+class FluffyTopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "ADVENTURERCOUNT")]
+  property adventurercount : Int32
+
+  @[JSON::Field(key: "AGENTCOUNT")]
+  property agentcount : Int32
+
+  @[JSON::Field(key: "ATROXCOUNT")]
+  property atroxcount : Int32
+
+  @[JSON::Field(key: "AVGLVL")]
+  property avglvl : Float64
+
+  @[JSON::Field(key: "BTCOUNT")]
+  property btcount : Int32
+
+  @[JSON::Field(key: "DESCRIPTION")]
+  property description : String
+
+  @[JSON::Field(key: "DOCTORCOUNT")]
+  property doctorcount : Int32
+
+  @[JSON::Field(key: "ENFCOUNT")]
+  property enfcount : Int32
+
+  @[JSON::Field(key: "ENGINEEERCOUNT")]
+  property engineeercount : Int32
+
+  @[JSON::Field(key: "FEMALECOUNT")]
+  property femalecount : Int32
+
+  @[JSON::Field(key: "FIXERCOUNT")]
+  property fixercount : Int32
+
+  @[JSON::Field(key: "GOVERNINGNAME")]
+  property governingname : String
+
+  @[JSON::Field(key: "HISTORY")]
+  property history : String
+
+  @[JSON::Field(key: "KEEPERCOUNT")]
+  property keepercount : Int32
+
+  @[JSON::Field(key: "MACOUNT")]
+  property macount : Int32
+
+  @[JSON::Field(key: "MALECOUNT")]
+  property malecount : Int32
+
+  @[JSON::Field(key: "MAXLVL")]
+  property maxlvl : Int32
+
+  @[JSON::Field(key: "METACOUNT")]
+  property metacount : Int32
+
+  @[JSON::Field(key: "MINLVL")]
+  property minlvl : Int32
+
+  @[JSON::Field(key: "MONSTERCOUNT")]
+  property monstercount : Int32
+
+  @[JSON::Field(key: "NAME")]
+  property name : String
+
+  @[JSON::Field(key: "NANOCOUNT")]
+  property nanocount : Int32
+
+  @[JSON::Field(key: "NANORACECOUNT")]
+  property nanoracecount : Int32
+
+  @[JSON::Field(key: "NEUTERCOUNT")]
+  property neutercount : Int32
+
+  @[JSON::Field(key: "NUMMEMBERS")]
+  property nummembers : Int32
+
+  @[JSON::Field(key: "OBJECTIVE")]
+  property objective : String
+
+  @[JSON::Field(key: "OPIFEXCOUNT")]
+  property opifexcount : Int32
+
+  @[JSON::Field(key: "ORG_DIMENSION")]
+  property org_dimension : Int32
+
+  @[JSON::Field(key: "ORG_INSTANCE")]
+  property org_instance : Int32
+
+  @[JSON::Field(key: "SHADECOUNT")]
+  property shadecount : Int32
+
+  @[JSON::Field(key: "SIDE")]
+  property side : Int32
+
+  @[JSON::Field(key: "SIDE_NAME")]
+  property side_name : String
+
+  @[JSON::Field(key: "SOLIDERCOUNT")]
+  property solidercount : Int32
+
+  @[JSON::Field(key: "SOLITUSCOUNT")]
+  property solituscount : Int32
+
+  @[JSON::Field(key: "TRADERCOUNT")]
+  property tradercount : Int32
+end
+
+alias TopLevelUnion = FluffyTopLevel | String | Array(PurpleTopLevel)
diff --git a/head/crystal/test/inputs/json/priority/coin-pairs.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/coin-pairs.json/default/TopLevel.cr
new file mode 100644
index 0000000..ad1c052
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/coin-pairs.json/default/TopLevel.cr
@@ -0,0 +1,29 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property pairs : Hash(String, Pair)
+
+  property server_time : Int32
+end
+
+class Pair
+  include JSON::Serializable
+
+  property decimal_places : Int32
+
+  property fee : Float64
+
+  property hidden : Int32
+
+  property max_amount : Int32
+
+  property max_price : Int32
+
+  property min_amount : Float64
+
+  property min_price : Float64
+
+  property min_total : Float64
+end
diff --git a/head/crystal/test/inputs/json/priority/combinations1.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/combinations1.json/default/TopLevel.cr
new file mode 100644
index 0000000..a49224e
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/combinations1.json/default/TopLevel.cr
@@ -0,0 +1,731 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property centrodesmose : String
+
+  property cerograph : Array(CerographElement?)
+
+  property chemotherapeutics : Array(ChemotherapeuticElement)
+
+  property cimelia : Array(CimeliaElement?)
+
+  property citrated : Int32
+
+  property clinodome : Array(Clinodome)
+
+  property coadjust : Array(CoadjustElement)
+
+  property consilience : Array(Consilience)
+
+  property constructor : Array(Constructor)
+
+  property continuative : Array(Continuative)
+
+  property credulity : Array(CredulityElement)
+
+  property creviced : Array(Creviced)
+
+  property cubiculum : Array(Array(Int32?))
+
+  property deruralize : Array(DeruralizeElement)
+
+  property diaereses : Array(DiaereseElement)
+
+  property dissolution : Array(Array(Nil)?)
+
+  property downstroke : Array(Downstroke)
+
+  property electrotautomerism : Array(Float64?)
+
+  property eleutheromania : Array(Eleutheromania)
+
+  property encrust : Encrust
+
+  property entomoid : Array(Entomoid)
+
+  property epipaleolithic : Array(Epipaleolithic)
+
+  property expropriable : Array(Expropriable)
+
+  property faggingly : Array(FagginglyElement)
+
+  property fenks : Array(FenkElement)
+
+  property flagmaking : Array(FlagmakingElement)
+
+  property fluorometer : Array(Fluorometer?)
+
+  property fulsome : Array(Int32?)
+
+  property fuzzy : Array(Fuzzy)
+
+  property gardenwards : Array(Gardenward)
+
+  property generalissimo : Array(Generalissimo?)
+
+  property habeas : Array(Hash(String, Int32)?)
+
+  property hemicrystalline : Array(Hemicrystalline)
+
+  property hemocoele : Array(HemocoeleElement)
+
+  property hoister : Array(Hoister?)
+
+  property hyperpiesis : Array(Hyperpiesi?)
+
+  property hyppish : Array(Hyppish?)
+
+  property idealizer : Array(Idealizer)
+
+  property incrustator : Array(Incrustator)
+
+  property intentiveness : Array(Intentiveness)
+
+  property interacinar : Interacinar
+
+  property intercorrelation : Array(Array(Int32)?)
+
+  property jacutinga : Array(Jacutinga)
+end
+
+class CerographClass
+  include JSON::Serializable
+
+  property apotropaion : Nil
+
+  property casuary : Nil
+
+  property creaker : Nil
+
+  property disqualification : Nil
+
+  property imperatorious : Nil
+
+  property impermeabilize : Nil
+
+  property metastoma : Nil
+
+  property noctidiurnal : Nil
+
+  property nonreserve : Nil
+
+  property ophthalmotonometry : Nil
+
+  property pailful : Nil
+
+  property pigfish : Nil
+
+  property pongee : Nil
+
+  property prosodical : Nil
+
+  property scrofuloderm : Nil
+
+  property storekeeping : Nil
+
+  property therologist : Nil
+
+  @[JSON::Field(key: "Tolowa")]
+  property tolowa : Nil
+
+  property tradeful : Nil
+
+  property unriveting : Nil
+end
+
+class ChemotherapeuticClass
+  include JSON::Serializable
+
+  property angioneurotic : Nil
+
+  property availment : Nil
+
+  property bladelet : Nil
+
+  property catharticalness : Float64?
+
+  property caulis : Nil
+
+  property chalcus : Nil
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property disdiapason : String?
+
+  property enteradenological : Nil
+
+  property homocerc : Bool?
+
+  property imporosity : Nil
+
+  property insistently : Nil
+
+  property intraparietal : Nil
+
+  property ivied : Nil
+
+  @[JSON::Field(key: "Maureen")]
+  property maureen : Nil
+
+  property nonbookish : Nil
+
+  property nostochine : Nil
+
+  property nutcracker : Nil
+
+  property ofttimes : Nil
+
+  property phenocryst : Nil
+
+  property precoincident : Nil
+
+  property ramiferous : Nil
+
+  property stagmometer : Nil
+
+  property tetherball : Nil
+
+  property unshy : Nil
+end
+
+class CimeliaClass
+  include JSON::Serializable
+
+  property catharticalness : Float64
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32
+
+  property disdiapason : String
+
+  property homocerc : Bool
+
+  property nonbookish : Nil
+end
+
+class CoadjustClass
+  include JSON::Serializable
+
+  property amidosulphonal : Nil
+
+  @[JSON::Field(key: "Benny")]
+  property benny : Nil
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property disdiapason : String?
+
+  property ensnare : Nil
+
+  property homocerc : Bool?
+
+  property hybridizer : Nil
+
+  property leastwise : Nil
+
+  property lof : Nil
+
+  property monkhood : Nil
+
+  @[JSON::Field(key: "Netherlandish")]
+  property netherlandish : Nil
+
+  property nonbookish : Nil
+
+  property peonism : Nil
+
+  @[JSON::Field(key: "Phonelescope")]
+  property phonelescope : Nil
+
+  property porphyrogeniture : Nil
+
+  property preindemnify : Nil
+
+  property rosal : Nil
+
+  property scalenous : Nil
+
+  property scopine : Nil
+
+  @[JSON::Field(key: "Sedaceae")]
+  property sedaceae : Nil
+
+  property suberinize : Nil
+
+  property symbiot : Nil
+
+  property tablefellow : Nil
+
+  property unchargeable : Nil
+end
+
+class CredulityClass
+  include JSON::Serializable
+
+  property ammonolytic : Nil
+
+  property bushmaster : Nil
+
+  property considering : Nil
+
+  property consuetudinary : Nil
+
+  property embarras : Nil
+
+  property fineness : Nil
+
+  property flaithship : Nil
+
+  @[JSON::Field(key: "Flavia")]
+  property flavia : Nil
+
+  property gruffly : Nil
+
+  @[JSON::Field(key: "Hedychium")]
+  property hedychium : Nil
+
+  property leadwort : Nil
+
+  property overseriously : Nil
+
+  property parabola : Nil
+
+  property pectinatodenticulate : Nil
+
+  @[JSON::Field(key: "Popean")]
+  property popean : Nil
+
+  property pornocrat : Nil
+
+  property quadrisect : Nil
+
+  property seriality : Nil
+
+  property vamphorn : Nil
+
+  property wharp : Nil
+end
+
+class DeruralizeClass
+  include JSON::Serializable
+
+  property bockerel : Nil
+
+  property boulder : Nil
+
+  property churrus : Nil
+
+  property counterdigged : Nil
+
+  property dialogite : Nil
+
+  property digenic : Nil
+
+  property dunbird : Nil
+
+  property ergatogyne : Nil
+
+  property fiendful : Nil
+
+  property jackrod : Nil
+
+  @[JSON::Field(key: "Jehovistic")]
+  property jehovistic : Nil
+
+  @[JSON::Field(key: "Paninean")]
+  property paninean : Nil
+
+  property panther : Nil
+
+  property placentigerous : Nil
+
+  @[JSON::Field(key: "Romney")]
+  property romney : Nil
+
+  property sparm : Nil
+
+  property tocsin : Nil
+
+  property unnicked : Nil
+
+  property unstavable : Nil
+
+  property windfirm : Nil
+end
+
+class DiaereseClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Amoreuxia")]
+  property amoreuxia : Nil
+
+  property ani : Nil
+
+  property bernicle : Nil
+
+  property blackwasher : Nil
+
+  property blowhard : Nil
+
+  property broma : Nil
+
+  property closecross : Nil
+
+  property congregationalism : Nil
+
+  property grayly : Nil
+
+  property historically : Nil
+
+  property hoast : Nil
+
+  property irretentive : Nil
+
+  property parcener : Nil
+
+  property pedder : Nil
+
+  property pseudoanatomic : Nil
+
+  property rhizocarpian : Nil
+
+  property samel : Nil
+
+  property silker : Nil
+
+  property subdentated : Nil
+
+  property subobscure : Nil
+end
+
+class Encrust
+  include JSON::Serializable
+
+  property comradely : Nil
+
+  property diacanthous : Nil
+
+  property feminineness : Nil
+
+  property gossamered : Nil
+
+  @[JSON::Field(key: "Hibernia")]
+  property hibernia : Nil
+
+  @[JSON::Field(key: "Hibiscus")]
+  property hibiscus : Nil
+
+  @[JSON::Field(key: "Lepidosauria")]
+  property lepidosauria : Nil
+
+  property lollingly : Nil
+
+  property manager : Nil
+
+  property mechanic : Nil
+
+  property overminuteness : Nil
+
+  property papelonne : Nil
+
+  property plebification : Nil
+
+  property pugmiller : Nil
+
+  property recoveror : Nil
+
+  property spermatoblastic : Nil
+
+  @[JSON::Field(key: "Syllidae")]
+  property syllidae : Nil
+
+  property ungyved : Nil
+
+  property whirlabout : Nil
+
+  property woodenware : Nil
+end
+
+class FagginglyClass
+  include JSON::Serializable
+
+  property abranchian : Nil
+
+  property aculeiform : Nil
+
+  property adiaphoristic : Nil
+
+  property adoptionism : Nil
+
+  @[JSON::Field(key: "Anglic")]
+  property anglic : Nil
+
+  property antrotomy : Nil
+
+  property coerciveness : Nil
+
+  property decorist : Nil
+
+  property duckhood : Nil
+
+  @[JSON::Field(key: "Heteromeri")]
+  property heteromeri : Nil
+
+  property hypochnose : Nil
+
+  property lochage : Nil
+
+  property melee : Nil
+
+  property nonconformitant : Nil
+
+  @[JSON::Field(key: "Poinsettia")]
+  property poinsettia : Nil
+
+  property putatively : Nil
+
+  property semivolatile : Nil
+
+  property soleas : Nil
+
+  property unfastenable : Nil
+
+  property unmillinered : Nil
+end
+
+class FenkClass
+  include JSON::Serializable
+
+  property apoise : Nil
+
+  property astronomize : Nil
+
+  property cockhorse : Nil
+
+  property copular : Nil
+
+  @[JSON::Field(key: "Dagomba")]
+  property dagomba : Nil
+
+  property draffy : Nil
+
+  property foreigner : Nil
+
+  @[JSON::Field(key: "Guyandot")]
+  property guyandot : Nil
+
+  property neurogliosis : Nil
+
+  property osmious : Nil
+
+  property palpitate : Nil
+
+  property rebukeable : Nil
+
+  @[JSON::Field(key: "Reinwardtia")]
+  property reinwardtia : Nil
+
+  property reservatory : Nil
+
+  property scalt : Nil
+
+  property scripturalize : Nil
+
+  property tintometer : Nil
+
+  @[JSON::Field(key: "Tritoness")]
+  property tritoness : Nil
+
+  property undergrade : Nil
+
+  property undermountain : Nil
+end
+
+class FlagmakingClass
+  include JSON::Serializable
+
+  property albarco : Nil
+
+  @[JSON::Field(key: "Bunodonta")]
+  property bunodonta : Nil
+
+  property hornify : Nil
+
+  @[JSON::Field(key: "Hydrocorisae")]
+  property hydrocorisae : Nil
+
+  property hypoglossus : Nil
+
+  property inexpiably : Nil
+
+  property ingratitude : Nil
+
+  property ladyfly : Nil
+
+  property medicament : Nil
+
+  property monogrammatic : Nil
+
+  property nobbut : Nil
+
+  @[JSON::Field(key: "Notacanthidae")]
+  property notacanthidae : Nil
+
+  property polyplacophore : Nil
+
+  property proexercise : Nil
+
+  property protoplast : Nil
+
+  property puzzling : Nil
+
+  property splanchnoskeleton : Nil
+
+  property unloveliness : Nil
+
+  property unquarantined : Nil
+
+  property unrenounceable : Nil
+end
+
+class HemocoeleClass
+  include JSON::Serializable
+
+  property acrogamy : Nil
+
+  property amelification : Nil
+
+  property autobiographic : Nil
+
+  property berat : Nil
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property disdiapason : String?
+
+  property disproportionably : Nil
+
+  property erythrite : Nil
+
+  property graphic : Nil
+
+  property hepatological : Nil
+
+  property homocerc : Bool?
+
+  property incommensurably : Nil
+
+  property misaffirm : Nil
+
+  property nonbookish : Nil
+
+  property pocketbook : Nil
+
+  property sclerometric : Nil
+
+  property stambouline : Nil
+
+  property stickpin : Nil
+
+  property tubulure : Nil
+
+  property undelated : Nil
+
+  property unsalt : Nil
+
+  property untutelar : Nil
+
+  property vagrant : Nil
+
+  @[JSON::Field(key: "Walt")]
+  property walt : Nil
+end
+
+class Interacinar
+  include JSON::Serializable
+
+  property assapan : Float64
+
+  property benefactorship : Bool
+
+  property triseriatim : String
+
+  property tubbing : Int32
+
+  property untrimmed : Nil
+end
+
+alias CerographElement = CerographClass | String
+
+alias ChemotherapeuticElement = ChemotherapeuticClass | Int32
+
+alias CimeliaElement = CimeliaClass | Array(Int32)
+
+alias Clinodome = Float64 | String
+
+alias CoadjustElement = CoadjustClass | Float64
+
+alias Consilience = Float64 | Hash(String, Int32)
+
+alias Constructor = Bool | Hash(String, Int32?)
+
+alias Continuative = Hash(String, Int32) | String
+
+alias CredulityElement = CredulityClass | Int32 | String
+
+alias Creviced = Hash(String, Int32) | Bool | String
+
+alias DeruralizeElement = DeruralizeClass | Array(Nil) | Bool
+
+alias DiaereseElement = DiaereseClass | Array(Int32) | Bool
+
+alias Downstroke = Array(Nil) | Bool | String
+
+alias Eleutheromania = Float64 | Hash(String, Int32) | String
+
+alias Entomoid = CimeliaClass | Int32
+
+alias Epipaleolithic = Float64 | Array(Int32)
+
+alias Expropriable = CimeliaClass | Float64 | Array(Nil)
+
+alias FagginglyElement = Float64 | FagginglyClass
+
+alias FenkElement = FenkClass | String
+
+alias FlagmakingElement = Float64 | FlagmakingClass | Bool
+
+alias Fluorometer = Int32 | String
+
+alias Fuzzy = Int32 | Hash(String, Int32?)
+
+alias Gardenward = Array(Int32) | Bool | String
+
+alias Generalissimo = Hash(String, Int32) | Bool
+
+alias Hemicrystalline = CimeliaClass | String
+
+alias HemocoeleElement = HemocoeleClass | Array(Int32)
+
+alias Hoister = CimeliaClass | String
+
+alias Hyperpiesi = CimeliaClass | Array(Nil)
+
+alias Hyppish = Bool | String
+
+alias Idealizer = CimeliaClass | Int32 | Array(Nil)
+
+alias Incrustator = Int32 | Array(Int32) | String
+
+alias Intentiveness = CimeliaClass | Float64 | String
+
+alias Jacutinga = Array(Int32) | Hash(String, Int32?)
diff --git a/head/crystal/test/inputs/json/priority/combinations2.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/combinations2.json/default/TopLevel.cr
new file mode 100644
index 0000000..a5b6587
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/combinations2.json/default/TopLevel.cr
@@ -0,0 +1,650 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Abranchiata")]
+  property abranchiata : Array(Abranchiata?)
+
+  property academe : Array(Academe)
+
+  property acquirable : Array(Acquirable)
+
+  property aerometry : Array(Aerometry)
+
+  property alexin : Array(Alexin)
+
+  property alleviate : Array(AlleviateElement)
+
+  property amaas : Array(Amaa)
+
+  property ambassage : Array(Ambassage)
+
+  property amphithyron : Array(Amphithyron?)
+
+  @[JSON::Field(key: "Andriana")]
+  property andriana : Array(String?)
+
+  property ankee : Array(AnkeeElement)
+
+  property annihilator : Array(Hash(String, Int32?)?)
+
+  property annulose : Nil
+
+  @[JSON::Field(key: "Ansarie")]
+  property ansarie : Array(AnsarieElement?)
+
+  property aphasia : Array(Aphasia)
+
+  property asprawl : Array(Asprawl)
+
+  property attractive : Array(Bool?)
+
+  property barksome : Hash(String, Int32)
+
+  property bedesman : Array(Bedesman)
+
+  property belard : Array(Belard)
+
+  property bocking : Array(Bocking)
+
+  property brawlingly : Array(Brawlingly)
+
+  property brookie : Array(Brookie)
+
+  property bumboatman : Array(Bumboatman?)
+
+  property bystreet : Array(Nil)
+
+  property calaverite : Array(Calaverite)
+
+  property catallactic : Array(Catallactic)
+
+  property cemental : Array(Cemental)
+
+  @[JSON::Field(key: "Chytridiaceae")]
+  property chytridiaceae : Array(ChytridiaceaeElement?)
+
+  @[JSON::Field(key: "Discordia")]
+  property discordia : Array(DiscordiaElement)
+
+  @[JSON::Field(key: "Endomyces")]
+  property endomyces : Array(Endomyce)
+
+  @[JSON::Field(key: "Epinephelidae")]
+  property epinephelidae : Array(Epinephelidae)
+
+  @[JSON::Field(key: "Eupatorium")]
+  property eupatorium : Array(Eupatorium)
+
+  @[JSON::Field(key: "Gryphosaurus")]
+  property gryphosaurus : Array(GryphosaurusElement)
+
+  @[JSON::Field(key: "Koryak")]
+  property koryak : Array(Koryak)
+
+  @[JSON::Field(key: "Lavinia")]
+  property lavinia : Array(LaviniaElement)
+
+  @[JSON::Field(key: "Oskar")]
+  property oskar : Array(OskarElement)
+
+  @[JSON::Field(key: "Rebecca")]
+  property rebecca : Array(RebeccaElement)
+
+  @[JSON::Field(key: "Rhomboganoidei")]
+  property rhomboganoidei : Array(Rhomboganoidei)
+
+  @[JSON::Field(key: "Rigsmal")]
+  property rigsmal : Bool
+
+  @[JSON::Field(key: "Ruellia")]
+  property ruellia : Array(Ruellia)
+
+  @[JSON::Field(key: "School")]
+  property school : Array(School?)
+
+  @[JSON::Field(key: "Shakespearolater")]
+  property shakespearolater : Array(Shakespearolater)
+
+  @[JSON::Field(key: "Svan")]
+  property svan : Array(Float64)
+
+  @[JSON::Field(key: "Wayao")]
+  property wayao : Hash(String, Float64)
+end
+
+class AlleviateClass
+  include JSON::Serializable
+
+  property apriori : Nil
+
+  property beggarer : Nil
+
+  property brokenheartedly : Nil
+
+  property debilitation : Nil
+
+  property frike : Nil
+
+  property gastrolith : Nil
+
+  @[JSON::Field(key: "Hulsean")]
+  property hulsean : Nil
+
+  property orthocentric : Nil
+
+  property petaly : Nil
+
+  property probudgeting : Nil
+
+  property reacquire : Nil
+
+  property scow : Nil
+
+  property shutoff : Nil
+
+  property subcontiguous : Nil
+
+  property suffumigate : Nil
+
+  property transformable : Nil
+
+  property uncoroneted : Nil
+
+  property unparking : Nil
+
+  property unvarnishedness : Nil
+
+  property wherewithal : Nil
+end
+
+class Rebecca
+  include JSON::Serializable
+
+  property catharticalness : Float64
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32
+
+  property disdiapason : String
+
+  property homocerc : Bool
+
+  property nonbookish : Nil
+end
+
+class Amphithyron
+  include JSON::Serializable
+
+  property akroasis : Int32?
+
+  property antiphonical : Int32?
+
+  property basebred : Int32?
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property conductometric : Int32?
+
+  property disdiapason : String?
+
+  property ensilation : Int32?
+
+  property eyebolt : Int32?
+
+  property fistulated : Int32?
+
+  property heteropod : Int32?
+
+  property homocerc : Bool?
+
+  @[JSON::Field(key: "Juniperus")]
+  property juniperus : Int32?
+
+  property labyrinthically : Int32?
+
+  property martyrization : Int32?
+
+  property mispolicy : Int32?
+
+  property multipara : Int32?
+
+  @[JSON::Field(key: "Nazirite")]
+  property nazirite : Int32?
+
+  property nonbookish : Nil
+
+  property possessorial : Int32?
+
+  property shamed : Int32?
+
+  property shelfworn : Int32?
+
+  property stagnum : Int32?
+
+  @[JSON::Field(key: "Those")]
+  property those : Int32?
+
+  property undecimal : Int32?
+end
+
+class AnkeeClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Anomoean")]
+  property anomoean : Nil
+
+  property barleyhood : Nil
+
+  property befriender : Nil
+
+  property brutishness : Nil
+
+  property cephalalgy : Nil
+
+  property cirurgian : Nil
+
+  property conventionally : Nil
+
+  property jackshay : Nil
+
+  property milammeter : Nil
+
+  @[JSON::Field(key: "Naja")]
+  property naja : Nil
+
+  property ombrological : Nil
+
+  property phonasthenia : Nil
+
+  property retrievableness : Nil
+
+  property snakily : Nil
+
+  property swot : Nil
+
+  property tartlet : Nil
+
+  property thiofuran : Nil
+
+  property tracheophone : Nil
+
+  property tuglike : Nil
+
+  property unscratchingly : Nil
+end
+
+class AnsarieClass
+  include JSON::Serializable
+
+  property accension : Nil
+
+  @[JSON::Field(key: "Alida")]
+  property alida : Nil
+
+  property asteria : Nil
+
+  property beriberic : Nil
+
+  property edgebone : Nil
+
+  property gastrodialysis : Nil
+
+  property geographic : Nil
+
+  @[JSON::Field(key: "Ictonyx")]
+  property ictonyx : Nil
+
+  property metrocele : Nil
+
+  property misgraft : Nil
+
+  property monteith : Nil
+
+  property notcher : Nil
+
+  property prorestriction : Nil
+
+  @[JSON::Field(key: "Ramist")]
+  property ramist : Nil
+
+  property throatlet : Nil
+
+  property unfair : Nil
+
+  property unsynonymous : Nil
+
+  property water : Nil
+
+  property zestfully : Nil
+
+  property zincic : Nil
+end
+
+class ChytridiaceaeClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Batidaceae")]
+  property batidaceae : Nil
+
+  @[JSON::Field(key: "Brechites")]
+  property brechites : Nil
+
+  property codespairer : Nil
+
+  @[JSON::Field(key: "Emery")]
+  property emery : Nil
+
+  property enervative : Nil
+
+  property excriminate : Nil
+
+  property goshenite : Nil
+
+  property grime : Nil
+
+  property gritten : Nil
+
+  property hectorly : Nil
+
+  property intermediation : Nil
+
+  property meeterly : Nil
+
+  @[JSON::Field(key: "Narraganset")]
+  property narraganset : Nil
+
+  property onymatic : Nil
+
+  property paddlecock : Nil
+
+  property thana : Nil
+
+  property thornily : Nil
+
+  property uckia : Nil
+
+  property unmettle : Nil
+
+  property vorticellid : Nil
+end
+
+class DiscordiaClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Altaic")]
+  property altaic : Int32?
+
+  property amoristic : Int32?
+
+  property blennophthalmia : Int32?
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property disciplinability : Int32?
+
+  property disdiapason : String?
+
+  property goofer : Int32?
+
+  property homocerc : Bool?
+
+  property laryngograph : Int32?
+
+  property leucitis : Int32?
+
+  property lymphocyst : Int32?
+
+  property microcosmology : Int32?
+
+  property nauseation : Int32?
+
+  property nonbookish : Nil
+
+  @[JSON::Field(key: "Patarin")]
+  property patarin : Int32?
+
+  property preliberal : Int32?
+
+  property prettifier : Int32?
+
+  property rangework : Int32?
+
+  property redient : Int32?
+
+  property subfusiform : Int32?
+
+  property suicidical : Int32?
+
+  property swow : Int32?
+
+  property wastrel : Int32?
+
+  property wingle : Int32?
+end
+
+class GryphosaurusClass
+  include JSON::Serializable
+
+  property amissibility : Nil
+
+  @[JSON::Field(key: "Burushaski")]
+  property burushaski : Nil
+
+  property citronin : Nil
+
+  property coplaintiff : Nil
+
+  property disquisitionary : Nil
+
+  property enoplan : Nil
+
+  property faintness : Nil
+
+  property hebetomy : Nil
+
+  property islandry : Nil
+
+  property lameduck : Nil
+
+  property overbattle : Nil
+
+  property overinterested : Nil
+
+  property phrenologic : Nil
+
+  property rainband : Nil
+
+  property shiningly : Nil
+
+  property stamineous : Nil
+
+  property subscapularis : Nil
+
+  @[JSON::Field(key: "Tahami")]
+  property tahami : Nil
+
+  property undaubed : Nil
+
+  property underntime : Nil
+end
+
+class LaviniaClass
+  include JSON::Serializable
+
+  property agitable : Int32?
+
+  property asininity : Int32?
+
+  property benefiter : Int32?
+
+  property bronzelike : Int32?
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property cholesteatomatous : Int32?
+
+  property deprivement : Int32?
+
+  property disdiapason : String?
+
+  property flippantness : Int32?
+
+  property fogproof : Int32?
+
+  property homocerc : Bool?
+
+  property merrymeeting : Int32?
+
+  property nonbookish : Nil
+
+  property overcareful : Int32?
+
+  property panaris : Int32?
+
+  property preacceptance : Int32?
+
+  property quinoxaline : Int32?
+
+  property sig : Int32?
+
+  property superconfusion : Int32?
+
+  @[JSON::Field(key: "Tacana")]
+  property tacana : Int32?
+
+  property tillotter : Int32?
+
+  property tranquillize : Int32?
+
+  property unquestionable : Int32?
+
+  property uproute : Int32?
+end
+
+class OskarClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Acrobates")]
+  property acrobates : Nil
+
+  property beanshooter : Nil
+
+  property bearhound : Nil
+
+  @[JSON::Field(key: "Cayuga")]
+  property cayuga : Nil
+
+  property guarneri : Nil
+
+  property hypochondriacism : Nil
+
+  property indication : Nil
+
+  property jaculative : Nil
+
+  property nagana : Nil
+
+  @[JSON::Field(key: "Netherlandish")]
+  property netherlandish : Nil
+
+  property noctivagous : Nil
+
+  property nonphysiological : Nil
+
+  property praxis : Nil
+
+  property provision : Nil
+
+  property subterhuman : Nil
+
+  property sunlit : Nil
+
+  property syncraniate : Nil
+
+  property teachment : Nil
+
+  property unmutinous : Nil
+
+  property unstoppable : Nil
+end
+
+alias Abranchiata = Int32 | Array(Int32)
+
+alias Academe = Int32 | Array(Int32) | Hash(String, Int32)
+
+alias Acquirable = Hash(String, Int32) | Array(Int32?)
+
+alias Aerometry = Float64 | Bool
+
+alias Alexin = Array(Int32) | Bool
+
+alias AlleviateElement = AlleviateClass | Array(Int32?)
+
+alias Amaa = Int32 | Bool | Rebecca
+
+alias Ambassage = Array(Nil) | String
+
+alias AnkeeElement = AnkeeClass | Int32 | Array(Int32)
+
+alias AnsarieElement = AnsarieClass | Array(Int32)
+
+alias Aphasia = Int32 | Array(Int32)
+
+alias Asprawl = Float64 | String
+
+alias Bedesman = Float64 | Bool | String
+
+alias Belard = Float64 | Array(Int32) | Rebecca
+
+alias Bocking = Array(Int32) | Hash(String, Int32) | Bool
+
+alias Brawlingly = Array(Nil) | Hash(String, Int32?)
+
+alias Brookie = Array(Int32) | Rebecca
+
+alias Bumboatman = Array(Nil) | String
+
+alias Calaverite = Array(Int32) | String
+
+alias Catallactic = Hash(String, Int32) | Array(Nil) | Bool
+
+alias Cemental = Float64 | Array(Int32) | Hash(String, Int32)
+
+alias ChytridiaceaeElement = ChytridiaceaeClass | Bool
+
+alias DiscordiaElement = DiscordiaClass | Array(Int32)
+
+alias Endomyce = Int32 | String
+
+alias Epinephelidae = Int32 | Bool | String
+
+alias Eupatorium = Hash(String, Int32) | Array(Nil)
+
+alias GryphosaurusElement = GryphosaurusClass | Array(Int32) | String
+
+alias Koryak = String | Hash(String, Int32?)
+
+alias LaviniaElement = LaviniaClass | String
+
+alias OskarElement = Array(Int32) | OskarClass
+
+alias RebeccaElement = Int32 | String | Rebecca
+
+alias Rhomboganoidei = Array(Int32) | String | Rebecca
+
+alias Ruellia = Bool | String | Rebecca
+
+alias School = Int32 | Hash(String, Int32)
+
+alias Shakespearolater = Float64 | Array(Int32) | String
diff --git a/head/crystal/test/inputs/json/priority/combinations3.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/combinations3.json/default/TopLevel.cr
new file mode 100644
index 0000000..616267f
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/combinations3.json/default/TopLevel.cr
@@ -0,0 +1,836 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property juror : Array(JurorElement)
+
+  property kongoni : Array(Kongoni)
+
+  property ladronism : Array(LadronismElement)
+
+  property landlubberly : Array(LandlubberlyElement)
+
+  property listener : Array(Listener)
+
+  property lupus : Array(LupusElement)
+
+  property maslin : Array(Maslin)
+
+  property monazite : Array(MonaziteElement)
+
+  property monoliteral : Array(Monoliteral)
+
+  property monotheistically : Array(MonotheisticallyElement)
+
+  property montage : Array(Montage)
+
+  property moralness : Array(Moralness?)
+
+  property mowra : Array(MonaziteClass?)
+
+  property mulishly : Array(Mulishly?)
+
+  property myoscope : Array(Myoscope)
+
+  property nach : Array(Array(Int32?)?)
+
+  property neuromastic : Array(Neuromastic)
+
+  property noncontributing : Array(Noncontributing)
+
+  property nonnervous : Array(Nonnervous)
+
+  property nonvaluation : Array(Nonvaluation)
+
+  property occupationalist : Array(OccupationalistElement?)
+
+  property outrival : Array(OutrivalElement?)
+
+  property paleographically : Array(Paleographically)
+
+  property pamphletwise : Array(Pamphletwise)
+
+  property pediatrics : Array(Pediatric?)
+
+  property perceptive : Array(Bool)
+
+  property piaculum : Array(PiaculumElement)
+
+  property piccadilly : Array(Piccadilly?)
+
+  property piffler : Array(Piffler)
+
+  property pithful : Array(Pithful?)
+
+  property placuntitis : Array(Placuntiti)
+
+  property plectopterous : Array(Plectopterous)
+
+  property pneumocele : Array(Pneumocele?)
+
+  property poliorcetic : Array(Poliorcetic)
+
+  property poormaster : Array(Poormaster?)
+
+  property potwhisky : Array(PotwhiskyElement?)
+
+  property practicalizer : Array(Practicalizer)
+
+  property prefreshman : Array(PrefreshmanElement)
+
+  property prehensility : Array(Prehensility)
+
+  property prevoidance : Array(Prevoidance)
+
+  property probant : Array(Hash(String, Int32?))
+
+  property protext : Array(Protext)
+end
+
+class JurorClass
+  include JSON::Serializable
+
+  property adipsy : Nil
+
+  property auxiliator : Nil
+
+  property benda : Nil
+
+  property benjamin : Nil
+
+  property brandling : Nil
+
+  property epicurishly : Nil
+
+  property eremochaetous : Nil
+
+  property marten : Nil
+
+  property monocline : Nil
+
+  @[JSON::Field(key: "Olea")]
+  property olea : Nil
+
+  property palgat : Nil
+
+  property pennyworth : Nil
+
+  property pioury : Nil
+
+  property pragmatistic : Nil
+
+  property stylelessness : Nil
+
+  property systematical : Nil
+
+  property thready : Nil
+
+  property uncontemporary : Nil
+
+  property uncouched : Nil
+
+  property uninhabitedness : Nil
+end
+
+class LadronismClass
+  include JSON::Serializable
+
+  property acclaimer : Nil
+
+  property achree : Nil
+
+  property base : Nil
+
+  property conundrumize : Nil
+
+  property degerminator : Nil
+
+  property describable : Nil
+
+  property exasperatedly : Nil
+
+  property heroine : Nil
+
+  property indazin : Nil
+
+  property luteous : Nil
+
+  property papular : Nil
+
+  property pritch : Nil
+
+  @[JSON::Field(key: "Prodenia")]
+  property prodenia : Nil
+
+  property seege : Nil
+
+  property shopgirl : Nil
+
+  property tragedietta : Nil
+
+  property unsparse : Nil
+
+  property uplook : Nil
+
+  property vermiformis : Nil
+
+  property whafabout : Nil
+end
+
+class LandlubberlyClass
+  include JSON::Serializable
+
+  property acropoleis : Nil
+
+  property aminate : Nil
+
+  @[JSON::Field(key: "Amyraldism")]
+  property amyraldism : Nil
+
+  property bipenniform : Nil
+
+  property bugre : Nil
+
+  property calycule : Nil
+
+  property caoutchouc : Nil
+
+  property disprover : Nil
+
+  property fitroot : Nil
+
+  property fulgently : Nil
+
+  property kickup : Nil
+
+  property laevoversion : Nil
+
+  property moter : Nil
+
+  property objectivity : Nil
+
+  property posterity : Nil
+
+  property postnuptial : Nil
+
+  property precedentary : Nil
+
+  property saddling : Nil
+
+  property subcurrent : Nil
+
+  property unrecriminative : Nil
+end
+
+class LupusClass
+  include JSON::Serializable
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  @[JSON::Field(key: "Chlorioninae")]
+  property chlorioninae : Int32?
+
+  @[JSON::Field(key: "Corvinae")]
+  property corvinae : Int32?
+
+  @[JSON::Field(key: "Crassina")]
+  property crassina : Int32?
+
+  property disdiapason : String?
+
+  property exiguity : Int32?
+
+  property farcist : Int32?
+
+  property holographical : Int32?
+
+  property homocerc : Bool?
+
+  property ichthyophagan : Int32?
+
+  property implacable : Int32?
+
+  property nonbookish : Nil
+
+  property outshiner : Int32?
+
+  property overweather : Int32?
+
+  property protonegroid : Int32?
+
+  property shallowish : Int32?
+
+  property snoke : Int32?
+
+  property snout : Int32?
+
+  property surveillance : Int32?
+
+  property threshingtime : Int32?
+
+  @[JSON::Field(key: "Thysanocarpus")]
+  property thysanocarpus : Int32?
+
+  property unsignificantly : Int32?
+
+  property unsnap : Int32?
+
+  property vendible : Int32?
+end
+
+class Maslin
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Alicant")]
+  property alicant : Int32?
+
+  property antiatonement : Nil
+
+  property anticorrosive : Int32?
+
+  property aphidozer : Nil
+
+  @[JSON::Field(key: "Bakuninist")]
+  property bakuninist : Nil
+
+  property be : Int32?
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property chub : Int32?
+
+  property cuprosilicon : Int32?
+
+  property curtailedly : Int32?
+
+  property dellenite : Int32?
+
+  @[JSON::Field(key: "Dimitry")]
+  property dimitry : Int32?
+
+  property disdiapason : String?
+
+  property edifying : Nil
+
+  property ethmoiditis : Int32?
+
+  property gastralgy : Nil
+
+  property goatherd : Int32?
+
+  property hammerdress : Int32?
+
+  property hangfire : Nil
+
+  property homocerc : Bool?
+
+  property lacunosity : Int32?
+
+  property longiloquence : Nil
+
+  property mameliere : Int32?
+
+  property motherless : Nil
+
+  property nonbookish : Nil
+
+  property noncorrodible : Nil
+
+  property nonsensicality : Nil
+
+  property oafishly : Int32?
+
+  property pfund : Nil
+
+  property preadvisory : Nil
+
+  property retroflexed : Nil
+
+  property saccharulmic : Int32?
+
+  property scowlful : Int32?
+
+  property secluded : Nil
+
+  property slackage : Nil
+
+  property sphaeridial : Int32?
+
+  property spondulics : Nil
+
+  property subsecive : Int32?
+
+  property swellmobsman : Nil
+
+  property trachyglossate : Int32?
+
+  property trialogue : Nil
+
+  property unassuaged : Int32?
+
+  property ungross : Nil
+
+  property unjudiciously : Nil
+end
+
+class MonaziteClass
+  include JSON::Serializable
+
+  property catharticalness : Float64
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32
+
+  property disdiapason : String
+
+  property homocerc : Bool
+
+  property nonbookish : Nil
+end
+
+class MonotheisticallyClass
+  include JSON::Serializable
+
+  property blaspheme : Nil
+
+  property catharticalness : Float64?
+
+  property celiosalpingectomy : Nil
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property consummativeness : Nil
+
+  property disdiapason : String?
+
+  property egestive : Nil
+
+  property enchylema : Nil
+
+  property gasconade : Nil
+
+  property holidayer : Nil
+
+  property homocerc : Bool?
+
+  property intuitionalism : Nil
+
+  property lophiostomate : Nil
+
+  property nonbookish : Nil
+
+  property nonvolition : Nil
+
+  property palatableness : Nil
+
+  property pimpery : Nil
+
+  property previolation : Nil
+
+  property reconveyance : Nil
+
+  property registership : Nil
+
+  property rhyacolite : Nil
+
+  property smithereens : Nil
+
+  property superedification : Nil
+
+  property trust : Nil
+
+  property whitestone : Nil
+end
+
+class Noncontributing
+  include JSON::Serializable
+
+  property estevin : String
+
+  property jolterhead : Float64
+
+  property sauternes : Int32
+
+  property sparsely : Bool
+
+  property unrequested : Nil
+end
+
+class OccupationalistClass
+  include JSON::Serializable
+
+  property beholdable : Nil
+
+  property brotuliform : Nil
+
+  @[JSON::Field(key: "Chimakum")]
+  property chimakum : Nil
+
+  property doodler : Nil
+
+  property emulsin : Nil
+
+  @[JSON::Field(key: "Fin")]
+  property fin : Nil
+
+  property flourishing : Nil
+
+  property flueless : Nil
+
+  property furtively : Nil
+
+  property gritter : Nil
+
+  property interwish : Nil
+
+  property monoxylic : Nil
+
+  property myristic : Nil
+
+  property nightwear : Nil
+
+  property peruser : Nil
+
+  property theoastrological : Nil
+
+  property thumby : Nil
+
+  property tingitid : Nil
+
+  property trailless : Nil
+
+  property unpocketed : Nil
+end
+
+class OutrivalClass
+  include JSON::Serializable
+
+  property adroitly : Nil
+
+  property bridehood : Nil
+
+  @[JSON::Field(key: "Castoroides")]
+  property castoroides : Nil
+
+  @[JSON::Field(key: "Czechoslovak")]
+  property czechoslovak : Nil
+
+  property diagenesis : Nil
+
+  property dihexahedron : Nil
+
+  property dopester : Nil
+
+  property eumerism : Nil
+
+  property flyness : Nil
+
+  property fouler : Nil
+
+  property laudanosine : Nil
+
+  @[JSON::Field(key: "Lingulidae")]
+  property lingulidae : Nil
+
+  property minutary : Nil
+
+  property mitra : Nil
+
+  property opisthorchiasis : Nil
+
+  property pensively : Nil
+
+  property pubigerous : Nil
+
+  property rebellious : Nil
+
+  property recodify : Nil
+
+  property unpaced : Nil
+end
+
+class PiaculumClass
+  include JSON::Serializable
+
+  property alada : Int32?
+
+  property amphistomous : Int32?
+
+  property boysenberry : Int32?
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property decardinalize : Int32?
+
+  property discouragement : Int32?
+
+  property disdiapason : String?
+
+  property doitrified : Int32?
+
+  property hexaspermous : Int32?
+
+  property homocerc : Bool?
+
+  property insinking : Int32?
+
+  property loathfulness : Int32?
+
+  property miasmatical : Int32?
+
+  property neurofibril : Int32?
+
+  property nonbookish : Nil
+
+  property phonendoscope : Int32?
+
+  property pilferment : Int32?
+
+  property predismissory : Int32?
+
+  property preinscription : Int32?
+
+  property quotative : Int32?
+
+  property sienna : Int32?
+
+  property thorax : Int32?
+
+  property yachting : Int32?
+
+  @[JSON::Field(key: "Zipper")]
+  property zipper : Int32?
+end
+
+class Pneumocele
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Carbonarism")]
+  property carbonarism : Nil
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property cineolic : Nil
+
+  property cobbly : Nil
+
+  property conchyliferous : Nil
+
+  property congregation : Nil
+
+  property disdiapason : String?
+
+  property enterotomy : Nil
+
+  property entophytal : Nil
+
+  property fewtrils : Nil
+
+  property herem : Nil
+
+  property homocerc : Bool?
+
+  @[JSON::Field(key: "Koniga")]
+  property koniga : Nil
+
+  property meticulosity : Nil
+
+  @[JSON::Field(key: "Micky")]
+  property micky : Nil
+
+  property mismarriage : Nil
+
+  property neurotrophic : Nil
+
+  property nonbookish : Nil
+
+  property persuasively : Nil
+
+  property replaceable : Nil
+
+  property silex : Nil
+
+  property taillight : Nil
+
+  property unjealous : Nil
+
+  property visitorial : Nil
+end
+
+class PotwhiskyClass
+  include JSON::Serializable
+
+  property arciform : Nil
+
+  property cresolin : Nil
+
+  property disheartener : Nil
+
+  property disproportionable : Nil
+
+  @[JSON::Field(key: "Euchorda")]
+  property euchorda : Nil
+
+  property ferryway : Nil
+
+  property filamentiferous : Nil
+
+  property flemish : Nil
+
+  property forgainst : Nil
+
+  property grainering : Nil
+
+  property irrevoluble : Nil
+
+  property kindredship : Nil
+
+  property pinguitudinous : Nil
+
+  property simpletonic : Nil
+
+  property singsong : Nil
+
+  property submergement : Nil
+
+  property supraoesophagal : Nil
+
+  property thrashel : Nil
+
+  property tyremesis : Nil
+
+  @[JSON::Field(key: "Yoruba")]
+  property yoruba : Nil
+end
+
+class PrefreshmanClass
+  include JSON::Serializable
+
+  property azorubine : Nil
+
+  property choroiditis : Nil
+
+  property coagulatory : Nil
+
+  property cyclorama : Nil
+
+  @[JSON::Field(key: "Dolphus")]
+  property dolphus : Nil
+
+  property duckhearted : Nil
+
+  @[JSON::Field(key: "Ficus")]
+  property ficus : Nil
+
+  @[JSON::Field(key: "Gemaric")]
+  property gemaric : Nil
+
+  property jugation : Nil
+
+  property myoliposis : Nil
+
+  property nonnomination : Nil
+
+  property palay : Nil
+
+  property pentactinal : Nil
+
+  @[JSON::Field(key: "Phaet")]
+  property phaet : Nil
+
+  property piquant : Nil
+
+  property registration : Nil
+
+  property remancipation : Nil
+
+  property scutatiform : Nil
+
+  property theodolite : Nil
+
+  property underward : Nil
+end
+
+alias JurorElement = JurorClass | Bool
+
+alias Kongoni = Array(Int32) | Hash(String, Int32)
+
+alias LadronismElement = Float64 | LadronismClass | String
+
+alias LandlubberlyElement = Int32 | LandlubberlyClass | Bool
+
+alias Listener = Int32 | Array(Nil)
+
+alias LupusElement = Int32 | LupusClass
+
+alias MonaziteElement = Float64 | MonaziteClass
+
+alias Monoliteral = Array(Nil) | Bool
+
+alias MonotheisticallyElement = MonotheisticallyClass | Array(Nil)
+
+alias Montage = Float64 | Array(Nil) | String
+
+alias Moralness = Float64 | Array(Nil)
+
+alias Mulishly = Float64 | Array(Int32)
+
+alias Myoscope = Int32 | Array(Nil) | Bool
+
+alias Neuromastic = Float64 | Array(Nil)
+
+alias Nonnervous = Int32 | Bool
+
+alias Nonvaluation = Float64 | Array(Nil) | Bool
+
+alias OccupationalistElement = Array(Nil) | OccupationalistClass
+
+alias OutrivalElement = Float64 | OutrivalClass
+
+alias Paleographically = Float64 | Hash(String, Int32?)
+
+alias Pamphletwise = Int32 | Hash(String, Int32) | String
+
+alias Pediatric = Float64 | Bool
+
+alias PiaculumElement = Float64 | PiaculumClass
+
+alias Piccadilly = Float64 | String
+
+alias Piffler = MonaziteClass | Array(Nil)
+
+alias Pithful = Int32 | Bool
+
+alias Placuntiti = Int32 | Hash(String, Int32)
+
+alias Plectopterous = Float64 | Hash(String, Int32)
+
+alias Poliorcetic = MonaziteClass | Bool
+
+alias Poormaster = Array(Int32) | Hash(String, Int32)
+
+alias PotwhiskyElement = Int32 | PotwhiskyClass
+
+alias Practicalizer = MonaziteClass | Array(Nil) | String
+
+alias PrefreshmanElement = Array(Nil) | PrefreshmanClass | String
+
+alias Prehensility = MonaziteClass | Array(Nil) | Bool
+
+alias Prevoidance = Int32 | Array(Int32) | MonaziteClass
+
+alias Protext = Array(Int32) | MonaziteClass | Bool
diff --git a/head/crystal/test/inputs/json/priority/combinations4.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/combinations4.json/default/TopLevel.cr
new file mode 100644
index 0000000..c180769
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/combinations4.json/default/TopLevel.cr
@@ -0,0 +1,964 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property protrusive : Array(Protrusive)
+
+  property pulpitism : Array(PulpitismElement)
+
+  property pyodermia : Array(PyodermiaElement)
+
+  property quebrachine : Array(QuebrachineElement?)
+
+  property querier : Array(Querier)
+
+  property rebarbative : Array(Rebarbative)
+
+  property reimagine : Array(Reimagine)
+
+  property ressaut : Ressaut
+
+  property retrocervical : Array(Retrocervical)
+
+  property revert : Array(Revert)
+
+  property rewrite : Array(RewriteElement)
+
+  property saccoderm : Array(Saccoderm?)
+
+  property santir : Array(SantirElement)
+
+  property saprophilous : Array(Saprophilous?)
+
+  property saxten : Array(SaxtenElement)
+
+  property scatty : Array(Scatty?)
+
+  property scoffer : Array(Scoffer?)
+
+  property scrampum : Array(Scrampum?)
+
+  property semantic : Float64
+
+  property serpentinic : Array(Serpentinic)
+
+  property shadowable : Array(Shadowable)
+
+  property sistering : Array(SisteringElement)
+
+  property staghunting : Array(Staghunting)
+
+  property stagmometer : Array(Stagmometer)
+
+  property stimulability : Array(Stimulability)
+
+  property strangleable : Array(Strangleable)
+
+  property strenuosity : Array(StrenuosityElement)
+
+  property tabaxir : Array(Tabaxir)
+
+  property talpiform : Array(Talpiform?)
+
+  property thwack : Array(Thwack)
+
+  property to : Array(Float64?)
+
+  property tortricine : Array(Tortricine)
+
+  property truantcy : Array(TruantcyElement)
+
+  property turgesce : Array(String)
+
+  property unbeginning : Array(Unbeginning)
+
+  property underdunged : Array(Float64)
+
+  property undesirability : Array(Undesirability)
+
+  property unerasing : Array(Unerasing)
+
+  property unguentarium : Array(Unguentarium?)
+
+  property unimpeachably : Array(UnimpeachablyElement)
+
+  property unmortgaged : Array(Unmortgaged?)
+
+  property unobstructed : Array(Unobstructed?)
+
+  property unreceptivity : Array(Unreceptivity)
+
+  property unsatisfactoriness : Array(Unsatisfactoriness)
+
+  property unsecurity : Array(Int32)
+
+  property unstressed : Array(UnstressedElement)
+
+  property untasked : Array(Untasked)
+
+  property unvarying : Array(Unvarying)
+
+  property vehemently : Array(Vehemently?)
+
+  property warriorship : Hash(String, Bool)
+
+  property whitepot : Array(Whitepot)
+
+  property wrothy : Array(WrothyElement)
+end
+
+class PulpitismClass
+  include JSON::Serializable
+
+  property abnet : Nil
+
+  property buckhorn : Nil
+
+  property calciform : Nil
+
+  property chelophore : Nil
+
+  property cogitation : Nil
+
+  property decreeable : Nil
+
+  property despicable : Nil
+
+  property isodiazo : Nil
+
+  property jadedly : Nil
+
+  property leptochlorite : Nil
+
+  property nursling : Nil
+
+  property palamedean : Nil
+
+  property photoheliograph : Nil
+
+  property pipewood : Nil
+
+  property roberd : Nil
+
+  property statable : Nil
+
+  property superassume : Nil
+
+  property syllabe : Nil
+
+  property toughhead : Nil
+
+  property underburn : Nil
+end
+
+class PyodermiaClass
+  include JSON::Serializable
+
+  property aphoristically : Nil
+
+  property apophyllous : Nil
+
+  property cognize : Nil
+
+  property dermonosology : Nil
+
+  @[JSON::Field(key: "Gyppo")]
+  property gyppo : Nil
+
+  property ither : Nil
+
+  property juglandaceous : Nil
+
+  property litho : Nil
+
+  property macropterous : Nil
+
+  property photographer : Nil
+
+  property romancing : Nil
+
+  property rumness : Nil
+
+  property somniloquist : Nil
+
+  property stressfully : Nil
+
+  property tactically : Nil
+
+  property tracheophony : Nil
+
+  property unappositely : Nil
+
+  property unclothedly : Nil
+
+  property unimplied : Nil
+
+  property unsyncopated : Nil
+end
+
+class QuebrachineClass
+  include JSON::Serializable
+
+  property catharticalness : Float64
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32
+
+  property disdiapason : String
+
+  property homocerc : Bool
+
+  property nonbookish : Nil
+end
+
+class Reimagine
+  include JSON::Serializable
+
+  property adducible : Nil
+
+  property anabolin : Nil
+
+  property brainy : Nil
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property chrysamine : Nil
+
+  property disdiapason : String?
+
+  property fluxweed : Nil
+
+  property glaucine : Nil
+
+  property grobianism : Nil
+
+  @[JSON::Field(key: "Hermo")]
+  property hermo : Nil
+
+  property hieroglyphist : Nil
+
+  property homocerc : Bool?
+
+  property icteroid : Nil
+
+  property immortal : Nil
+
+  property impetulant : Nil
+
+  property irrigate : Nil
+
+  property myxedema : Nil
+
+  property nonbookish : Nil
+
+  property onyx : Nil
+
+  property repasser : Nil
+
+  property septomarginal : Nil
+
+  property subdie : Nil
+
+  property tibiometatarsal : Nil
+
+  property waltzlike : Nil
+end
+
+class Ressaut
+  include JSON::Serializable
+
+  property apperceptive : String
+
+  property cuttoo : String
+
+  property douser : String
+
+  property drinkproof : String
+
+  property forementioned : String
+
+  @[JSON::Field(key: "Freesia")]
+  property freesia : String
+
+  @[JSON::Field(key: "Genevieve")]
+  property genevieve : String
+
+  property hyperdiabolical : String
+
+  property hypocone : String
+
+  property irreverentially : String
+
+  property jumart : String
+
+  @[JSON::Field(key: "Mimosaceae")]
+  property mimosaceae : String
+
+  property mollicrush : String
+
+  property nedder : String
+
+  property retinasphalt : String
+
+  property sough : String
+
+  property steading : String
+
+  @[JSON::Field(key: "Theopaschitism")]
+  property theopaschitism : String
+
+  property undurableness : String
+
+  property unmingleable : String
+end
+
+class RewriteClass
+  include JSON::Serializable
+
+  property accountancy : Nil
+
+  property cacotrophic : Nil
+
+  property contest : Nil
+
+  property couthily : Nil
+
+  property falculate : Nil
+
+  property foreseize : Nil
+
+  @[JSON::Field(key: "Hyades")]
+  property hyades : Nil
+
+  property lemnad : Nil
+
+  property monotheistically : Nil
+
+  property nonflying : Nil
+
+  @[JSON::Field(key: "Ptenoglossa")]
+  property ptenoglossa : Nil
+
+  property repatch : Nil
+
+  property rodman : Nil
+
+  property strung : Nil
+
+  property titmal : Nil
+
+  property twalpennyworth : Nil
+
+  property unblamable : Nil
+
+  property vertical : Nil
+
+  @[JSON::Field(key: "Whiggification")]
+  property whiggification : Nil
+
+  property yardman : Nil
+end
+
+class SantirClass
+  include JSON::Serializable
+
+  property admiredly : Nil
+
+  property demicaponier : Nil
+
+  property epitympanic : Nil
+
+  property investitor : Nil
+
+  property lupiform : Nil
+
+  property monoflagellate : Nil
+
+  property paleoethnic : Nil
+
+  property prediscountable : Nil
+
+  property rhetoricals : Nil
+
+  property roomth : Nil
+
+  property saccharose : Nil
+
+  property septonasal : Nil
+
+  property serpenticide : Nil
+
+  property setarious : Nil
+
+  property spaework : Nil
+
+  property stylite : Nil
+
+  @[JSON::Field(key: "Suessiones")]
+  property suessiones : Nil
+
+  property timelily : Nil
+
+  property unprofaned : Nil
+
+  property vorticular : Nil
+end
+
+class SaxtenClass
+  include JSON::Serializable
+
+  property algarrobilla : Nil
+
+  property bowgrace : Nil
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Centaurid")]
+  property centaurid : Nil
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property disdiapason : String?
+
+  property flix : Nil
+
+  property germanely : Nil
+
+  property homocerc : Bool?
+
+  property inhume : Nil
+
+  property lepidote : Nil
+
+  property megalochirous : Nil
+
+  property ninepenny : Nil
+
+  property nonbookish : Nil
+
+  property nondeist : Nil
+
+  property nymphaeaceous : Nil
+
+  property parietofrontal : Nil
+
+  property sancyite : Nil
+
+  property subjectivist : Nil
+
+  property tibiad : Nil
+
+  property transonic : Nil
+
+  property tripetalous : Nil
+
+  property trunchman : Nil
+
+  property urger : Nil
+
+  property withdrawnness : Nil
+end
+
+class Scatty
+  include JSON::Serializable
+
+  property aeriferous : Nil
+
+  property antical : Nil
+
+  property antighostism : Nil
+
+  property arcanum : Nil
+
+  property autotrophy : Nil
+
+  property baronial : Nil
+
+  property caffeine : Nil
+
+  property gorgoniacean : Nil
+
+  property heroical : Nil
+
+  property hydropical : Nil
+
+  property mechanology : Nil
+
+  property musicopoetic : Nil
+
+  property officiality : Nil
+
+  property oftentimes : Nil
+
+  property ophthalmotonometer : Nil
+
+  property reflectively : Nil
+
+  property springer : Nil
+
+  @[JSON::Field(key: "Tabasco")]
+  property tabasco : Nil
+
+  property teleianthous : Nil
+
+  property uncombated : Nil
+end
+
+class SisteringClass
+  include JSON::Serializable
+
+  property amphicarpic : Nil
+
+  @[JSON::Field(key: "Chianti")]
+  property chianti : Nil
+
+  property frigorific : Nil
+
+  @[JSON::Field(key: "Haplomi")]
+  property haplomi : Nil
+
+  property hyperkinesis : Nil
+
+  property laudable : Nil
+
+  property madwoman : Nil
+
+  property maimedly : Nil
+
+  @[JSON::Field(key: "Micropterygidae")]
+  property micropterygidae : Nil
+
+  property microrhabdus : Nil
+
+  property nondense : Nil
+
+  property phlebemphraxis : Nil
+
+  property redsear : Nil
+
+  property schismatical : Nil
+
+  property tartryl : Nil
+
+  property unabhorred : Nil
+
+  property undeliberateness : Nil
+
+  property unmixable : Nil
+
+  property untruckling : Nil
+
+  property vineal : Nil
+end
+
+class Staghunting
+  include JSON::Serializable
+
+  property calorimetric : Int32?
+
+  property canid : Int32?
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property disdiapason : String?
+
+  property ditriglyphic : Int32?
+
+  property floriferousness : Int32?
+
+  property gamelike : Int32?
+
+  property grig : Int32?
+
+  property homocerc : Bool?
+
+  property interloan : Int32?
+
+  property lithotomy : Int32?
+
+  property loric : Int32?
+
+  property membranocoriaceous : Int32?
+
+  property membranogenic : Int32?
+
+  property nonbookish : Nil
+
+  property overtrump : Int32?
+
+  property scotino : Int32?
+
+  property seasonable : Int32?
+
+  property sephen : Int32?
+
+  property stigmarioid : Int32?
+
+  property tired : Int32?
+
+  property trifid : Int32?
+
+  property undefeatedly : Int32?
+
+  property ungirlish : Int32?
+end
+
+class StrenuosityClass
+  include JSON::Serializable
+
+  property bliss : Int32?
+
+  property buccate : Int32?
+
+  property bulletproof : Int32?
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property crumblingness : Int32?
+
+  property disdiapason : String?
+
+  property engagedly : Int32?
+
+  property fightable : Int32?
+
+  property hoariness : Int32?
+
+  property homocerc : Bool?
+
+  property hypopodium : Int32?
+
+  property luxurist : Int32?
+
+  property mechanician : Int32?
+
+  property nonbookish : Nil
+
+  @[JSON::Field(key: "Onopordon")]
+  property onopordon : Int32?
+
+  property podgily : Int32?
+
+  property reformableness : Int32?
+
+  property scatterbrains : Int32?
+
+  property seminuria : Int32?
+
+  @[JSON::Field(key: "Sodomite")]
+  property sodomite : Int32?
+
+  property tramp : Int32?
+
+  property undueness : Int32?
+
+  property worthily : Int32?
+
+  @[JSON::Field(key: "Yankeeist")]
+  property yankeeist : Int32?
+end
+
+class TruantcyClass
+  include JSON::Serializable
+
+  property alfiona : Nil
+
+  property ascaridiasis : Nil
+
+  property bungey : Nil
+
+  property catharticalness : Float64?
+
+  property ceroxyle : Nil
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property chorology : Nil
+
+  property disdiapason : String?
+
+  property enmarble : Nil
+
+  @[JSON::Field(key: "Epeira")]
+  property epeira : Nil
+
+  @[JSON::Field(key: "Eurylaimi")]
+  property eurylaimi : Nil
+
+  property germination : Nil
+
+  property hallelujah : Nil
+
+  property homocerc : Bool?
+
+  property lev : Nil
+
+  property mouthing : Nil
+
+  property nonbookish : Nil
+
+  property philliloo : Nil
+
+  property planetal : Nil
+
+  property poney : Nil
+
+  property punctualist : Nil
+
+  property returnlessly : Nil
+
+  property skelder : Nil
+
+  property windwaywardly : Nil
+
+  @[JSON::Field(key: "Yuman")]
+  property yuman : Nil
+end
+
+class UnimpeachablyClass
+  include JSON::Serializable
+
+  property acerin : Int32?
+
+  @[JSON::Field(key: "Bobadil")]
+  property bobadil : Int32?
+
+  property catharticalness : Float64?
+
+  @[JSON::Field(key: "Chirotherium")]
+  property chirotherium : Int32?
+
+  property chlorophylligenous : Int32?
+
+  property conversational : Int32?
+
+  property demiowl : Int32?
+
+  property disdiapason : String?
+
+  property ectorhinal : Int32?
+
+  property gamblesomeness : Int32?
+
+  property homocerc : Bool?
+
+  property irrorate : Int32?
+
+  property kindergartening : Int32?
+
+  property lateritic : Int32?
+
+  property mespil : Int32?
+
+  property misconfiguration : Int32?
+
+  property nonbookish : Nil
+
+  property planometry : Int32?
+
+  @[JSON::Field(key: "Quiina")]
+  property quiina : Int32?
+
+  @[JSON::Field(key: "Robert")]
+  property robert : Int32?
+
+  property rot : Int32?
+
+  property subcinctorium : Int32?
+
+  property tussocker : Int32?
+
+  property ultraproud : Int32?
+
+  property unsuggestedness : Int32?
+end
+
+class UnstressedClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Alain")]
+  property alain : Nil
+
+  @[JSON::Field(key: "Amphirhina")]
+  property amphirhina : Nil
+
+  property antimachinery : Nil
+
+  property coldish : Nil
+
+  property crantara : Nil
+
+  property distinguishing : Nil
+
+  property elytroposis : Nil
+
+  property gentianwort : Nil
+
+  property heliosis : Nil
+
+  property instrumental : Nil
+
+  property introinflection : Nil
+
+  property kala : Nil
+
+  @[JSON::Field(key: "Lincolnian")]
+  property lincolnian : Nil
+
+  property metad : Nil
+
+  @[JSON::Field(key: "Sarcophilus")]
+  property sarcophilus : Nil
+
+  property swingingly : Nil
+
+  property unconformity : Nil
+
+  property undecreed : Nil
+
+  property venerable : Nil
+
+  property vowellessness : Nil
+end
+
+class WrothyClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Aeschynanthus")]
+  property aeschynanthus : Nil
+
+  property aquiferous : Nil
+
+  property cheapener : Nil
+
+  property enumeration : Nil
+
+  @[JSON::Field(key: "Ephesine")]
+  property ephesine : Nil
+
+  property escadrille : Nil
+
+  property estrous : Nil
+
+  property interestedly : Nil
+
+  property katakinetomer : Nil
+
+  property mortification : Nil
+
+  property morula : Nil
+
+  property orthosymmetrical : Nil
+
+  property overbark : Nil
+
+  property politist : Nil
+
+  property qualified : Nil
+
+  property sphenomalar : Nil
+
+  property throatful : Nil
+
+  property transhumance : Nil
+
+  property triandrian : Nil
+
+  property unbooked : Nil
+end
+
+alias Protrusive = Float64 | Array(Int32?)
+
+alias PulpitismElement = Float64 | Array(Int32) | PulpitismClass
+
+alias PyodermiaElement = Int32 | PyodermiaClass
+
+alias QuebrachineElement = Bool | QuebrachineClass
+
+alias Querier = Hash(String, Int32) | Bool
+
+alias Rebarbative = Float64 | Array(Int32) | Bool
+
+alias Retrocervical = Int32 | Array(Int32?)
+
+alias Revert = Bool | String
+
+alias RewriteElement = Float64 | Array(Nil) | RewriteClass
+
+alias Saccoderm = Array(Int32) | String
+
+alias SantirElement = Float64 | SantirClass
+
+alias Saprophilous = Hash(String, Int32) | String
+
+alias SaxtenElement = String | SaxtenClass
+
+alias Scoffer = Hash(String, Int32) | Array(Nil)
+
+alias Scrampum = Array(Int32) | Bool
+
+alias Serpentinic = Float64 | Array(Int32)
+
+alias Shadowable = Bool | Array(Int32?)
+
+alias SisteringElement = Int32 | Array(Nil) | SisteringClass
+
+alias Stagmometer = String | Array(Int32?)
+
+alias Stimulability = Int32 | Hash(String, Int32) | Bool
+
+alias Strangleable = Float64 | Array(Nil)
+
+alias StrenuosityElement = Array(Nil) | StrenuosityClass
+
+alias Tabaxir = Float64 | Bool
+
+alias Talpiform = Float64 | QuebrachineClass
+
+alias Thwack = Float64 | Bool | QuebrachineClass
+
+alias Tortricine = QuebrachineClass | Array(Int32?)
+
+alias TruantcyElement = Bool | TruantcyClass
+
+alias Unbeginning = Hash(String, Int32) | Array(Nil) | String
+
+alias Undesirability = Array(Int32) | Hash(String, Int32) | String
+
+alias Unerasing = Int32 | Hash(String, Int32) | Array(Nil)
+
+alias Unguentarium = Int32 | Array(Nil)
+
+alias UnimpeachablyElement = Bool | UnimpeachablyClass
+
+alias Unmortgaged = Float64 | Hash(String, Int32)
+
+alias Unobstructed = Int32 | QuebrachineClass
+
+alias Unreceptivity = Int32 | Array(Nil) | String
+
+alias Unsatisfactoriness = Int32 | Array(Int32) | Bool
+
+alias UnstressedElement = Bool | String | UnstressedClass
+
+alias Untasked = Float64 | Hash(String, Int32) | Array(Nil)
+
+alias Unvarying = Float64 | Hash(String, Int32) | Bool
+
+alias Vehemently = Array(Nil) | Bool
+
+alias Whitepot = Float64 | QuebrachineClass
+
+alias WrothyElement = Array(Nil) | WrothyClass
diff --git a/head/crystal/test/inputs/json/priority/combined-enum.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/combined-enum.json/default/TopLevel.cr
new file mode 100644
index 0000000..9443672
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/combined-enum.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property bar : Array(Bar)
+
+  property foo : Array(Bar)
+end
+
+class Bar
+  include JSON::Serializable
+
+  property x : String
+end
diff --git a/head/crystal/test/inputs/json/priority/direct-recursive.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/direct-recursive.json/default/TopLevel.cr
new file mode 100644
index 0000000..b7c5109
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/direct-recursive.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property also : TopLevel?
+
+  property bar : Bool
+
+  property foo : Int32
+
+  property moo : Float64
+
+  property quux : String
+end
diff --git a/head/crystal/test/inputs/json/priority/empty-enum.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/empty-enum.json/default/TopLevel.cr
new file mode 100644
index 0000000..2ac09b8
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/empty-enum.json/default/TopLevel.cr
@@ -0,0 +1,13 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Array(Foo)
+end
+
+class Foo
+  include JSON::Serializable
+
+  property bar : String?
+end
diff --git a/head/crystal/test/inputs/json/priority/issue2680-object-array.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/issue2680-object-array.json/default/TopLevel.cr
new file mode 100644
index 0000000..a22a4fd
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/issue2680-object-array.json/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property key : String
+end
diff --git a/head/crystal/test/inputs/json/priority/issue2680-scalar-array.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/issue2680-scalar-array.json/default/TopLevel.cr
new file mode 100644
index 0000000..8607039
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/issue2680-scalar-array.json/default/TopLevel.cr
@@ -0,0 +1,3 @@
+require "json"
+
+alias TopLevel = Array(Int32)
diff --git a/head/crystal/test/inputs/json/priority/keywords.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/keywords.json/default/TopLevel.cr
new file mode 100644
index 0000000..60dba38
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/keywords.json/default/TopLevel.cr
@@ -0,0 +1,2402 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property dummy : Int32
+
+  property obj1 : Obj1
+
+  property obj2 : Obj2
+
+  property obj3 : Obj3
+
+  property obj4 : Obj4
+
+  property obj5 : Obj5
+end
+
+class Obj1
+  include JSON::Serializable
+
+  property alignas : Alignas
+
+  property alignof : Alignof
+
+  property and : And
+
+  property and_eq : AndEq
+
+  @[JSON::Field(key: "Any")]
+  property any : AnyClass
+
+  property array : ArrayClass
+
+  property assert : Assert
+
+  property associatedtype : Associatedtype
+
+  property associativity : Associativity
+
+  property async : Async
+
+  property atomic : AtomicClass
+
+  property atomic_cancel : AtomicCancel
+
+  property atomic_commit : AtomicCommit
+
+  property atomic_noexcept : AtomicNoexcept
+
+  property auto : Auto
+
+  property await : Await
+
+  property base : Base
+
+  property bitand : Bitand
+
+  property bitor : Bitor
+
+  @[JSON::Field(key: "BOOL")]
+  property bool : BoolClass
+
+  property boolean : Boolean
+
+  property bycopy : Bycopy
+
+  property byref : Byref
+
+  property byte : Byte
+
+  property catch : Catch
+
+  property chan : Chan
+
+  property char : CharClass
+
+  property char16_t : Char16T
+
+  property char32_t : Char32T
+
+  property checked : Checked
+
+  property clone : Clone
+
+  property co_await : CoAwait
+
+  property co_return : CoReturn
+
+  property co_yield : CoYield
+
+  property compl : Compl
+
+  @[JSON::Field(key: "_Complex")]
+  property complex : Complex
+
+  property concept : Concept
+
+  property console : Console
+
+  property const : Const
+
+  property const_cast : ConstCast
+
+  property constexpr : Constexpr
+
+  property constructor : Constructor
+
+  property continue : Continue
+
+  property convenience : Convenience
+
+  property convert : Convert
+
+  property converter : Converter
+
+  property date : Date
+
+  property date_parse_handling : DateParseHandling
+
+  property debugger : Debugger
+
+  property decimal : Decimal
+
+  property declare : Declare
+
+  property decltype : Decltype
+
+  property decode_string : DecodeString
+
+  property dummy : Int32
+
+  @[JSON::Field(key: "_")]
+  property empty : Empty
+
+  @[JSON::Field(key: "_Imaginery")]
+  property imaginery : Imaginery
+
+  @[JSON::Field(key: "abstract")]
+  property obj1_abstract : Abstract
+
+  @[JSON::Field(key: "any")]
+  property obj1_any : Obj1Any
+
+  @[JSON::Field(key: "as")]
+  property obj1_as : As
+
+  @[JSON::Field(key: "asm")]
+  property obj1_asm : Asm
+
+  @[JSON::Field(key: "_Bool")]
+  property obj1_bool : Obj1Bool
+
+  @[JSON::Field(key: "break")]
+  property obj1_break : Break
+
+  @[JSON::Field(key: "case")]
+  property obj1_case : Case
+
+  @[JSON::Field(key: "Class")]
+  property obj1_class : ClassClass
+
+  @[JSON::Field(key: "bool")]
+  property purple_bool : Obj1BoolClass
+
+  @[JSON::Field(key: "class")]
+  property purple_class : Obj1Class
+end
+
+class Alignas
+  include JSON::Serializable
+
+  property alignas : Int32
+end
+
+class Alignof
+  include JSON::Serializable
+
+  property alignof : Int32
+end
+
+class And
+  include JSON::Serializable
+
+  property and : Int32
+end
+
+class AndEq
+  include JSON::Serializable
+
+  property and_eq : Int32
+end
+
+class AnyClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Any")]
+  property any : Int32
+end
+
+class ArrayClass
+  include JSON::Serializable
+
+  property array : Int32
+end
+
+class Assert
+  include JSON::Serializable
+
+  property assert : Int32
+end
+
+class Associatedtype
+  include JSON::Serializable
+
+  property associatedtype : Int32
+end
+
+class Associativity
+  include JSON::Serializable
+
+  property associativity : Int32
+end
+
+class Async
+  include JSON::Serializable
+
+  property async : Int32
+end
+
+class AtomicClass
+  include JSON::Serializable
+
+  property atomic : Int32
+end
+
+class AtomicCancel
+  include JSON::Serializable
+
+  property atomic_cancel : Int32
+end
+
+class AtomicCommit
+  include JSON::Serializable
+
+  property atomic_commit : Int32
+end
+
+class AtomicNoexcept
+  include JSON::Serializable
+
+  property atomic_noexcept : Int32
+end
+
+class Auto
+  include JSON::Serializable
+
+  property auto : Int32
+end
+
+class Await
+  include JSON::Serializable
+
+  property await : Int32
+end
+
+class Base
+  include JSON::Serializable
+
+  property base : Int32
+end
+
+class Bitand
+  include JSON::Serializable
+
+  property bitand : Int32
+end
+
+class Bitor
+  include JSON::Serializable
+
+  property bitor : Int32
+end
+
+class BoolClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "BOOL")]
+  property bool : Int32
+end
+
+class Boolean
+  include JSON::Serializable
+
+  property boolean : Int32
+end
+
+class Bycopy
+  include JSON::Serializable
+
+  property bycopy : Int32
+end
+
+class Byref
+  include JSON::Serializable
+
+  property byref : Int32
+end
+
+class Byte
+  include JSON::Serializable
+
+  property byte : Int32
+end
+
+class Catch
+  include JSON::Serializable
+
+  property catch : Int32
+end
+
+class Chan
+  include JSON::Serializable
+
+  property chan : Int32
+end
+
+class CharClass
+  include JSON::Serializable
+
+  property char : Int32
+end
+
+class Char16T
+  include JSON::Serializable
+
+  property char16_t : Int32
+end
+
+class Char32T
+  include JSON::Serializable
+
+  property char32_t : Int32
+end
+
+class Checked
+  include JSON::Serializable
+
+  property checked : Int32
+end
+
+class Clone
+  include JSON::Serializable
+
+  property clone : Int32
+end
+
+class CoAwait
+  include JSON::Serializable
+
+  property co_await : Int32
+end
+
+class CoReturn
+  include JSON::Serializable
+
+  property co_return : Int32
+end
+
+class CoYield
+  include JSON::Serializable
+
+  property co_yield : Int32
+end
+
+class Compl
+  include JSON::Serializable
+
+  property compl : Int32
+end
+
+class Complex
+  include JSON::Serializable
+
+  @[JSON::Field(key: "_Complex")]
+  property complex : Int32
+end
+
+class Concept
+  include JSON::Serializable
+
+  property concept : Int32
+end
+
+class Console
+  include JSON::Serializable
+
+  property console : Int32
+end
+
+class Const
+  include JSON::Serializable
+
+  property const : Int32
+end
+
+class ConstCast
+  include JSON::Serializable
+
+  property const_cast : Int32
+end
+
+class Constexpr
+  include JSON::Serializable
+
+  property constexpr : Int32
+end
+
+class Constructor
+  include JSON::Serializable
+
+  property constructor : Int32
+end
+
+class Continue
+  include JSON::Serializable
+
+  property continue : Int32
+end
+
+class Convenience
+  include JSON::Serializable
+
+  property convenience : Int32
+end
+
+class Convert
+  include JSON::Serializable
+
+  property convert : Int32
+end
+
+class Converter
+  include JSON::Serializable
+
+  property converter : Int32
+end
+
+class Date
+  include JSON::Serializable
+
+  property date : Int32
+end
+
+class DateParseHandling
+  include JSON::Serializable
+
+  property date_parse_handling : Int32
+end
+
+class Debugger
+  include JSON::Serializable
+
+  property debugger : Int32
+end
+
+class Decimal
+  include JSON::Serializable
+
+  property decimal : Int32
+end
+
+class Declare
+  include JSON::Serializable
+
+  property declare : Int32
+end
+
+class Decltype
+  include JSON::Serializable
+
+  property decltype : Int32
+end
+
+class DecodeString
+  include JSON::Serializable
+
+  property decode_string : Int32
+end
+
+class Empty
+  include JSON::Serializable
+
+  @[JSON::Field(key: "_")]
+  property empty : Int32
+end
+
+class Imaginery
+  include JSON::Serializable
+
+  @[JSON::Field(key: "_Imaginery")]
+  property imaginery : Int32
+end
+
+class Abstract
+  include JSON::Serializable
+
+  @[JSON::Field(key: "abstract")]
+  property abstract_abstract : Int32
+end
+
+class Obj1Any
+  include JSON::Serializable
+
+  property any : Int32
+end
+
+class As
+  include JSON::Serializable
+
+  @[JSON::Field(key: "as")]
+  property as_as : Int32
+end
+
+class Asm
+  include JSON::Serializable
+
+  @[JSON::Field(key: "asm")]
+  property asm_asm : Int32
+end
+
+class Obj1Bool
+  include JSON::Serializable
+
+  @[JSON::Field(key: "_Bool")]
+  property bool : Int32
+end
+
+class Break
+  include JSON::Serializable
+
+  @[JSON::Field(key: "break")]
+  property break_break : Int32
+end
+
+class Case
+  include JSON::Serializable
+
+  @[JSON::Field(key: "case")]
+  property case_case : Int32
+end
+
+class ClassClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Class")]
+  property class_class : Int32
+end
+
+class Obj1BoolClass
+  include JSON::Serializable
+
+  property bool : Int32
+end
+
+class Obj1Class
+  include JSON::Serializable
+
+  @[JSON::Field(key: "class")]
+  property class_class : Int32
+end
+
+class Obj2
+  include JSON::Serializable
+
+  property default : Default
+
+  property defer : Defer
+
+  property deinit : Deinit
+
+  property del : Del
+
+  property delegate : Delegate
+
+  property delete : Delete
+
+  property dict : Dict
+
+  property dictionary : Dictionary
+
+  @[JSON::Field(key: "didSet")]
+  property did_set : DidSet
+
+  property double : Double
+
+  property dummy : Int32
+
+  property dynamic : Dynamic
+
+  property dynamic_cast : DynamicCast
+
+  property elif : Elif
+
+  property encode_quick_type : EncodeQuickType
+
+  @[JSON::Field(key: "equalityContract")]
+  property equality_contract : EqualityContract
+
+  property event : EventClass
+
+  property except : Except
+
+  property exception : ExceptionClass
+
+  property explicit : Explicit
+
+  property export : Export
+
+  property exposing : Exposing
+
+  property extends : Extends
+
+  property extension : Extension
+
+  property extern : ExternClass
+
+  property fallthrough : Fallthrough
+
+  property fileprivate : Fileprivate
+
+  property final : Final
+
+  property finally : Finally
+
+  property fixed : Fixed
+
+  property float : FloatClass
+
+  property foreach : Foreach
+
+  property friend : Friend
+
+  property from : From
+
+  property from_json : FromJson
+
+  property func : Func
+
+  property function : Function
+
+  property get : Get
+
+  property global : Global
+
+  property go : Go
+
+  property goto : Goto
+
+  property guard : Guard
+
+  @[JSON::Field(key: "hasOwnProperty")]
+  property has_own_property : HasOwnProperty
+
+  property id : Id
+
+  @[JSON::Field(key: "IMP")]
+  property imp : Imp
+
+  property implements : Implements
+
+  property implicit : Implicit
+
+  property import : Import
+
+  property indirect : Indirect
+
+  property infix : Infix
+
+  property init : Init
+
+  property inline : Inline
+
+  property inout : Inout
+
+  property instanceof : Instanceof
+
+  property int : IntClass
+
+  property interface : Interface
+
+  property internal : Internal
+
+  @[JSON::Field(key: "def")]
+  property obj2_def : Def
+
+  @[JSON::Field(key: "do")]
+  property obj2_do : Do
+
+  @[JSON::Field(key: "else")]
+  property obj2_else : Else
+
+  @[JSON::Field(key: "enum")]
+  property obj2_enum : EnumClass
+
+  @[JSON::Field(key: "False")]
+  property obj2_false : False
+
+  @[JSON::Field(key: "for")]
+  property obj2_for : For
+
+  @[JSON::Field(key: "if")]
+  property obj2_if : If
+
+  @[JSON::Field(key: "in")]
+  property obj2_in : In
+
+  @[JSON::Field(key: "false")]
+  property purple_false : FalseClass
+end
+
+class Default
+  include JSON::Serializable
+
+  property default : Int32
+end
+
+class Defer
+  include JSON::Serializable
+
+  property defer : Int32
+end
+
+class Deinit
+  include JSON::Serializable
+
+  property deinit : Int32
+end
+
+class Del
+  include JSON::Serializable
+
+  property del : Int32
+end
+
+class Delegate
+  include JSON::Serializable
+
+  property delegate : Int32
+end
+
+class Delete
+  include JSON::Serializable
+
+  property delete : Int32
+end
+
+class Dict
+  include JSON::Serializable
+
+  property dict : Int32
+end
+
+class Dictionary
+  include JSON::Serializable
+
+  property dictionary : Int32
+end
+
+class DidSet
+  include JSON::Serializable
+
+  @[JSON::Field(key: "didSet")]
+  property did_set : Int32
+end
+
+class Double
+  include JSON::Serializable
+
+  property double : Int32
+end
+
+class Dynamic
+  include JSON::Serializable
+
+  property dynamic : Int32
+end
+
+class DynamicCast
+  include JSON::Serializable
+
+  property dynamic_cast : Int32
+end
+
+class Elif
+  include JSON::Serializable
+
+  property elif : Int32
+end
+
+class EncodeQuickType
+  include JSON::Serializable
+
+  property encode_quick_type : Int32
+end
+
+class EqualityContract
+  include JSON::Serializable
+
+  @[JSON::Field(key: "equalityContract")]
+  property equality_contract : Int32
+end
+
+class EventClass
+  include JSON::Serializable
+
+  property event : Int32
+end
+
+class Except
+  include JSON::Serializable
+
+  property except : Int32
+end
+
+class ExceptionClass
+  include JSON::Serializable
+
+  property exception : Int32
+end
+
+class Explicit
+  include JSON::Serializable
+
+  property explicit : Int32
+end
+
+class Export
+  include JSON::Serializable
+
+  property export : Int32
+end
+
+class Exposing
+  include JSON::Serializable
+
+  property exposing : Int32
+end
+
+class Extends
+  include JSON::Serializable
+
+  property extends : Int32
+end
+
+class Extension
+  include JSON::Serializable
+
+  property extension : Int32
+end
+
+class ExternClass
+  include JSON::Serializable
+
+  property extern : Int32
+end
+
+class Fallthrough
+  include JSON::Serializable
+
+  property fallthrough : Int32
+end
+
+class Fileprivate
+  include JSON::Serializable
+
+  property fileprivate : Int32
+end
+
+class Final
+  include JSON::Serializable
+
+  property final : Int32
+end
+
+class Finally
+  include JSON::Serializable
+
+  property finally : Int32
+end
+
+class Fixed
+  include JSON::Serializable
+
+  property fixed : Int32
+end
+
+class FloatClass
+  include JSON::Serializable
+
+  property float : Int32
+end
+
+class Foreach
+  include JSON::Serializable
+
+  property foreach : Int32
+end
+
+class Friend
+  include JSON::Serializable
+
+  property friend : Int32
+end
+
+class From
+  include JSON::Serializable
+
+  property from : Int32
+end
+
+class FromJson
+  include JSON::Serializable
+
+  property from_json : Int32
+end
+
+class Func
+  include JSON::Serializable
+
+  property func : Int32
+end
+
+class Function
+  include JSON::Serializable
+
+  property function : Int32
+end
+
+class Get
+  include JSON::Serializable
+
+  property get : Int32
+end
+
+class Global
+  include JSON::Serializable
+
+  property global : Int32
+end
+
+class Go
+  include JSON::Serializable
+
+  property go : Int32
+end
+
+class Goto
+  include JSON::Serializable
+
+  property goto : Int32
+end
+
+class Guard
+  include JSON::Serializable
+
+  property guard : Int32
+end
+
+class HasOwnProperty
+  include JSON::Serializable
+
+  @[JSON::Field(key: "hasOwnProperty")]
+  property has_own_property : Int32
+end
+
+class Id
+  include JSON::Serializable
+
+  property id : Int32
+end
+
+class Imp
+  include JSON::Serializable
+
+  @[JSON::Field(key: "IMP")]
+  property imp : Int32
+end
+
+class Implements
+  include JSON::Serializable
+
+  property implements : Int32
+end
+
+class Implicit
+  include JSON::Serializable
+
+  property implicit : Int32
+end
+
+class Import
+  include JSON::Serializable
+
+  property import : Int32
+end
+
+class Indirect
+  include JSON::Serializable
+
+  property indirect : Int32
+end
+
+class Infix
+  include JSON::Serializable
+
+  property infix : Int32
+end
+
+class Init
+  include JSON::Serializable
+
+  property init : Int32
+end
+
+class Inline
+  include JSON::Serializable
+
+  property inline : Int32
+end
+
+class Inout
+  include JSON::Serializable
+
+  property inout : Int32
+end
+
+class Instanceof
+  include JSON::Serializable
+
+  property instanceof : Int32
+end
+
+class IntClass
+  include JSON::Serializable
+
+  property int : Int32
+end
+
+class Interface
+  include JSON::Serializable
+
+  property interface : Int32
+end
+
+class Internal
+  include JSON::Serializable
+
+  property internal : Int32
+end
+
+class Def
+  include JSON::Serializable
+
+  @[JSON::Field(key: "def")]
+  property def_def : Int32
+end
+
+class Do
+  include JSON::Serializable
+
+  @[JSON::Field(key: "do")]
+  property do_do : Int32
+end
+
+class Else
+  include JSON::Serializable
+
+  @[JSON::Field(key: "else")]
+  property else_else : Int32
+end
+
+class EnumClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "enum")]
+  property enum_enum : Int32
+end
+
+class False
+  include JSON::Serializable
+
+  @[JSON::Field(key: "False")]
+  property false_false : Int32
+end
+
+class For
+  include JSON::Serializable
+
+  @[JSON::Field(key: "for")]
+  property for_for : Int32
+end
+
+class If
+  include JSON::Serializable
+
+  @[JSON::Field(key: "if")]
+  property if_if : Int32
+end
+
+class In
+  include JSON::Serializable
+
+  @[JSON::Field(key: "in")]
+  property in_in : Int32
+end
+
+class FalseClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "false")]
+  property false_false : Int32
+end
+
+class Obj3
+  include JSON::Serializable
+
+  property dummy : Int32
+
+  property is : Is
+
+  property iterable : IterableClass
+
+  property jdec : Jdec
+
+  property jenc : Jenc
+
+  property jpipe : Jpipe
+
+  property json : Json
+
+  property json_converter : JsonConverter
+
+  property json_serializer : JsonSerializer
+
+  property json_token : JsonToken
+
+  property json_writer : JsonWriter
+
+  property lambda : Lambda
+
+  property lazy : Lazy
+
+  property left : Left
+
+  property let : Let
+
+  property list : List
+
+  property lock : Lock
+
+  property long : Long
+
+  property map : Map
+
+  property metadata_property_handling : MetadataPropertyHandling
+
+  property mutable : Mutable
+
+  property mutating : Mutating
+
+  property namespace : Namespace
+
+  property native : Native
+
+  property new : New
+
+  property newtonsoft : Newtonsoft
+
+  @[JSON::Field(key: "NO")]
+  property no : No
+
+  property noexcept : Noexcept
+
+  property nonatomic : Nonatomic
+
+  @[JSON::Field(key: "None")]
+  property none : None
+
+  property nonlocal : Nonlocal
+
+  property nonmutating : Nonmutating
+
+  property not : Not
+
+  property not_eq : NotEq
+
+  @[JSON::Field(key: "NSString")]
+  property ns_string : NsString
+
+  @[JSON::Field(key: "NULL")]
+  property null : Null
+
+  property nullptr : Nullptr
+
+  property number : NumberClass
+
+  @[JSON::Field(key: "module")]
+  property obj3_module : Module
+
+  @[JSON::Field(key: "nil")]
+  property obj3_nil : NilClass
+
+  @[JSON::Field(key: "none")]
+  property obj3_none : NoneClass
+
+  @[JSON::Field(key: "null")]
+  property obj3_null : NullClass
+
+  @[JSON::Field(key: "of")]
+  property obj3_of : Of
+
+  @[JSON::Field(key: "out")]
+  property obj3_out : Out
+
+  @[JSON::Field(key: "private")]
+  property obj3_private : Private
+
+  @[JSON::Field(key: "protected")]
+  property obj3_protected : Protected
+
+  @[JSON::Field(key: "protocol")]
+  property obj3_protocol : ProtocolClass
+
+  property object : ObjectClass
+
+  property oneway : Oneway
+
+  property open : Open
+
+  property operator : Operator
+
+  property optional : Optional
+
+  property or : Or
+
+  property or_eq : OrEq
+
+  property override : Override
+
+  property package : Package
+
+  property params : Params
+
+  property pass : Pass
+
+  property port : Port
+
+  property postfix : Postfix
+
+  property precedence : Precedence
+
+  property prefix : Prefix
+
+  property print : Print
+
+  @[JSON::Field(key: "printMembers")]
+  property print_members : PrintMembers
+
+  property printf : Printf
+
+  @[JSON::Field(key: "Protocol")]
+  property protocol : Protocol
+end
+
+class Is
+  include JSON::Serializable
+
+  property is : Int32
+end
+
+class IterableClass
+  include JSON::Serializable
+
+  property iterable : Int32
+end
+
+class Jdec
+  include JSON::Serializable
+
+  property jdec : Int32
+end
+
+class Jenc
+  include JSON::Serializable
+
+  property jenc : Int32
+end
+
+class Jpipe
+  include JSON::Serializable
+
+  property jpipe : Int32
+end
+
+class Json
+  include JSON::Serializable
+
+  property json : Int32
+end
+
+class JsonConverter
+  include JSON::Serializable
+
+  property json_converter : Int32
+end
+
+class JsonSerializer
+  include JSON::Serializable
+
+  property json_serializer : Int32
+end
+
+class JsonToken
+  include JSON::Serializable
+
+  property json_token : Int32
+end
+
+class JsonWriter
+  include JSON::Serializable
+
+  property json_writer : Int32
+end
+
+class Lambda
+  include JSON::Serializable
+
+  property lambda : Int32
+end
+
+class Lazy
+  include JSON::Serializable
+
+  property lazy : Int32
+end
+
+class Left
+  include JSON::Serializable
+
+  property left : Int32
+end
+
+class Let
+  include JSON::Serializable
+
+  property let : Int32
+end
+
+class List
+  include JSON::Serializable
+
+  property list : Int32
+end
+
+class Lock
+  include JSON::Serializable
+
+  property lock : Int32
+end
+
+class Long
+  include JSON::Serializable
+
+  property long : Int32
+end
+
+class Map
+  include JSON::Serializable
+
+  property map : Int32
+end
+
+class MetadataPropertyHandling
+  include JSON::Serializable
+
+  property metadata_property_handling : Int32
+end
+
+class Mutable
+  include JSON::Serializable
+
+  property mutable : Int32
+end
+
+class Mutating
+  include JSON::Serializable
+
+  property mutating : Int32
+end
+
+class Namespace
+  include JSON::Serializable
+
+  property namespace : Int32
+end
+
+class Native
+  include JSON::Serializable
+
+  property native : Int32
+end
+
+class New
+  include JSON::Serializable
+
+  property new : Int32
+end
+
+class Newtonsoft
+  include JSON::Serializable
+
+  property newtonsoft : Int32
+end
+
+class No
+  include JSON::Serializable
+
+  @[JSON::Field(key: "NO")]
+  property no : Int32
+end
+
+class Noexcept
+  include JSON::Serializable
+
+  property noexcept : Int32
+end
+
+class Nonatomic
+  include JSON::Serializable
+
+  property nonatomic : Int32
+end
+
+class None
+  include JSON::Serializable
+
+  @[JSON::Field(key: "None")]
+  property none : Int32
+end
+
+class Nonlocal
+  include JSON::Serializable
+
+  property nonlocal : Int32
+end
+
+class Nonmutating
+  include JSON::Serializable
+
+  property nonmutating : Int32
+end
+
+class Not
+  include JSON::Serializable
+
+  property not : Int32
+end
+
+class NotEq
+  include JSON::Serializable
+
+  property not_eq : Int32
+end
+
+class NsString
+  include JSON::Serializable
+
+  @[JSON::Field(key: "NSString")]
+  property ns_string : Int32
+end
+
+class Null
+  include JSON::Serializable
+
+  @[JSON::Field(key: "NULL")]
+  property null : Int32
+end
+
+class Nullptr
+  include JSON::Serializable
+
+  property nullptr : Int32
+end
+
+class NumberClass
+  include JSON::Serializable
+
+  property number : Int32
+end
+
+class Module
+  include JSON::Serializable
+
+  @[JSON::Field(key: "module")]
+  property module_module : Int32
+end
+
+class NilClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "nil")]
+  property nil_nil : Int32
+end
+
+class NoneClass
+  include JSON::Serializable
+
+  property none : Int32
+end
+
+class NullClass
+  include JSON::Serializable
+
+  property null : Int32
+end
+
+class Of
+  include JSON::Serializable
+
+  @[JSON::Field(key: "of")]
+  property of_of : Int32
+end
+
+class Out
+  include JSON::Serializable
+
+  @[JSON::Field(key: "out")]
+  property out_out : Int32
+end
+
+class Private
+  include JSON::Serializable
+
+  @[JSON::Field(key: "private")]
+  property private_private : Int32
+end
+
+class Protected
+  include JSON::Serializable
+
+  @[JSON::Field(key: "protected")]
+  property protected_protected : Int32
+end
+
+class ProtocolClass
+  include JSON::Serializable
+
+  property protocol : Int32
+end
+
+class ObjectClass
+  include JSON::Serializable
+
+  property object : Int32
+end
+
+class Oneway
+  include JSON::Serializable
+
+  property oneway : Int32
+end
+
+class Open
+  include JSON::Serializable
+
+  property open : Int32
+end
+
+class Operator
+  include JSON::Serializable
+
+  property operator : Int32
+end
+
+class Optional
+  include JSON::Serializable
+
+  property optional : Int32
+end
+
+class Or
+  include JSON::Serializable
+
+  property or : Int32
+end
+
+class OrEq
+  include JSON::Serializable
+
+  property or_eq : Int32
+end
+
+class Override
+  include JSON::Serializable
+
+  property override : Int32
+end
+
+class Package
+  include JSON::Serializable
+
+  property package : Int32
+end
+
+class Params
+  include JSON::Serializable
+
+  property params : Int32
+end
+
+class Pass
+  include JSON::Serializable
+
+  property pass : Int32
+end
+
+class Port
+  include JSON::Serializable
+
+  property port : Int32
+end
+
+class Postfix
+  include JSON::Serializable
+
+  property postfix : Int32
+end
+
+class Precedence
+  include JSON::Serializable
+
+  property precedence : Int32
+end
+
+class Prefix
+  include JSON::Serializable
+
+  property prefix : Int32
+end
+
+class Print
+  include JSON::Serializable
+
+  property print : Int32
+end
+
+class PrintMembers
+  include JSON::Serializable
+
+  @[JSON::Field(key: "printMembers")]
+  property print_members : Int32
+end
+
+class Printf
+  include JSON::Serializable
+
+  property printf : Int32
+end
+
+class Protocol
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Protocol")]
+  property protocol : Int32
+end
+
+class Obj4
+  include JSON::Serializable
+
+  property dummy : Int32
+
+  @[JSON::Field(key: "require")]
+  property obj4_require : Require
+
+  @[JSON::Field(key: "return")]
+  property obj4_return : Return
+
+  @[JSON::Field(key: "select")]
+  property obj4_select : Select
+
+  @[JSON::Field(key: "Self")]
+  property obj4_self : Self
+
+  @[JSON::Field(key: "sizeof")]
+  property obj4_sizeof : Sizeof
+
+  @[JSON::Field(key: "struct")]
+  property obj4_struct : StructClass
+
+  @[JSON::Field(key: "super")]
+  property obj4_super : Super
+
+  @[JSON::Field(key: "then")]
+  property obj4_then : Then
+
+  @[JSON::Field(key: "True")]
+  property obj4_true : True
+
+  @[JSON::Field(key: "Type")]
+  property obj4_type : Type
+
+  @[JSON::Field(key: "typeof")]
+  property obj4_typeof : Typeof
+
+  property public : Public
+
+  @[JSON::Field(key: "self")]
+  property purple_self : SelfClass
+
+  @[JSON::Field(key: "true")]
+  property purple_true : TrueClass
+
+  @[JSON::Field(key: "type")]
+  property purple_type : TypeClass
+
+  property quicktype : Quicktype
+
+  property raise : Raise
+
+  property range : RangeClass
+
+  property readonly : Readonly
+
+  property ref : Ref
+
+  property register : Register
+
+  property reinterpret_cast : ReinterpretCast
+
+  property repeat : Repeat
+
+  property required : Required
+
+  property requires : Requires
+
+  property restrict : Restrict
+
+  property retain : Retain
+
+  property rethrows : Rethrows
+
+  property right : Right
+
+  property sbyte : Sbyte
+
+  property sealed : Sealed
+
+  @[JSON::Field(key: "SEL")]
+  property sel : Sel
+
+  property serialize : Serialize
+
+  property set : SetClass
+
+  property short : Short
+
+  property signed : Signed
+
+  property stackalloc : Stackalloc
+
+  property static : Static
+
+  property static_assert : StaticAssert
+
+  property static_cast : StaticCast
+
+  property strictfp : Strictfp
+
+  property string : StringClass
+
+  property subscript : Subscript
+
+  property switch : Switch
+
+  property symbol : SymbolClass
+
+  property synchronized : Synchronized
+
+  property system : SystemClass
+
+  property template : Template
+
+  property this : This
+
+  property thread_local : ThreadLocalClass
+
+  property throw : Throw
+
+  property throws : Throws
+
+  property to_json : ToJson
+
+  property top_level : TopLevelClass
+
+  property transient : Transient
+
+  property try : Try
+
+  property typealias : Typealias
+
+  property typedef : Typedef
+
+  property typeid : Typeid
+
+  property typename : Typename
+
+  property uint : Uint
+
+  property ulong : Ulong
+
+  property unchecked : Unchecked
+
+  property undefined : Undefined
+end
+
+class Require
+  include JSON::Serializable
+
+  @[JSON::Field(key: "require")]
+  property require_require : Int32
+end
+
+class Return
+  include JSON::Serializable
+
+  @[JSON::Field(key: "return")]
+  property return_return : Int32
+end
+
+class Select
+  include JSON::Serializable
+
+  @[JSON::Field(key: "select")]
+  property select_select : Int32
+end
+
+class Self
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Self")]
+  property self_self : Int32
+end
+
+class Sizeof
+  include JSON::Serializable
+
+  @[JSON::Field(key: "sizeof")]
+  property sizeof_sizeof : Int32
+end
+
+class StructClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "struct")]
+  property struct_struct : Int32
+end
+
+class Super
+  include JSON::Serializable
+
+  @[JSON::Field(key: "super")]
+  property super_super : Int32
+end
+
+class Then
+  include JSON::Serializable
+
+  @[JSON::Field(key: "then")]
+  property then_then : Int32
+end
+
+class True
+  include JSON::Serializable
+
+  @[JSON::Field(key: "True")]
+  property true_true : Int32
+end
+
+class Type
+  include JSON::Serializable
+
+  @[JSON::Field(key: "Type")]
+  property type_type : Int32
+end
+
+class Typeof
+  include JSON::Serializable
+
+  @[JSON::Field(key: "typeof")]
+  property typeof_typeof : Int32
+end
+
+class Public
+  include JSON::Serializable
+
+  property public : Int32
+end
+
+class SelfClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "self")]
+  property self_self : Int32
+end
+
+class TrueClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "true")]
+  property true_true : Int32
+end
+
+class TypeClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property type_type : Int32
+end
+
+class Quicktype
+  include JSON::Serializable
+
+  property quicktype : Int32
+end
+
+class Raise
+  include JSON::Serializable
+
+  property raise : Int32
+end
+
+class RangeClass
+  include JSON::Serializable
+
+  property range : Int32
+end
+
+class Readonly
+  include JSON::Serializable
+
+  property readonly : Int32
+end
+
+class Ref
+  include JSON::Serializable
+
+  property ref : Int32
+end
+
+class Register
+  include JSON::Serializable
+
+  property register : Int32
+end
+
+class ReinterpretCast
+  include JSON::Serializable
+
+  property reinterpret_cast : Int32
+end
+
+class Repeat
+  include JSON::Serializable
+
+  property repeat : Int32
+end
+
+class Required
+  include JSON::Serializable
+
+  property required : Int32
+end
+
+class Requires
+  include JSON::Serializable
+
+  property requires : Int32
+end
+
+class Restrict
+  include JSON::Serializable
+
+  property restrict : Int32
+end
+
+class Retain
+  include JSON::Serializable
+
+  property retain : Int32
+end
+
+class Rethrows
+  include JSON::Serializable
+
+  property rethrows : Int32
+end
+
+class Right
+  include JSON::Serializable
+
+  property right : Int32
+end
+
+class Sbyte
+  include JSON::Serializable
+
+  property sbyte : Int32
+end
+
+class Sealed
+  include JSON::Serializable
+
+  property sealed : Int32
+end
+
+class Sel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "SEL")]
+  property sel : Int32
+end
+
+class Serialize
+  include JSON::Serializable
+
+  property serialize : Int32
+end
+
+class SetClass
+  include JSON::Serializable
+
+  property set : Int32
+end
+
+class Short
+  include JSON::Serializable
+
+  property short : Int32
+end
+
+class Signed
+  include JSON::Serializable
+
+  property signed : Int32
+end
+
+class Stackalloc
+  include JSON::Serializable
+
+  property stackalloc : Int32
+end
+
+class Static
+  include JSON::Serializable
+
+  property static : Int32
+end
+
+class StaticAssert
+  include JSON::Serializable
+
+  property static_assert : Int32
+end
+
+class StaticCast
+  include JSON::Serializable
+
+  property static_cast : Int32
+end
+
+class Strictfp
+  include JSON::Serializable
+
+  property strictfp : Int32
+end
+
+class StringClass
+  include JSON::Serializable
+
+  property string : Int32
+end
+
+class Subscript
+  include JSON::Serializable
+
+  property subscript : Int32
+end
+
+class Switch
+  include JSON::Serializable
+
+  property switch : Int32
+end
+
+class SymbolClass
+  include JSON::Serializable
+
+  property symbol : Int32
+end
+
+class Synchronized
+  include JSON::Serializable
+
+  property synchronized : Int32
+end
+
+class SystemClass
+  include JSON::Serializable
+
+  property system : Int32
+end
+
+class Template
+  include JSON::Serializable
+
+  property template : Int32
+end
+
+class This
+  include JSON::Serializable
+
+  property this : Int32
+end
+
+class ThreadLocalClass
+  include JSON::Serializable
+
+  property thread_local : Int32
+end
+
+class Throw
+  include JSON::Serializable
+
+  property throw : Int32
+end
+
+class Throws
+  include JSON::Serializable
+
+  property throws : Int32
+end
+
+class ToJson
+  include JSON::Serializable
+
+  property to_json : Int32
+end
+
+class TopLevelClass
+  include JSON::Serializable
+
+  property top_level : Int32
+end
+
+class Transient
+  include JSON::Serializable
+
+  property transient : Int32
+end
+
+class Try
+  include JSON::Serializable
+
+  property try : Int32
+end
+
+class Typealias
+  include JSON::Serializable
+
+  property typealias : Int32
+end
+
+class Typedef
+  include JSON::Serializable
+
+  property typedef : Int32
+end
+
+class Typeid
+  include JSON::Serializable
+
+  property typeid : Int32
+end
+
+class Typename
+  include JSON::Serializable
+
+  property typename : Int32
+end
+
+class Uint
+  include JSON::Serializable
+
+  property uint : Int32
+end
+
+class Ulong
+  include JSON::Serializable
+
+  property ulong : Int32
+end
+
+class Unchecked
+  include JSON::Serializable
+
+  property unchecked : Int32
+end
+
+class Undefined
+  include JSON::Serializable
+
+  property undefined : Int32
+end
+
+class Obj5
+  include JSON::Serializable
+
+  property dummy : Int32
+
+  @[JSON::Field(key: "union")]
+  property obj5_union : UnionClass
+
+  @[JSON::Field(key: "while")]
+  property obj5_while : While
+
+  @[JSON::Field(key: "with")]
+  property obj5_with : With
+
+  @[JSON::Field(key: "yield")]
+  property obj5_yield : Yield
+
+  property unowned : Unowned
+
+  property unsafe : Unsafe
+
+  property unsigned : Unsigned
+
+  property ushort : Ushort
+
+  property using : Using
+
+  property var : Var
+
+  property virtual : Virtual
+
+  property void : VoidClass
+
+  property volatile : Volatile
+
+  property wchar_t : WcharT
+
+  property weak : Weak
+
+  property where : Where
+
+  @[JSON::Field(key: "willSet")]
+  property will_set : WillSet
+
+  property xor : Xor
+
+  property xor_eq : XorEq
+
+  @[JSON::Field(key: "YES")]
+  property yes : Yes
+end
+
+class UnionClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "union")]
+  property union_union : Int32
+end
+
+class While
+  include JSON::Serializable
+
+  @[JSON::Field(key: "while")]
+  property while_while : Int32
+end
+
+class With
+  include JSON::Serializable
+
+  @[JSON::Field(key: "with")]
+  property with_with : Int32
+end
+
+class Yield
+  include JSON::Serializable
+
+  @[JSON::Field(key: "yield")]
+  property yield_yield : Int32
+end
+
+class Unowned
+  include JSON::Serializable
+
+  property unowned : Int32
+end
+
+class Unsafe
+  include JSON::Serializable
+
+  property unsafe : Int32
+end
+
+class Unsigned
+  include JSON::Serializable
+
+  property unsigned : Int32
+end
+
+class Ushort
+  include JSON::Serializable
+
+  property ushort : Int32
+end
+
+class Using
+  include JSON::Serializable
+
+  property using : Int32
+end
+
+class Var
+  include JSON::Serializable
+
+  property var : Int32
+end
+
+class Virtual
+  include JSON::Serializable
+
+  property virtual : Int32
+end
+
+class VoidClass
+  include JSON::Serializable
+
+  property void : Int32
+end
+
+class Volatile
+  include JSON::Serializable
+
+  property volatile : Int32
+end
+
+class WcharT
+  include JSON::Serializable
+
+  property wchar_t : Int32
+end
+
+class Weak
+  include JSON::Serializable
+
+  property weak : Int32
+end
+
+class Where
+  include JSON::Serializable
+
+  property where : Int32
+end
+
+class WillSet
+  include JSON::Serializable
+
+  @[JSON::Field(key: "willSet")]
+  property will_set : Int32
+end
+
+class Xor
+  include JSON::Serializable
+
+  property xor : Int32
+end
+
+class XorEq
+  include JSON::Serializable
+
+  property xor_eq : Int32
+end
+
+class Yes
+  include JSON::Serializable
+
+  @[JSON::Field(key: "YES")]
+  property yes : Int32
+end
diff --git a/head/crystal/test/inputs/json/priority/kotlin-enum-class-case-collision.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/kotlin-enum-class-case-collision.json/default/TopLevel.cr
new file mode 100644
index 0000000..41a4e6c
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/kotlin-enum-class-case-collision.json/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property category : String
+end
diff --git a/head/crystal/test/inputs/json/priority/list.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/list.json/default/TopLevel.cr
new file mode 100644
index 0000000..af012e8
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/list.json/default/TopLevel.cr
@@ -0,0 +1,10 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Int32
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : TopLevel?
+end
diff --git a/head/crystal/test/inputs/json/priority/name-style.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/name-style.json/default/TopLevel.cr
new file mode 100644
index 0000000..098d0fc
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/name-style.json/default/TopLevel.cr
@@ -0,0 +1,25 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "anEasyOne")]
+  property an_easy_one : Int32
+
+  @[JSON::Field(key: "isMNISTLetter")]
+  property is_mnist_letter : Bool
+
+  @[JSON::Field(key: "JSONString")]
+  property json_string : String
+
+  @[JSON::Field(key: "PrettyEasyToo")]
+  property pretty_easy_too : Bool
+
+  property should_know_that_id_is_an_initialism : String
+
+  @[JSON::Field(key: "   spaces are separators  too  ")]
+  property spaces_are_separators_too : Bool
+
+  @[JSON::Field(key: "ZSR_HH_DEMO3_DELMATNR")]
+  property zsr_hh_demo3_delmatnr : Bool
+end
diff --git a/head/crystal/test/inputs/json/priority/nbl-stats.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/nbl-stats.json/default/TopLevel.cr
new file mode 100644
index 0000000..ddac0cb
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/nbl-stats.json/default/TopLevel.cr
@@ -0,0 +1,647 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property attendance : Int32
+
+  property clock : String
+
+  @[JSON::Field(key: "disableMatch")]
+  property disable_match : Int32
+
+  @[JSON::Field(key: "inOT")]
+  property in_ot : Int32
+
+  property leaddata : Array(Array(LeaddatumElement))
+
+  property officials_referee1 : String
+
+  property officials_referee2 : String
+
+  property officials_referee3 : String
+
+  property othermatches : Array(Othermatch)
+
+  property pbp : Array(Pbp)
+
+  property period : Int32
+
+  @[JSON::Field(key: "periodLengthOVERTIME")]
+  property period_length_overtime : Int32
+
+  @[JSON::Field(key: "periodLengthREGULAR")]
+  property period_length_regular : Int32
+
+  @[JSON::Field(key: "periodType")]
+  property period_type : String
+
+  @[JSON::Field(key: "periodsMax")]
+  property periods_max : Int32
+
+  property scorers : Hash(String, Array(Scorer))
+
+  property timeline : Array(JSON::Any?)
+
+  property tm : Hash(String, Tm)
+
+  @[JSON::Field(key: "totalTimeAdded")]
+  property total_time_added : Int32
+
+  property totallds : Totallds
+end
+
+class Othermatch
+  include JSON::Serializable
+
+  property clock : String
+
+  @[JSON::Field(key: "competitionName")]
+  property competition_name : String
+
+  property id : String
+
+  property period : Int32
+
+  @[JSON::Field(key: "periodType")]
+  property period_type : String
+
+  property team1 : Team
+
+  @[JSON::Field(key: "team1Name")]
+  property team1_name : String
+
+  @[JSON::Field(key: "team1Score")]
+  property team1_score : Int32
+
+  property team2 : Team
+
+  @[JSON::Field(key: "team2Name")]
+  property team2_name : String
+
+  @[JSON::Field(key: "team2Score")]
+  property team2_score : Int32
+end
+
+class Team
+  include JSON::Serializable
+
+  @[JSON::Field(key: "teamCode")]
+  property team_code : String
+
+  @[JSON::Field(key: "teamCodeInternational")]
+  property team_code_international : String
+
+  @[JSON::Field(key: "teamName")]
+  property team_name : String
+
+  @[JSON::Field(key: "teamNameInternational")]
+  property team_name_international : String
+end
+
+class Pbp
+  include JSON::Serializable
+
+  @[JSON::Field(key: "actionType")]
+  property action_type : String
+
+  @[JSON::Field(key: "familyName")]
+  property family_name : String?
+
+  @[JSON::Field(key: "familyNameInitial")]
+  property family_name_initial : String?
+
+  @[JSON::Field(key: "firstName")]
+  property first_name : String?
+
+  @[JSON::Field(key: "firstNameInitial")]
+  property first_name_initial : String?
+
+  property gt : String
+
+  @[JSON::Field(key: "internationalFamilyName")]
+  property international_family_name : String?
+
+  @[JSON::Field(key: "internationalFamilyNameInitial")]
+  property international_family_name_initial : String?
+
+  @[JSON::Field(key: "internationalFirstName")]
+  property international_first_name : String?
+
+  @[JSON::Field(key: "internationalFirstNameInitial")]
+  property international_first_name_initial : String?
+
+  property lead : Int32
+
+  property period : Int32
+
+  @[JSON::Field(key: "periodType")]
+  property period_type : String
+
+  property player : String
+
+  property pno : Int32
+
+  property qualifier : String
+
+  property s1 : Int32
+
+  property s2 : Int32
+
+  @[JSON::Field(key: "scoreboardName")]
+  property scoreboard_name : String?
+
+  property scoring : Int32
+
+  @[JSON::Field(key: "shirtNumber")]
+  property shirt_number : String
+
+  @[JSON::Field(key: "subType")]
+  property sub_type : String
+
+  property success : Int32
+
+  property tno : Int32
+end
+
+class Scorer
+  include JSON::Serializable
+
+  @[JSON::Field(key: "familyName")]
+  property family_name : String?
+
+  @[JSON::Field(key: "familyNameInitial")]
+  property family_name_initial : String?
+
+  @[JSON::Field(key: "firstName")]
+  property first_name : String?
+
+  @[JSON::Field(key: "firstNameInitial")]
+  property first_name_initial : String?
+
+  property gt : String?
+
+  @[JSON::Field(key: "internationalFamilyName")]
+  property international_family_name : String?
+
+  @[JSON::Field(key: "internationalFamilyNameInitial")]
+  property international_family_name_initial : String?
+
+  @[JSON::Field(key: "internationalFirstName")]
+  property international_first_name : String?
+
+  @[JSON::Field(key: "internationalFirstNameInitial")]
+  property international_first_name_initial : String?
+
+  property name : String?
+
+  property per : Int32?
+
+  @[JSON::Field(key: "perType")]
+  property per_type : String?
+
+  property player : String?
+
+  property pno : Int32
+
+  @[JSON::Field(key: "scoreboardName")]
+  property scoreboard_name : String?
+
+  @[JSON::Field(key: "shirtNumber")]
+  property shirt_number : String
+
+  property summary : String?
+
+  property times : Array(TimeElement)?
+
+  property tno : Int32
+
+  property tot : Int32?
+end
+
+class TimeElement
+  include JSON::Serializable
+
+  property gt : String
+
+  property per : Int32
+
+  @[JSON::Field(key: "perType")]
+  property per_type : String
+end
+
+class Tm
+  include JSON::Serializable
+
+  property code : String
+
+  @[JSON::Field(key: "codeInternational")]
+  property code_international : String
+
+  property fouls : Int32
+
+  property full_score : Int32
+
+  property lds : Lds
+
+  property logo : String
+
+  property name : String
+
+  @[JSON::Field(key: "nameInternational")]
+  property name_international : String
+
+  property p1_score : Int32
+
+  property p2_score : Int32
+
+  property p3_score : Int32
+
+  property p4_score : Int32
+
+  property pl : Hash(String, Pl)
+
+  property score : Int32
+
+  property scoring : Array(Scorer)
+
+  @[JSON::Field(key: "shortName")]
+  property short_name : String
+
+  @[JSON::Field(key: "shortNameInternational")]
+  property short_name_international : String
+
+  property shot : Array(Shot)
+
+  property timeouts : Int32
+
+  property tot_eff_1 : Int32
+
+  property tot_eff_2 : Int32
+
+  property tot_eff_3 : Float64
+
+  property tot_eff_4 : Int32
+
+  property tot_eff_5 : Int32
+
+  property tot_eff_6 : Int32
+
+  property tot_eff_7 : Int32
+
+  @[JSON::Field(key: "tot_sAssists")]
+  property tot_s_assists : Int32
+
+  @[JSON::Field(key: "tot_sBenchPoints")]
+  property tot_s_bench_points : Int32
+
+  @[JSON::Field(key: "tot_sBiggestLead")]
+  property tot_s_biggest_lead : Int32
+
+  @[JSON::Field(key: "tot_sBiggestScoringRun")]
+  property tot_s_biggest_scoring_run : Int32
+
+  @[JSON::Field(key: "tot_sBlocks")]
+  property tot_s_blocks : Int32
+
+  @[JSON::Field(key: "tot_sBlocksReceived")]
+  property tot_s_blocks_received : Int32
+
+  @[JSON::Field(key: "tot_sFieldGoalsAttempted")]
+  property tot_s_field_goals_attempted : Int32
+
+  @[JSON::Field(key: "tot_sFieldGoalsMade")]
+  property tot_s_field_goals_made : Int32
+
+  @[JSON::Field(key: "tot_sFieldGoalsPercentage")]
+  property tot_s_field_goals_percentage : Int32
+
+  @[JSON::Field(key: "tot_sFoulsOn")]
+  property tot_s_fouls_on : Int32
+
+  @[JSON::Field(key: "tot_sFoulsPersonal")]
+  property tot_s_fouls_personal : Int32
+
+  @[JSON::Field(key: "tot_sFoulsTeam")]
+  property tot_s_fouls_team : Int32
+
+  @[JSON::Field(key: "tot_sFreeThrowsAttempted")]
+  property tot_s_free_throws_attempted : Int32
+
+  @[JSON::Field(key: "tot_sFreeThrowsMade")]
+  property tot_s_free_throws_made : Int32
+
+  @[JSON::Field(key: "tot_sFreeThrowsPercentage")]
+  property tot_s_free_throws_percentage : Int32
+
+  @[JSON::Field(key: "tot_sLeadChanges")]
+  property tot_s_lead_changes : Int32
+
+  @[JSON::Field(key: "tot_sMinutes")]
+  property tot_s_minutes : Int32
+
+  @[JSON::Field(key: "tot_sPoints")]
+  property tot_s_points : Int32
+
+  @[JSON::Field(key: "tot_sPointsFastBreak")]
+  property tot_s_points_fast_break : Int32
+
+  @[JSON::Field(key: "tot_sPointsFromTurnovers")]
+  property tot_s_points_from_turnovers : Int32
+
+  @[JSON::Field(key: "tot_sPointsInThePaint")]
+  property tot_s_points_in_the_paint : Int32
+
+  @[JSON::Field(key: "tot_sPointsSecondChance")]
+  property tot_s_points_second_chance : Int32
+
+  @[JSON::Field(key: "tot_sReboundsDefensive")]
+  property tot_s_rebounds_defensive : Int32
+
+  @[JSON::Field(key: "tot_sReboundsOffensive")]
+  property tot_s_rebounds_offensive : Int32
+
+  @[JSON::Field(key: "tot_sReboundsTeam")]
+  property tot_s_rebounds_team : Int32
+
+  @[JSON::Field(key: "tot_sReboundsTeamDefensive")]
+  property tot_s_rebounds_team_defensive : Int32
+
+  @[JSON::Field(key: "tot_sReboundsTeamOffensive")]
+  property tot_s_rebounds_team_offensive : Int32
+
+  @[JSON::Field(key: "tot_sReboundsTotal")]
+  property tot_s_rebounds_total : Int32
+
+  @[JSON::Field(key: "tot_sSteals")]
+  property tot_s_steals : Int32
+
+  @[JSON::Field(key: "tot_sThreePointersAttempted")]
+  property tot_s_three_pointers_attempted : Int32
+
+  @[JSON::Field(key: "tot_sThreePointersMade")]
+  property tot_s_three_pointers_made : Int32
+
+  @[JSON::Field(key: "tot_sThreePointersPercentage")]
+  property tot_s_three_pointers_percentage : Int32
+
+  @[JSON::Field(key: "tot_sTimeLeading")]
+  property tot_s_time_leading : Float64
+
+  @[JSON::Field(key: "tot_sTimesScoresLevel")]
+  property tot_s_times_scores_level : Int32
+
+  @[JSON::Field(key: "tot_sTurnovers")]
+  property tot_s_turnovers : Int32
+
+  @[JSON::Field(key: "tot_sTurnoversTeam")]
+  property tot_s_turnovers_team : Int32
+
+  @[JSON::Field(key: "tot_sTwoPointersAttempted")]
+  property tot_s_two_pointers_attempted : Int32
+
+  @[JSON::Field(key: "tot_sTwoPointersMade")]
+  property tot_s_two_pointers_made : Int32
+
+  @[JSON::Field(key: "tot_sTwoPointersPercentage")]
+  property tot_s_two_pointers_percentage : Int32
+end
+
+class Lds
+  include JSON::Serializable
+
+  @[JSON::Field(key: "sAssists")]
+  property s_assists : Hash(String, Scorer)
+
+  @[JSON::Field(key: "sBlocks")]
+  property s_blocks : SBlocks
+
+  @[JSON::Field(key: "sPoints")]
+  property s_points : Hash(String, Scorer)
+
+  @[JSON::Field(key: "sReboundsTotal")]
+  property s_rebounds_total : Hash(String, Scorer)
+
+  @[JSON::Field(key: "sSteals")]
+  property s_steals : Hash(String, Scorer)
+end
+
+class SBlocks
+  include JSON::Serializable
+
+  @[JSON::Field(key: "1")]
+  property the_1 : Scorer
+end
+
+class Pl
+  include JSON::Serializable
+
+  property active : Int32
+
+  property captain : Int32?
+
+  property comp : Comp?
+
+  property eff_1 : Int32
+
+  property eff_2 : Int32
+
+  property eff_3 : Float64
+
+  property eff_4 : Float64
+
+  property eff_5 : Int32
+
+  property eff_6 : Int32
+
+  property eff_7 : Int32
+
+  @[JSON::Field(key: "familyName")]
+  property family_name : String
+
+  @[JSON::Field(key: "familyNameInitial")]
+  property family_name_initial : String
+
+  @[JSON::Field(key: "firstName")]
+  property first_name : String
+
+  @[JSON::Field(key: "firstNameInitial")]
+  property first_name_initial : String
+
+  @[JSON::Field(key: "internationalFamilyName")]
+  property international_family_name : String
+
+  @[JSON::Field(key: "internationalFamilyNameInitial")]
+  property international_family_name_initial : String
+
+  @[JSON::Field(key: "internationalFirstName")]
+  property international_first_name : String
+
+  @[JSON::Field(key: "internationalFirstNameInitial")]
+  property international_first_name_initial : String
+
+  property name : String
+
+  @[JSON::Field(key: "playingPosition")]
+  property playing_position : String
+
+  @[JSON::Field(key: "sAssists")]
+  property s_assists : Int32
+
+  @[JSON::Field(key: "sBlocks")]
+  property s_blocks : Int32
+
+  @[JSON::Field(key: "sBlocksReceived")]
+  property s_blocks_received : Int32
+
+  @[JSON::Field(key: "sFieldGoalsAttempted")]
+  property s_field_goals_attempted : Int32
+
+  @[JSON::Field(key: "sFieldGoalsMade")]
+  property s_field_goals_made : Int32
+
+  @[JSON::Field(key: "sFieldGoalsPercentage")]
+  property s_field_goals_percentage : Int32
+
+  @[JSON::Field(key: "sFoulsOn")]
+  property s_fouls_on : Int32
+
+  @[JSON::Field(key: "sFoulsPersonal")]
+  property s_fouls_personal : Int32
+
+  @[JSON::Field(key: "sFreeThrowsAttempted")]
+  property s_free_throws_attempted : Int32
+
+  @[JSON::Field(key: "sFreeThrowsMade")]
+  property s_free_throws_made : Int32
+
+  @[JSON::Field(key: "sFreeThrowsPercentage")]
+  property s_free_throws_percentage : Int32
+
+  @[JSON::Field(key: "sMinutes")]
+  property s_minutes : SMinutes
+
+  @[JSON::Field(key: "sPlusMinusPoints")]
+  property s_plus_minus_points : Int32
+
+  @[JSON::Field(key: "sPoints")]
+  property s_points : Int32
+
+  @[JSON::Field(key: "sPointsFastBreak")]
+  property s_points_fast_break : Int32
+
+  @[JSON::Field(key: "sPointsInThePaint")]
+  property s_points_in_the_paint : Int32
+
+  @[JSON::Field(key: "sPointsSecondChance")]
+  property s_points_second_chance : Int32
+
+  @[JSON::Field(key: "sReboundsDefensive")]
+  property s_rebounds_defensive : Int32
+
+  @[JSON::Field(key: "sReboundsOffensive")]
+  property s_rebounds_offensive : Int32
+
+  @[JSON::Field(key: "sReboundsTotal")]
+  property s_rebounds_total : Int32
+
+  @[JSON::Field(key: "sSteals")]
+  property s_steals : Int32
+
+  @[JSON::Field(key: "sThreePointersAttempted")]
+  property s_three_pointers_attempted : Int32
+
+  @[JSON::Field(key: "sThreePointersMade")]
+  property s_three_pointers_made : Int32
+
+  @[JSON::Field(key: "sThreePointersPercentage")]
+  property s_three_pointers_percentage : Int32
+
+  @[JSON::Field(key: "sTurnovers")]
+  property s_turnovers : Int32
+
+  @[JSON::Field(key: "sTwoPointersAttempted")]
+  property s_two_pointers_attempted : Int32
+
+  @[JSON::Field(key: "sTwoPointersMade")]
+  property s_two_pointers_made : Int32
+
+  @[JSON::Field(key: "sTwoPointersPercentage")]
+  property s_two_pointers_percentage : Int32
+
+  @[JSON::Field(key: "shirtNumber")]
+  property shirt_number : String
+
+  property starter : Int32
+end
+
+class Comp
+  include JSON::Serializable
+
+  @[JSON::Field(key: "sAssistsAverage")]
+  property s_assists_average : Float64
+
+  @[JSON::Field(key: "sMinutesAverage")]
+  property s_minutes_average : String
+
+  @[JSON::Field(key: "sPointsAverage")]
+  property s_points_average : Float64
+
+  @[JSON::Field(key: "sReboundsTotalAverage")]
+  property s_rebounds_total_average : Float64
+end
+
+class Shot
+  include JSON::Serializable
+
+  @[JSON::Field(key: "actionType")]
+  property action_type : String
+
+  property p : Int32
+
+  property per : Int32
+
+  @[JSON::Field(key: "perType")]
+  property per_type : String
+
+  property player : String
+
+  property pno : Int32
+
+  property r : Int32
+
+  @[JSON::Field(key: "shirtNumber")]
+  property shirt_number : String
+
+  @[JSON::Field(key: "subType")]
+  property sub_type : String
+
+  property tno : Int32
+
+  property x : Int32
+
+  property y : Int32
+end
+
+class Totallds
+  include JSON::Serializable
+
+  @[JSON::Field(key: "sAssists")]
+  property s_assists : Hash(String, Scorer)
+
+  @[JSON::Field(key: "sBlocks")]
+  property s_blocks : Hash(String, Scorer)
+
+  @[JSON::Field(key: "sPoints")]
+  property s_points : Hash(String, Scorer)
+
+  @[JSON::Field(key: "sReboundsTotal")]
+  property s_rebounds_total : Hash(String, Scorer)
+
+  @[JSON::Field(key: "sSteals")]
+  property s_steals : Hash(String, Scorer)
+end
+
+alias LeaddatumElement = Int32 | String
+
+alias SMinutes = Int32 | String
diff --git a/head/crystal/test/inputs/json/priority/nested-objects.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/nested-objects.json/default/TopLevel.cr
new file mode 100644
index 0000000..28cf867
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/nested-objects.json/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property a : Hash(String, Int32)
+
+  property b : Hash(String, Int32)
+end
diff --git a/head/crystal/test/inputs/json/priority/no-classes.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/no-classes.json/default/TopLevel.cr
new file mode 100644
index 0000000..d47e0bf
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/no-classes.json/default/TopLevel.cr
@@ -0,0 +1,3 @@
+require "json"
+
+alias TopLevel = Int32
diff --git a/head/crystal/test/inputs/json/priority/number-map.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/number-map.json/default/TopLevel.cr
new file mode 100644
index 0000000..84f5417
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/number-map.json/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Hash(String, Float64)
+end
diff --git a/head/crystal/test/inputs/json/priority/omit-empty.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/omit-empty.json/default/TopLevel.cr
new file mode 100644
index 0000000..d1e7044
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/omit-empty.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property results : Array(Result)
+end
+
+class Result
+  include JSON::Serializable
+
+  property age : Int32
+
+  property name : Nil
+end
diff --git a/head/crystal/test/inputs/json/priority/optional-union.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/optional-union.json/default/TopLevel.cr
new file mode 100644
index 0000000..238db97
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/optional-union.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property a : A?
+end
+
+alias A = Int32 | String
diff --git a/head/crystal/test/inputs/json/priority/php-mixed-union.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/php-mixed-union.json/default/TopLevel.cr
new file mode 100644
index 0000000..4c9d8fc
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/php-mixed-union.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property mixed : Array(MixedElement?)
+end
+
+class MixedClass
+  include JSON::Serializable
+
+  property nested : String
+end
+
+alias MixedElement = Int32 | MixedClass | Bool | String
diff --git a/head/crystal/test/inputs/json/priority/php-validation.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/php-validation.json/default/TopLevel.cr
new file mode 100644
index 0000000..1bbf448
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/php-validation.json/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property boolean : Bool
+
+  property integer : Int32
+
+  property integers : Array(Int32)
+
+  property number : Float64
+
+  property string : String
+end
diff --git a/head/crystal/test/inputs/json/priority/recursive.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/recursive.json/default/TopLevel.cr
new file mode 100644
index 0000000..3de7f98
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/recursive.json/default/TopLevel.cr
@@ -0,0 +1,214 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property category : Category
+
+  property details : Details
+
+  property match : String
+
+  property page : Int32
+
+  property product : Array(ProductElement)
+
+  property schk : Bool
+
+  property totallooseoffers : Int32
+
+  property totalpages : Int32
+
+  property totalresultsavailable : Int32
+
+  property totalresultsreturned : Int32
+end
+
+class Category
+  include JSON::Serializable
+
+  property concatenatecategoryname : Bool
+
+  property hasoffer : Bool
+
+  property id : Int32
+
+  property isfinal : Bool
+
+  property links : Array(LinkElement)
+
+  property name : String
+
+  property parentcategoryid : Int32
+
+  property thumbnail : CategoryThumbnail
+end
+
+class LinkElement
+  include JSON::Serializable
+
+  property link : LinkLink
+end
+
+class LinkLink
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property link_type : String
+
+  property url : String
+end
+
+class CategoryThumbnail
+  include JSON::Serializable
+
+  property url : String
+end
+
+class Details
+  include JSON::Serializable
+
+  property applicationid : String
+
+  property applicationversion : String
+
+  property code : Int32
+
+  property date : Date
+
+  property elapsedtime : Int32
+
+  property message : String
+
+  property status : String
+end
+
+class Date
+  include JSON::Serializable
+
+  property day : Int32
+
+  property eonandyear : Eonandyear
+
+  property hour : Int32
+
+  property millisecond : Int32
+
+  property minute : Int32
+
+  property month : Int32
+
+  property second : Int32
+
+  property timezone : Int32
+
+  property valid : Bool
+
+  property xmlschematype : Xmlschematype
+
+  property year : Int32
+end
+
+class Eonandyear
+  include JSON::Serializable
+
+  property lowestsetbit : Int32
+end
+
+class Xmlschematype
+  include JSON::Serializable
+
+  property localpart : String
+
+  property namespaceuri : String
+
+  property prefix : String
+end
+
+class ProductElement
+  include JSON::Serializable
+
+  property product : ProductProduct
+end
+
+class ProductProduct
+  include JSON::Serializable
+
+  property categoryid : Int32
+
+  property currency : Currency
+
+  property eco : Bool
+
+  property fulldescription : Bool
+
+  property hasmetasearch : Bool
+
+  property id : Int32
+
+  property links : Array(LinkElement)
+
+  property numoffers : Int32
+
+  property pricemax : String
+
+  property pricemin : String
+
+  property productname : String
+
+  property quantity : Int32
+
+  property rating : Rating
+
+  property specification : Specification
+
+  property thumbnail : FormatsClass
+
+  property totalsellers : Int32
+end
+
+class Currency
+  include JSON::Serializable
+
+  property abbreviation : String
+end
+
+class Rating
+  include JSON::Serializable
+
+  property useraveragerating : Useraveragerating
+end
+
+class Useraveragerating
+  include JSON::Serializable
+
+  property links : Array(LinkElement)
+
+  property numcomments : Int32
+
+  property rating : String
+end
+
+class Specification
+  include JSON::Serializable
+
+  property links : Array(LinkElement)
+end
+
+class Format
+  include JSON::Serializable
+
+  property formats : FormatsClass
+end
+
+class FormatsClass
+  include JSON::Serializable
+
+  property formats : Array(Format)?
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
diff --git a/head/crystal/test/inputs/json/priority/union-constructor-clash.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/union-constructor-clash.json/default/TopLevel.cr
new file mode 100644
index 0000000..da268fe
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/union-constructor-clash.json/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "BoolInUnion")]
+  property bool_in_union : BoolInUnion
+
+  @[JSON::Field(key: "union")]
+  property top_level_union : Array(UnionElement)
+end
+
+class BoolInUnion
+  include JSON::Serializable
+
+  property a : Bool
+end
+
+alias UnionElement = Int32 | Bool
diff --git a/head/crystal/test/inputs/json/priority/unions.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/unions.json/default/TopLevel.cr
new file mode 100644
index 0000000..cb5fd12
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/unions.json/default/TopLevel.cr
@@ -0,0 +1,21 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property x : Array(X?)
+
+  property y : Array(Int32?)
+
+  property z : Array(ZElement)
+end
+
+class ZClass
+  include JSON::Serializable
+
+  property a : Bool
+end
+
+alias X = Int32 | String
+
+alias ZElement = Int32 | Array(Int32) | ZClass
diff --git a/head/crystal/test/inputs/json/priority/url.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/url.json/default/TopLevel.cr
new file mode 100644
index 0000000..49f380a
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/url.json/default/TopLevel.cr
@@ -0,0 +1,10 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "fromURL")]
+  property from_url : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/priority/uuids.json/default/TopLevel.cr b/head/crystal/test/inputs/json/priority/uuids.json/default/TopLevel.cr
new file mode 100644
index 0000000..8994464
--- /dev/null
+++ b/head/crystal/test/inputs/json/priority/uuids.json/default/TopLevel.cr
@@ -0,0 +1,23 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "booleanValue")]
+  property boolean_value : Bool
+
+  @[JSON::Field(key: "doubleValue")]
+  property double_value : Float64
+
+  @[JSON::Field(key: "intValue")]
+  property int_value : Int32
+
+  @[JSON::Field(key: "stringValue")]
+  property string_value : String
+
+  @[JSON::Field(key: "uuidValue")]
+  property uuid_value : String
+
+  @[JSON::Field(key: "uuidValues")]
+  property uuid_values : Array(String)
+end
diff --git a/head/crystal/test/inputs/json/samples/bitcoin-block.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/bitcoin-block.json/default/TopLevel.cr
new file mode 100644
index 0000000..2ae8da8
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/bitcoin-block.json/default/TopLevel.cr
@@ -0,0 +1,16 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property block_index : Int32
+
+  property hash : String
+
+  property height : Int32
+
+  property time : Int32
+
+  @[JSON::Field(key: "txIndexes")]
+  property tx_indexes : Array(Int32)
+end
diff --git a/head/crystal/test/inputs/json/samples/getting-started.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/getting-started.json/default/TopLevel.cr
new file mode 100644
index 0000000..ec0464a
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/getting-started.json/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property greeting : String
+
+  property instructions : Array(String)
+end
diff --git a/head/crystal/test/inputs/json/samples/github-events.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/github-events.json/default/TopLevel.cr
new file mode 100644
index 0000000..0995433
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/github-events.json/default/TopLevel.cr
@@ -0,0 +1,546 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property actor : Actor
+
+  property created_at : String
+
+  property id : String
+
+  property org : Actor?
+
+  property payload : Payload
+
+  property public : Bool
+
+  property repo : TopLevelRepo
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+end
+
+class Actor
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property display_login : String?
+
+  property gravatar_id : String
+
+  property id : Int32
+
+  property login : String
+
+  property url : String
+end
+
+class Payload
+  include JSON::Serializable
+
+  property action : String?
+
+  property before : String?
+
+  property comment : Comment?
+
+  property commits : Array(Commit)?
+
+  property description : String?
+
+  property distinct_size : Int32?
+
+  property head : String?
+
+  property issue : Issue?
+
+  property master_branch : String?
+
+  property number : Int32?
+
+  property pull_request : PayloadPullRequest?
+
+  property push_id : Int32?
+
+  property pusher_type : String?
+
+  property ref : String?
+
+  property ref_type : String?
+
+  property size : Int32?
+end
+
+class Comment
+  include JSON::Serializable
+
+  property body : String
+
+  property created_at : String
+
+  property html_url : String
+
+  property id : Int32
+
+  property issue_url : String
+
+  property updated_at : String
+
+  property url : String
+
+  property user : User
+end
+
+class User
+  include JSON::Serializable
+
+  property avatar_url : String
+
+  property events_url : String
+
+  property followers_url : String
+
+  property following_url : String
+
+  property gists_url : String
+
+  property gravatar_id : String
+
+  property html_url : String
+
+  property id : Int32
+
+  property login : String
+
+  property organizations_url : String
+
+  property received_events_url : String
+
+  property repos_url : String
+
+  property site_admin : Bool
+
+  property starred_url : String
+
+  property subscriptions_url : String
+
+  property url : String
+
+  @[JSON::Field(key: "type")]
+  property user_type : String
+end
+
+class Commit
+  include JSON::Serializable
+
+  property author : Author
+
+  property distinct : Bool
+
+  property message : String
+
+  property sha : String
+
+  property url : String
+end
+
+class Author
+  include JSON::Serializable
+
+  property email : String
+
+  property name : String
+end
+
+class Issue
+  include JSON::Serializable
+
+  property assignee : Nil
+
+  property assignees : Array(JSON::Any?)
+
+  property body : String
+
+  property closed_at : String?
+
+  property comments : Int32
+
+  property comments_url : String
+
+  property created_at : String
+
+  property events_url : String
+
+  property html_url : String
+
+  property id : Int32
+
+  property labels : Array(Label)
+
+  property labels_url : String
+
+  property locked : Bool
+
+  property milestone : Milestone?
+
+  property number : Int32
+
+  property pull_request : IssuePullRequest?
+
+  property repository_url : String
+
+  property state : String
+
+  property title : String
+
+  property updated_at : String
+
+  property url : String
+
+  property user : User
+end
+
+class Label
+  include JSON::Serializable
+
+  property color : String
+
+  property default : Bool
+
+  property id : Int32
+
+  property name : String
+
+  property url : String
+end
+
+class Milestone
+  include JSON::Serializable
+
+  property closed_at : Nil
+
+  property closed_issues : Int32
+
+  property created_at : String
+
+  property creator : User
+
+  property description : String
+
+  property due_on : String?
+
+  property html_url : String
+
+  property id : Int32
+
+  property labels_url : String
+
+  property number : Int32
+
+  property open_issues : Int32
+
+  property state : String
+
+  property title : String
+
+  property updated_at : String
+
+  property url : String
+end
+
+class IssuePullRequest
+  include JSON::Serializable
+
+  property diff_url : String
+
+  property html_url : String
+
+  property patch_url : String
+
+  property url : String
+end
+
+class PayloadPullRequest
+  include JSON::Serializable
+
+  property additions : Int32
+
+  property assignee : Nil
+
+  property assignees : Array(JSON::Any?)
+
+  property base : Base
+
+  property body : String
+
+  property changed_files : Int32
+
+  property closed_at : String
+
+  property comments : Int32
+
+  property comments_url : String
+
+  property commits : Int32
+
+  property commits_url : String
+
+  property created_at : String
+
+  property deletions : Int32
+
+  property diff_url : String
+
+  property head : Base
+
+  property html_url : String
+
+  property id : Int32
+
+  property issue_url : String
+
+  @[JSON::Field(key: "_links")]
+  property links : Links
+
+  property locked : Bool
+
+  property maintainer_can_modify : Bool
+
+  property merge_commit_sha : String
+
+  property mergeable : Nil
+
+  property mergeable_state : String
+
+  property merged : Bool
+
+  property merged_at : String
+
+  property merged_by : User
+
+  property milestone : Nil
+
+  property number : Int32
+
+  property patch_url : String
+
+  property rebaseable : Nil
+
+  property requested_reviewers : Array(JSON::Any?)
+
+  property review_comment_url : String
+
+  property review_comments : Int32
+
+  property review_comments_url : String
+
+  property state : String
+
+  property statuses_url : String
+
+  property title : String
+
+  property updated_at : String
+
+  property url : String
+
+  property user : User
+end
+
+class Base
+  include JSON::Serializable
+
+  property label : String
+
+  property ref : String
+
+  property repo : BaseRepo
+
+  property sha : String
+
+  property user : User
+end
+
+class BaseRepo
+  include JSON::Serializable
+
+  property archive_url : String
+
+  property assignees_url : String
+
+  property blobs_url : String
+
+  property branches_url : String
+
+  property clone_url : String
+
+  property collaborators_url : String
+
+  property comments_url : String
+
+  property commits_url : String
+
+  property compare_url : String
+
+  property contents_url : String
+
+  property contributors_url : String
+
+  property created_at : String
+
+  property default_branch : String
+
+  property deployments_url : String
+
+  property description : String
+
+  property downloads_url : String
+
+  property events_url : String
+
+  property fork : Bool
+
+  property forks : Int32
+
+  property forks_count : Int32
+
+  property forks_url : String
+
+  property full_name : String
+
+  property git_commits_url : String
+
+  property git_refs_url : String
+
+  property git_tags_url : String
+
+  property git_url : String
+
+  property has_downloads : Bool
+
+  property has_issues : Bool
+
+  property has_pages : Bool
+
+  property has_projects : Bool
+
+  property has_wiki : Bool
+
+  property homepage : String
+
+  property hooks_url : String
+
+  property html_url : String
+
+  property id : Int32
+
+  property issue_comment_url : String
+
+  property issue_events_url : String
+
+  property issues_url : String
+
+  property keys_url : String
+
+  property labels_url : String
+
+  property language : String
+
+  property languages_url : String
+
+  property merges_url : String
+
+  property milestones_url : String
+
+  property mirror_url : Nil
+
+  property name : String
+
+  property notifications_url : String
+
+  property open_issues : Int32
+
+  property open_issues_count : Int32
+
+  property owner : User
+
+  property pulls_url : String
+
+  property pushed_at : String
+
+  property releases_url : String
+
+  @[JSON::Field(key: "private")]
+  property repo_private : Bool
+
+  property size : Int32
+
+  property ssh_url : String
+
+  property stargazers_count : Int32
+
+  property stargazers_url : String
+
+  property statuses_url : String
+
+  property subscribers_url : String
+
+  property subscription_url : String
+
+  property svn_url : String
+
+  property tags_url : String
+
+  property teams_url : String
+
+  property trees_url : String
+
+  property updated_at : String
+
+  property url : String
+
+  property watchers : Int32
+
+  property watchers_count : Int32
+end
+
+class Links
+  include JSON::Serializable
+
+  property comments : Comments
+
+  property commits : Comments
+
+  property html : Comments
+
+  property issue : Comments
+
+  @[JSON::Field(key: "self")]
+  property links_self : Comments
+
+  property review_comment : Comments
+
+  property review_comments : Comments
+
+  property statuses : Comments
+end
+
+class Comments
+  include JSON::Serializable
+
+  property href : String
+end
+
+class TopLevelRepo
+  include JSON::Serializable
+
+  property id : Int32
+
+  property name : String
+
+  property url : String
+end
diff --git a/head/crystal/test/inputs/json/samples/kitchen-sink.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/kitchen-sink.json/default/TopLevel.cr
new file mode 100644
index 0000000..67a3e26
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/kitchen-sink.json/default/TopLevel.cr
@@ -0,0 +1,75 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "booleanValue")]
+  property boolean_value : Bool
+
+  @[JSON::Field(key: "dateValue")]
+  property date_value : String
+
+  @[JSON::Field(key: "differentThings")]
+  property different_things : Array(DifferentThingElement)
+
+  @[JSON::Field(key: "doubleValue")]
+  property double_value : Float64
+
+  @[JSON::Field(key: "intValue")]
+  property int_value : Int32
+
+  @[JSON::Field(key: "mapValue")]
+  property map_value : Hash(String, Int32?)
+
+  @[JSON::Field(key: "name with spaces")]
+  property name_with_spaces : Nil
+
+  @[JSON::Field(key: "nullValue")]
+  property null_value : Nil
+
+  @[JSON::Field(key: "nullableMapValue")]
+  property nullable_map_value : Hash(String, Int32?)
+
+  property people : Array(PersonElement)
+
+  property person : PurplePerson
+
+  @[JSON::Field(key: "stringValue")]
+  property string_value : String
+
+  property tuples : Array(Array(IntOrString))
+
+  @[JSON::Field(key: "uuidValue")]
+  property uuid_value : String
+end
+
+class DifferentThingClass
+  include JSON::Serializable
+
+  property name : String
+end
+
+class PersonElement
+  include JSON::Serializable
+
+  @[JSON::Field(key: "intOrString")]
+  property int_or_string : IntOrString
+
+  property name : String
+
+  @[JSON::Field(key: "optionalValue")]
+  property optional_value : Bool?
+end
+
+class PurplePerson
+  include JSON::Serializable
+
+  @[JSON::Field(key: "intOrString")]
+  property int_or_string : Int32
+
+  property name : String
+end
+
+alias DifferentThingElement = DifferentThingClass | Int32 | String
+
+alias IntOrString = Int32 | String
diff --git a/head/crystal/test/inputs/json/samples/null-safe.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/null-safe.json/default/TopLevel.cr
new file mode 100644
index 0000000..7ecc9e4
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/null-safe.json/default/TopLevel.cr
@@ -0,0 +1,29 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property item : Array(Item)
+end
+
+class Item
+  include JSON::Serializable
+
+  property address : Address?
+
+  property created_at : String?
+
+  property name : String
+
+  property sex : Int32?
+end
+
+class Address
+  include JSON::Serializable
+
+  property city : String
+
+  property country : String
+
+  property street : String
+end
diff --git a/head/crystal/test/inputs/json/samples/pokedex.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/pokedex.json/default/TopLevel.cr
new file mode 100644
index 0000000..50f4516
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/pokedex.json/default/TopLevel.cr
@@ -0,0 +1,54 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property pokemon : Array(Pokemon)
+end
+
+class Pokemon
+  include JSON::Serializable
+
+  property avg_spawns : Float64
+
+  property candy : String
+
+  property candy_count : Int32?
+
+  property egg : String
+
+  property height : String
+
+  property id : Int32
+
+  property img : String
+
+  property multipliers : Array(Float64)?
+
+  property name : String
+
+  property next_evolution : Array(Evolution)?
+
+  property num : String
+
+  @[JSON::Field(key: "type")]
+  property pokemon_type : Array(String)
+
+  property prev_evolution : Array(Evolution)?
+
+  property spawn_chance : Float64
+
+  property spawn_time : String
+
+  property weaknesses : Array(String)
+
+  property weight : String
+end
+
+class Evolution
+  include JSON::Serializable
+
+  property name : String
+
+  property num : String
+end
diff --git a/head/crystal/test/inputs/json/samples/reddit.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/reddit.json/default/TopLevel.cr
new file mode 100644
index 0000000..3edffc8
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/reddit.json/default/TopLevel.cr
@@ -0,0 +1,216 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : TopLevelData
+
+  property kind : String
+end
+
+class TopLevelData
+  include JSON::Serializable
+
+  property after : String
+
+  property before : Nil
+
+  property children : Array(Child)
+
+  property modhash : String
+end
+
+class Child
+  include JSON::Serializable
+
+  property data : ChildData
+
+  property kind : String
+end
+
+class ChildData
+  include JSON::Serializable
+
+  property approved_at_utc : Nil
+
+  property approved_by : Nil
+
+  property archived : Bool
+
+  property author : String
+
+  property author_flair_css_class : String?
+
+  property author_flair_text : String?
+
+  property banned_at_utc : Nil
+
+  property banned_by : Nil
+
+  property brand_safe : Bool
+
+  property can_gild : Bool
+
+  property can_mod_post : Bool
+
+  property clicked : Bool
+
+  property contest_mode : Bool
+
+  property created : Int32
+
+  property created_utc : Int32
+
+  property distinguished : String?
+
+  property domain : String
+
+  property downs : Int32
+
+  property edited : Bool
+
+  property gilded : Int32
+
+  property hidden : Bool
+
+  property hide_score : Bool
+
+  property id : String
+
+  property is_self : Bool
+
+  property is_video : Bool
+
+  property likes : Nil
+
+  property link_flair_css_class : String?
+
+  property link_flair_text : String?
+
+  property locked : Bool
+
+  property media : Nil
+
+  property media_embed : MediaEmbed
+
+  property mod_reports : Array(JSON::Any?)
+
+  property name : String
+
+  property num_comments : Int32
+
+  property num_reports : Nil
+
+  property over_18 : Bool
+
+  property parent_whitelist_status : String
+
+  property permalink : String
+
+  property post_hint : String
+
+  property preview : Preview
+
+  property quarantine : Bool
+
+  property removal_reason : Nil
+
+  property report_reasons : Nil
+
+  property saved : Bool
+
+  property score : Int32
+
+  property secure_media : Nil
+
+  property secure_media_embed : MediaEmbed
+
+  property selftext : String
+
+  property selftext_html : Nil
+
+  property spoiler : Bool
+
+  property stickied : Bool
+
+  property subreddit : String
+
+  property subreddit_id : String
+
+  property subreddit_name_prefixed : String
+
+  property subreddit_type : String
+
+  property suggested_sort : Nil
+
+  property thumbnail : String
+
+  property thumbnail_height : Int32
+
+  property thumbnail_width : Int32
+
+  property title : String
+
+  property ups : Int32
+
+  property url : String
+
+  property user_reports : Array(JSON::Any?)
+
+  property view_count : Nil
+
+  property visited : Bool
+
+  property whitelist_status : String
+end
+
+class MediaEmbed
+  include JSON::Serializable
+
+end
+
+class Preview
+  include JSON::Serializable
+
+  property enabled : Bool
+
+  property images : Array(Image)
+end
+
+class Image
+  include JSON::Serializable
+
+  property id : String
+
+  property resolutions : Array(Source)
+
+  property source : Source
+
+  property variants : Variants
+end
+
+class Source
+  include JSON::Serializable
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
+
+class Variants
+  include JSON::Serializable
+
+  property gif : Gif?
+
+  property mp4 : Gif?
+end
+
+class Gif
+  include JSON::Serializable
+
+  property resolutions : Array(Source)
+
+  property source : Source
+end
diff --git a/head/crystal/test/inputs/json/samples/simple-object.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/simple-object.json/default/TopLevel.cr
new file mode 100644
index 0000000..5147fab
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/simple-object.json/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property date : Int32
+
+  property title : String
+
+  property validity : Bool
+end
diff --git a/head/crystal/test/inputs/json/samples/spotify-album.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/spotify-album.json/default/TopLevel.cr
new file mode 100644
index 0000000..c4f12e6
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/spotify-album.json/default/TopLevel.cr
@@ -0,0 +1,138 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property album_type : String
+
+  property artists : Array(Artist)
+
+  property available_markets : Array(String)
+
+  property copyrights : Array(Copyright)
+
+  property external_ids : ExternalIds
+
+  property external_urls : ExternalUrls
+
+  property genres : Array(JSON::Any?)
+
+  property href : String
+
+  property id : String
+
+  property images : Array(Image)
+
+  property name : String
+
+  property popularity : Int32
+
+  property release_date : String
+
+  property release_date_precision : String
+
+  @[JSON::Field(key: "type")]
+  property top_level_type : String
+
+  property tracks : Tracks
+
+  property uri : String
+end
+
+class Artist
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property artist_type : String
+
+  property external_urls : ExternalUrls
+
+  property href : String
+
+  property id : String
+
+  property name : String
+
+  property uri : String
+end
+
+class ExternalUrls
+  include JSON::Serializable
+
+  property spotify : String
+end
+
+class Copyright
+  include JSON::Serializable
+
+  @[JSON::Field(key: "type")]
+  property copyright_type : String
+
+  property text : String
+end
+
+class ExternalIds
+  include JSON::Serializable
+
+  property upc : String
+end
+
+class Image
+  include JSON::Serializable
+
+  property height : Int32
+
+  property url : String
+
+  property width : Int32
+end
+
+class Tracks
+  include JSON::Serializable
+
+  property href : String
+
+  property items : Array(Item)
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property previous : Nil
+
+  property total : Int32
+
+  @[JSON::Field(key: "next")]
+  property tracks_next : Nil
+end
+
+class Item
+  include JSON::Serializable
+
+  property artists : Array(Artist)
+
+  property available_markets : Array(String)
+
+  property disc_number : Int32
+
+  property duration_ms : Int32
+
+  property explicit : Bool
+
+  property external_urls : ExternalUrls
+
+  property href : String
+
+  property id : String
+
+  @[JSON::Field(key: "type")]
+  property item_type : String
+
+  property name : String
+
+  property preview_url : String
+
+  property track_number : Int32
+
+  property uri : String
+end
diff --git a/head/crystal/test/inputs/json/samples/us-avg-temperatures.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/us-avg-temperatures.json/default/TopLevel.cr
new file mode 100644
index 0000000..c658019
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/us-avg-temperatures.json/default/TopLevel.cr
@@ -0,0 +1,29 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property data : Hash(String, Datum)
+
+  property description : Description
+end
+
+class Datum
+  include JSON::Serializable
+
+  property anomaly : String
+
+  property value : String
+end
+
+class Description
+  include JSON::Serializable
+
+  property base_period : String
+
+  property missing : Int32
+
+  property title : String
+
+  property units : String
+end
diff --git a/head/crystal/test/inputs/json/samples/us-senators.json/default/TopLevel.cr b/head/crystal/test/inputs/json/samples/us-senators.json/default/TopLevel.cr
new file mode 100644
index 0000000..691f349
--- /dev/null
+++ b/head/crystal/test/inputs/json/samples/us-senators.json/default/TopLevel.cr
@@ -0,0 +1,119 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property meta : Meta
+
+  property objects : Array(ObjectElement)
+end
+
+class Meta
+  include JSON::Serializable
+
+  property limit : Int32
+
+  property offset : Int32
+
+  property total_count : Int32
+end
+
+class ObjectElement
+  include JSON::Serializable
+
+  property caucus : String?
+
+  property congress_numbers : Array(Int32)
+
+  property current : Bool
+
+  property description : String
+
+  property district : Nil
+
+  property enddate : String
+
+  property extra : Extra
+
+  property leadership_title : String?
+
+  property party : String
+
+  property person : Person
+
+  property phone : String
+
+  property role_type : String
+
+  property role_type_label : String
+
+  property senator_class : String
+
+  property senator_class_label : String
+
+  property senator_rank : String
+
+  property senator_rank_label : String
+
+  property startdate : String
+
+  property state : String
+
+  property title : String
+
+  property title_long : String
+
+  property website : String
+end
+
+class Extra
+  include JSON::Serializable
+
+  property address : String
+
+  property contact_form : String
+
+  property fax : String?
+
+  property office : String
+
+  property rss_url : String?
+end
+
+class Person
+  include JSON::Serializable
+
+  property bioguideid : String
+
+  property birthday : String
+
+  property cspanid : Int32
+
+  property firstname : String
+
+  property gender : String
+
+  property gender_label : String
+
+  property lastname : String
+
+  property link : String
+
+  property middlename : String
+
+  property name : String
+
+  property namemod : String
+
+  property nickname : String
+
+  property osid : String
+
+  property pvsid : String
+
+  property sortname : String
+
+  property twitterid : String?
+
+  property youtubeid : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/accessors.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/accessors.schema/default/TopLevel.cr
new file mode 100644
index 0000000..7353e93
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/accessors.schema/default/TopLevel.cr
@@ -0,0 +1,18 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "bar")]
+  property barre : String
+
+  @[JSON::Field(key: "enum")]
+  property enumerification : String
+
+  property foo : String
+
+  @[JSON::Field(key: "union")]
+  property unionization : UnionUnion
+end
+
+alias UnionUnion = Float64 | Bool
diff --git a/head/schema-crystal/test/inputs/schema/any.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/any.schema/default/TopLevel.cr
new file mode 100644
index 0000000..c86458c
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/any.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : JSON::Any?
+
+  property values : Hash(String, JSON::Any?)
+end
diff --git a/head/schema-crystal/test/inputs/schema/bool-string.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/bool-string.schema/default/TopLevel.cr
new file mode 100644
index 0000000..3514cb4
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/bool-string.schema/default/TopLevel.cr
@@ -0,0 +1,25 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "arrNullable")]
+  property arr_nullable : Array(String?)?
+
+  @[JSON::Field(key: "arrOne")]
+  property arr_one : Array(String)?
+
+  property nullable : String?
+
+  property one : String
+
+  property optional : String?
+
+  @[JSON::Field(key: "unionWithBool")]
+  property union_with_bool : UnionWithBool
+
+  @[JSON::Field(key: "unionWithBoolAndEnum")]
+  property union_with_bool_and_enum : UnionWithBool
+end
+
+alias UnionWithBool = Bool | String
diff --git a/head/schema-crystal/test/inputs/schema/boolean-subschema.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/boolean-subschema.schema/default/TopLevel.cr
new file mode 100644
index 0000000..a844cb0
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/boolean-subschema.schema/default/TopLevel.cr
@@ -0,0 +1,13 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property disallowed : JSON::Any?
+
+  property empty : Array(JSON::Any?)
+
+  property foo : String
+
+  property impossible : JSON::Any?
+end
diff --git a/head/schema-crystal/test/inputs/schema/camelCase.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/camelCase.schema/default/TopLevel.cr
new file mode 100644
index 0000000..219225b
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/camelCase.schema/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "myProperty")]
+  property my_property : String?
+
+  @[JSON::Field(key: "secondProperty")]
+  property second_property : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/class-map-union.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/class-map-union.schema/default/TopLevel.cr
new file mode 100644
index 0000000..b5ff3d7
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/class-map-union.schema/default/TopLevel.cr
@@ -0,0 +1,16 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "union")]
+  property top_level_union : Hash(String, UnionValue)?
+end
+
+class UnionClass
+  include JSON::Serializable
+
+  property quux : Int32?
+end
+
+alias UnionValue = Float64 | Bool | String | UnionClass
diff --git a/head/schema-crystal/test/inputs/schema/class-with-additional.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/class-with-additional.schema/default/TopLevel.cr
new file mode 100644
index 0000000..2ccf680
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/class-with-additional.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property map : Hash(String, Map)?
+end
+
+alias Map = Float64 | Bool
diff --git a/head/schema-crystal/test/inputs/schema/comment-injection-enum-nested-comment.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/comment-injection-enum-nested-comment.schema/default/TopLevel.cr
new file mode 100644
index 0000000..b243f54
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/comment-injection-enum-nested-comment.schema/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  # Nested enum block comment openers:
+  # {-
+  # /*
+  # This text must remain inside the generated enum documentation comment.
+  property kind : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/comment-injection-enum.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/comment-injection-enum.schema/default/TopLevel.cr
new file mode 100644
index 0000000..f713b81
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/comment-injection-enum.schema/default/TopLevel.cr
@@ -0,0 +1,14 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  # Comment delimiters:
+  # */
+  # {-
+  # -}
+  # """
+  # }
+  # }
+  property kind : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/comment-injection-nested-comment.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/comment-injection-nested-comment.schema/default/TopLevel.cr
new file mode 100644
index 0000000..2483921
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/comment-injection-nested-comment.schema/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+# Nested block comment openers:
+# {-
+# /*
+# This text must remain inside the generated documentation comment.
+class TopLevel
+  include JSON::Serializable
+
+  # Nested property block comment openers:
+  # {-
+  # /*
+  # This text must remain inside the generated property documentation comment.
+  property value : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/comment-injection.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/comment-injection.schema/default/TopLevel.cr
new file mode 100644
index 0000000..9ec5b9d
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/comment-injection.schema/default/TopLevel.cr
@@ -0,0 +1,43 @@
+require "json"
+
+# Comment delimiters:
+# */
+# /*
+# {-
+# -}
+# """
+# </summary> & <br>
+# }
+# }
+# }
+# }
+# }
+class TopLevel
+  include JSON::Serializable
+
+  # Ends with a backslash \
+  @[JSON::Field(key: "trailingBackslash")]
+  property trailing_backslash : String?
+
+  # Ends with a quote "
+  @[JSON::Field(key: "trailingQuote")]
+  property trailing_quote : String?
+
+  # Ends with a triple quote """
+  @[JSON::Field(key: "trailingTripleQuote")]
+  property trailing_triple_quote : String?
+
+  # Property delimiters:
+  # */
+  # /*
+  # {-
+  # -}
+  # """
+  # </summary> & <br>
+  # }
+  # }
+  # }
+  # }
+  # }
+  property value : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/constructor.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/constructor.schema/default/TopLevel.cr
new file mode 100644
index 0000000..cea8515
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/constructor.schema/default/TopLevel.cr
@@ -0,0 +1,14 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property constructor : UnionConstructor?
+end
+
+class Constructor
+  include JSON::Serializable
+
+end
+
+alias UnionConstructor = Constructor | Float64
diff --git a/head/schema-crystal/test/inputs/schema/cut-enum.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/cut-enum.schema/default/TopLevel.cr
new file mode 100644
index 0000000..ed03ea1
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/cut-enum.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Int32
+end
diff --git a/head/schema-crystal/test/inputs/schema/date-time-or-string.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/date-time-or-string.schema/default/TopLevel.cr
new file mode 100644
index 0000000..e21115f
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/date-time-or-string.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property bar : String
+
+  property foo : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/date-time.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/date-time.schema/default/TopLevel.cr
new file mode 100644
index 0000000..c145352
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/date-time.schema/default/TopLevel.cr
@@ -0,0 +1,20 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "complex-union-array")]
+  property complex_union_array : Array(ComplexUnionArray)
+
+  property date : String
+
+  @[JSON::Field(key: "date-time")]
+  property date_time : String
+
+  property time : String
+
+  @[JSON::Field(key: "union-array")]
+  property union_array : Array(String)
+end
+
+alias ComplexUnionArray = Int32 | String
diff --git a/head/schema-crystal/test/inputs/schema/default-value.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/default-value.schema/default/TopLevel.cr
new file mode 100644
index 0000000..884f630
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/default-value.schema/default/TopLevel.cr
@@ -0,0 +1,8 @@
+require "json"
+
+# An object with a scalar default
+class TopLevel
+  include JSON::Serializable
+
+  property id : Int32
+end
diff --git a/head/schema-crystal/test/inputs/schema/description-with-double-quotes.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/description-with-double-quotes.schema/default/TopLevel.cr
new file mode 100644
index 0000000..023f928
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/description-with-double-quotes.schema/default/TopLevel.cr
@@ -0,0 +1,8 @@
+require "json"
+
+# Cool "stuff"
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Bool?
+end
diff --git a/head/schema-crystal/test/inputs/schema/description.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/description.schema/default/TopLevel.cr
new file mode 100644
index 0000000..3db424e
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/description.schema/default/TopLevel.cr
@@ -0,0 +1,35 @@
+require "json"
+
+# The top-level class.
+# Its description has two lines.
+class TopLevel
+  include JSON::Serializable
+
+  # A pretty boolean
+  property bar : Bool?
+
+  # An enumeration
+  @[JSON::Field(key: "enum")]
+  property top_level_enum : String
+
+  property foo : Float64?
+
+  @[JSON::Field(key: "object-or-string")]
+  property object_or_string : ObjectOrStringUnion
+
+  # Either a number or a string
+  @[JSON::Field(key: "union")]
+  property top_level_union : UnionUnion
+end
+
+class ObjectOrStringClass
+  include JSON::Serializable
+
+  # This must not get lost
+  property prop : Float64
+end
+
+alias ObjectOrStringUnion = ObjectOrStringClass | String
+
+# Either a number or a string
+alias UnionUnion = Float64 | String
diff --git a/head/schema-crystal/test/inputs/schema/direct-union.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/direct-union.schema/default/TopLevel.cr
new file mode 100644
index 0000000..d0e6576
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/direct-union.schema/default/TopLevel.cr
@@ -0,0 +1,17 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property stuff : Hash(String, Thing)
+end
+
+class Thing
+  include JSON::Serializable
+
+  property optional : Anything?
+
+  property required : Anything?
+end
+
+alias Anything = Array(JSON::Any?) | Hash(String, JSON::Any?) | Float64 | Int32 | Bool | String
diff --git a/head/schema-crystal/test/inputs/schema/empty-object.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/empty-object.schema/default/TopLevel.cr
new file mode 100644
index 0000000..b91bdf1
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/empty-object.schema/default/TopLevel.cr
@@ -0,0 +1,3 @@
+require "json"
+
+alias TopLevel = Hash(String, JSON::Any?)
diff --git a/head/schema-crystal/test/inputs/schema/enum-with-null.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/enum-with-null.schema/default/TopLevel.cr
new file mode 100644
index 0000000..add12eb
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/enum-with-null.schema/default/TopLevel.cr
@@ -0,0 +1,8 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "enum")]
+  property top_level_enum : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/enum-with-values.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/enum-with-values.schema/default/TopLevel.cr
new file mode 100644
index 0000000..1e8406a
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/enum-with-values.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property weekdays : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/go-schema-pattern-properties.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/go-schema-pattern-properties.schema/default/TopLevel.cr
new file mode 100644
index 0000000..5e48e8d
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/go-schema-pattern-properties.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property map : Hash(String, Int32)
+end
diff --git a/head/schema-crystal/test/inputs/schema/id-no-address.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/id-no-address.schema/default/TopLevel.cr
new file mode 100644
index 0000000..e1d04b2
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/id-no-address.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property item : Int32
+end
diff --git a/head/schema-crystal/test/inputs/schema/id-root.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/id-root.schema/default/TopLevel.cr
new file mode 100644
index 0000000..096f325
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/id-root.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property bar : Int32
+end
diff --git a/head/schema-crystal/test/inputs/schema/ie-suffix-singularization.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/ie-suffix-singularization.schema/default/TopLevel.cr
new file mode 100644
index 0000000..c4b2352
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/ie-suffix-singularization.schema/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property cookies : Array(Cookie)
+end
+
+class Cookie
+  include JSON::Serializable
+
+  property name : String
+
+  property value : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/implicit-all-of.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/implicit-all-of.schema/default/TopLevel.cr
new file mode 100644
index 0000000..bc6b823
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/implicit-all-of.schema/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Int32
+
+  property bar : Bool
+
+  property quux : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/implicit-class-array-union.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/implicit-class-array-union.schema/default/TopLevel.cr
new file mode 100644
index 0000000..e94cb82
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/implicit-class-array-union.schema/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : FooUnion?
+end
+
+class FooClass
+  include JSON::Serializable
+
+  property bar : Int32
+end
+
+alias FooUnion = Float64 | FooClass | Int32 | Array(Int32) | Bool | String
diff --git a/head/schema-crystal/test/inputs/schema/implicit-one-of.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/implicit-one-of.schema/default/TopLevel.cr
new file mode 100644
index 0000000..58e6974
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/implicit-one-of.schema/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Int32
+
+  property bar : Bool?
+
+  property quux : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/integer-float-union.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/integer-float-union.schema/default/TopLevel.cr
new file mode 100644
index 0000000..d491fc9
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/integer-float-union.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property number : Float64
+end
diff --git a/head/schema-crystal/test/inputs/schema/integer-string.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/integer-string.schema/default/TopLevel.cr
new file mode 100644
index 0000000..1e9fbec
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/integer-string.schema/default/TopLevel.cr
@@ -0,0 +1,25 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "arrNullable")]
+  property arr_nullable : Array(String?)?
+
+  @[JSON::Field(key: "arrOne")]
+  property arr_one : Array(String)?
+
+  property nullable : String?
+
+  property one : String
+
+  property optional : String?
+
+  @[JSON::Field(key: "unionWithInt")]
+  property union_with_int : UnionWithInt
+
+  @[JSON::Field(key: "unionWithIntAndEnum")]
+  property union_with_int_and_enum : UnionWithInt
+end
+
+alias UnionWithInt = Int32 | String
diff --git a/head/schema-crystal/test/inputs/schema/integer-type.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/integer-type.schema/default/TopLevel.cr
new file mode 100644
index 0000000..e5b4b64
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/integer-type.schema/default/TopLevel.cr
@@ -0,0 +1,23 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property above_i32_max : Int32
+
+  property below_i32_min : Int32
+
+  property i32_range : Int32
+
+  property large_bounds : Int32
+
+  property only_maximum : Int32
+
+  property only_minimum : Int32
+
+  property small_negative : Int32
+
+  property small_positive : Int32
+
+  property unbounded : Int32
+end
diff --git a/head/schema-crystal/test/inputs/schema/intersection-nested.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/intersection-nested.schema/default/TopLevel.cr
new file mode 100644
index 0000000..bbdd217
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/intersection-nested.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property intersection : Float64?
+end
diff --git a/head/schema-crystal/test/inputs/schema/intersection.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/intersection.schema/default/TopLevel.cr
new file mode 100644
index 0000000..89be215
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/intersection.schema/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property intersection : Intersection?
+end
+
+class Intersection
+  include JSON::Serializable
+
+  property foo : Float64
+
+  property bar : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/issue2680-top-level-array.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/issue2680-top-level-array.schema/default/TopLevel.cr
new file mode 100644
index 0000000..8607039
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/issue2680-top-level-array.schema/default/TopLevel.cr
@@ -0,0 +1,3 @@
+require "json"
+
+alias TopLevel = Array(Int32)
diff --git a/head/schema-crystal/test/inputs/schema/keyword-enum.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/keyword-enum.schema/default/TopLevel.cr
new file mode 100644
index 0000000..add12eb
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/keyword-enum.schema/default/TopLevel.cr
@@ -0,0 +1,8 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "enum")]
+  property top_level_enum : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/light.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/light.schema/default/TopLevel.cr
new file mode 100644
index 0000000..eacd2b5
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/light.schema/default/TopLevel.cr
@@ -0,0 +1,18 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "LightParams")]
+  property light_params : LightParams
+end
+
+class LightParams
+  include JSON::Serializable
+
+  property app_id : String
+
+  property outlet_id : String
+
+  property rgba : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/list.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/list.schema/default/TopLevel.cr
new file mode 100644
index 0000000..1537ac5
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/list.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+# A recursive class type
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : TopLevel?
+end
diff --git a/head/schema-crystal/test/inputs/schema/min-max-items.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/min-max-items.schema/default/TopLevel.cr
new file mode 100644
index 0000000..11dada5
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/min-max-items.schema/default/TopLevel.cr
@@ -0,0 +1,21 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "maxOnly")]
+  property max_only : Array(Int32)
+
+  @[JSON::Field(key: "minAndMax")]
+  property min_and_max : Array(Float64)
+
+  @[JSON::Field(key: "minOnly")]
+  property min_only : Array(String)
+
+  property plain : Array(String)
+
+  @[JSON::Field(key: "unionItems")]
+  property union_items : Array(UnionItem)
+end
+
+alias UnionItem = Int32 | String
diff --git a/head/schema-crystal/test/inputs/schema/minmax-integer.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/minmax-integer.schema/default/TopLevel.cr
new file mode 100644
index 0000000..83b4fbf
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/minmax-integer.schema/default/TopLevel.cr
@@ -0,0 +1,24 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property free : Int32
+
+  property intersection : Int32
+
+  property max : Int32
+
+  property min : Int32
+
+  property minmax : Int32
+
+  @[JSON::Field(key: "minMaxIntersection")]
+  property min_max_intersection : Int32
+
+  @[JSON::Field(key: "minMaxUnion")]
+  property min_max_union : Int32
+
+  @[JSON::Field(key: "union")]
+  property top_level_union : Int32
+end
diff --git a/head/schema-crystal/test/inputs/schema/minmax.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/minmax.schema/default/TopLevel.cr
new file mode 100644
index 0000000..9d4ab2a
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/minmax.schema/default/TopLevel.cr
@@ -0,0 +1,24 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property free : Float64
+
+  property intersection : Float64
+
+  property max : Float64
+
+  property min : Float64
+
+  property minmax : Float64
+
+  @[JSON::Field(key: "minMaxIntersection")]
+  property min_max_intersection : Float64
+
+  @[JSON::Field(key: "minMaxUnion")]
+  property min_max_union : Float64
+
+  @[JSON::Field(key: "union")]
+  property top_level_union : Float64
+end
diff --git a/head/schema-crystal/test/inputs/schema/minmaxlength.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/minmaxlength.schema/default/TopLevel.cr
new file mode 100644
index 0000000..c72c6e3
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/minmaxlength.schema/default/TopLevel.cr
@@ -0,0 +1,27 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property intersection : String
+
+  @[JSON::Field(key: "inUnion")]
+  property in_union : InUnion
+
+  property maxlength : String
+
+  property minlength : String
+
+  @[JSON::Field(key: "minMaxIntersection")]
+  property min_max_intersection : String
+
+  property minmaxlength : String
+
+  @[JSON::Field(key: "minMaxUnion")]
+  property min_max_union : String
+
+  @[JSON::Field(key: "union")]
+  property top_level_union : String
+end
+
+alias InUnion = Float64 | String
diff --git a/head/schema-crystal/test/inputs/schema/multi-type-enum.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/multi-type-enum.schema/default/TopLevel.cr
new file mode 100644
index 0000000..529c7c5
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/multi-type-enum.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : FooUnion
+end
+
+alias FooUnion = Float64 | Bool | String
diff --git a/head/schema-crystal/test/inputs/schema/mutually-recursive.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/mutually-recursive.schema/default/TopLevel.cr
new file mode 100644
index 0000000..3d03f75
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/mutually-recursive.schema/default/TopLevel.cr
@@ -0,0 +1,15 @@
+require "json"
+
+class Bar
+  include JSON::Serializable
+
+  property foo : Foo
+end
+
+class TopLevel
+  include JSON::Serializable
+
+  property bar : Bar?
+end
+
+alias Foo = TopLevel | Array(TopLevel)
diff --git a/head/schema-crystal/test/inputs/schema/nested-intersection-union.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/nested-intersection-union.schema/default/TopLevel.cr
new file mode 100644
index 0000000..be4b5eb
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/nested-intersection-union.schema/default/TopLevel.cr
@@ -0,0 +1,19 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property input : Array(Item)
+end
+
+class Item
+  include JSON::Serializable
+
+  property content : String?
+
+  property id : String?
+
+  property output : String?
+
+  property summary : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/non-standard-ref.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/non-standard-ref.schema/default/TopLevel.cr
new file mode 100644
index 0000000..d2d3d6d
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/non-standard-ref.schema/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property bar : Int32
+
+  property foo : Int32
+
+  property quux : Bool
+end
diff --git a/head/schema-crystal/test/inputs/schema/object-type-required.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/object-type-required.schema/default/TopLevel.cr
new file mode 100644
index 0000000..170be21
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/object-type-required.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : String
+
+  property bar : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/optional-any.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/optional-any.schema/default/TopLevel.cr
new file mode 100644
index 0000000..bea26e0
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/optional-any.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property bar : Bool
+
+  property foo : JSON::Any?
+end
diff --git a/head/schema-crystal/test/inputs/schema/optional-const-ref.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/optional-const-ref.schema/default/TopLevel.cr
new file mode 100644
index 0000000..eeaba72
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/optional-const-ref.schema/default/TopLevel.cr
@@ -0,0 +1,30 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property coordinates : Array(Coordinate)?
+
+  property count : Int32?
+
+  property label : String?
+
+  @[JSON::Field(key: "requiredCoordinates")]
+  property required_coordinates : Array(Coordinate)
+
+  @[JSON::Field(key: "requiredCount")]
+  property required_count : Int32
+
+  @[JSON::Field(key: "requiredLabel")]
+  property required_label : String
+
+  property weight : Float64?
+end
+
+class Coordinate
+  include JSON::Serializable
+
+  property latitude : Float64
+
+  property longitude : Float64
+end
diff --git a/head/schema-crystal/test/inputs/schema/optional-constraints.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/optional-constraints.schema/default/TopLevel.cr
new file mode 100644
index 0000000..6e81b73
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/optional-constraints.schema/default/TopLevel.cr
@@ -0,0 +1,20 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "optDouble")]
+  property opt_double : Float64?
+
+  @[JSON::Field(key: "optInt")]
+  property opt_int : Int32?
+
+  @[JSON::Field(key: "optPattern")]
+  property opt_pattern : String?
+
+  @[JSON::Field(key: "optString")]
+  property opt_string : String?
+
+  @[JSON::Field(key: "reqZeroMin")]
+  property req_zero_min : Int32
+end
diff --git a/head/schema-crystal/test/inputs/schema/optional-date-time.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/optional-date-time.schema/default/TopLevel.cr
new file mode 100644
index 0000000..e73b27c
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/optional-date-time.schema/default/TopLevel.cr
@@ -0,0 +1,23 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "optional-date")]
+  property optional_date : String?
+
+  @[JSON::Field(key: "optional-date-time")]
+  property optional_date_time : String?
+
+  @[JSON::Field(key: "optional-time")]
+  property optional_time : String?
+
+  @[JSON::Field(key: "required-date")]
+  property required_date : String
+
+  @[JSON::Field(key: "required-date-time")]
+  property required_date_time : String
+
+  @[JSON::Field(key: "required-time")]
+  property required_time : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/optional-enum.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/optional-enum.schema/default/TopLevel.cr
new file mode 100644
index 0000000..6fd3b9c
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/optional-enum.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property name : String
+
+  property shortcut : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/pattern.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/pattern.schema/default/TopLevel.cr
new file mode 100644
index 0000000..b830f0d
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/pattern.schema/default/TopLevel.cr
@@ -0,0 +1,12 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property pattern1 : String
+
+  property pattern2 : String
+
+  @[JSON::Field(key: "union")]
+  property top_level_union : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/postman-collection.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/postman-collection.schema/default/TopLevel.cr
new file mode 100644
index 0000000..3c60958
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/postman-collection.schema/default/TopLevel.cr
@@ -0,0 +1,18 @@
+require "json"
+
+# Postman collection
+class TopLevel
+  include JSON::Serializable
+
+  property item : Array(TopLevel)?
+
+  property name : String?
+
+  property response : Array(Response)?
+end
+
+class Response
+  include JSON::Serializable
+
+  property body : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/prefix-items.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/prefix-items.schema/default/TopLevel.cr
new file mode 100644
index 0000000..7dddcd5
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/prefix-items.schema/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property open : Array(Open)
+
+  property tuple : Array(Open)
+end
+
+alias Open = Int32 | Bool
diff --git a/head/schema-crystal/test/inputs/schema/recursive-union-flattening.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/recursive-union-flattening.schema/default/TopLevel.cr
new file mode 100644
index 0000000..181fa49
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/recursive-union-flattening.schema/default/TopLevel.cr
@@ -0,0 +1,21 @@
+require "json"
+
+class TopLevelClass
+  include JSON::Serializable
+
+  property x : X?
+end
+
+class XClass
+  include JSON::Serializable
+
+  property x : X?
+end
+
+alias TopLevel = Hash(String, JSON::Any?) | Float64 | Int32 | Bool | String | Array(TopLevelElement?)
+
+alias TopLevelElement = Array(JSON::Any?) | Float64 | Bool | String | TopLevelClass
+
+alias XElement = Array(JSON::Any?) | Float64 | Bool | String | XClass
+
+alias X = Hash(String, JSON::Any?) | Float64 | Bool | String | Array(XElement?)
diff --git a/head/schema-crystal/test/inputs/schema/ref-id-files.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/ref-id-files.schema/default/TopLevel.cr
new file mode 100644
index 0000000..ed03ea1
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/ref-id-files.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Int32
+end
diff --git a/head/schema-crystal/test/inputs/schema/ref-remote.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/ref-remote.schema/default/TopLevel.cr
new file mode 100644
index 0000000..1537ac5
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/ref-remote.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+# A recursive class type
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : TopLevel?
+end
diff --git a/head/schema-crystal/test/inputs/schema/renaming-bug.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/renaming-bug.schema/default/TopLevel.cr
new file mode 100644
index 0000000..7226488
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/renaming-bug.schema/default/TopLevel.cr
@@ -0,0 +1,127 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property version : String?
+
+  property fruits : Array(Fruit)?
+
+  property vehicles : Array(Vehicle)?
+end
+
+class Fruit
+  include JSON::Serializable
+
+  property apple : Bool?
+
+  property berries : Array(Berry)?
+
+  property orange : Bool?
+end
+
+class Berry
+  include JSON::Serializable
+
+  property color : Color?
+
+  property name : String?
+
+  property shapes : Array(Shape)?
+end
+
+class Color
+  include JSON::Serializable
+
+  property rgb : Float64?
+end
+
+class Shape
+  include JSON::Serializable
+
+  property geometry : Geometry?
+
+  property history : History?
+end
+
+class Geometry
+  include JSON::Serializable
+
+  @[JSON::Field(key: "rectShape")]
+  property rect_shape : RectShape?
+
+  @[JSON::Field(key: "circularShape")]
+  property circular_shape : Array(CircularShape)?
+end
+
+class CircularShape
+  include JSON::Serializable
+
+  @[JSON::Field(key: "shapeName")]
+  property shape_name : String?
+end
+
+class RectShape
+  include JSON::Serializable
+
+  property parts : Array(Part)?
+end
+
+class Part
+  include JSON::Serializable
+
+  property depth : String?
+
+  property length : String?
+
+  property width : String?
+end
+
+class History
+  include JSON::Serializable
+
+  @[JSON::Field(key: "class")]
+  property history_class : String?
+end
+
+class Vehicle
+  include JSON::Serializable
+
+  property brand : String?
+
+  property id : String?
+
+  property speed : Speed?
+
+  @[JSON::Field(key: "subModule")]
+  property sub_module : Bool?
+
+  @[JSON::Field(key: "type")]
+  property vehicle_type : VehicleType?
+
+  property year : String?
+end
+
+class Speed
+  include JSON::Serializable
+
+  property velocity : Limit?
+end
+
+class Limit
+  include JSON::Serializable
+
+  property maximum : Float64?
+
+  property minimum : Float64?
+end
+
+class VehicleType
+  include JSON::Serializable
+
+  property name : String?
+
+  property width : String?
+
+  property length : String?
+end
diff --git a/head/schema-crystal/test/inputs/schema/required-draft3.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/required-draft3.schema/default/TopLevel.cr
new file mode 100644
index 0000000..99ab3cb
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/required-draft3.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property longitude : Float64
+end
diff --git a/head/schema-crystal/test/inputs/schema/required-non-properties.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/required-non-properties.schema/default/TopLevel.cr
new file mode 100644
index 0000000..07d4dad
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/required-non-properties.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Int32
+
+  property bar : JSON::Any?
+end
diff --git a/head/schema-crystal/test/inputs/schema/required.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/required.schema/default/TopLevel.cr
new file mode 100644
index 0000000..99ab3cb
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/required.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property longitude : Float64
+end
diff --git a/head/schema-crystal/test/inputs/schema/rust-cycle-breaker-union.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/rust-cycle-breaker-union.schema/default/TopLevel.cr
new file mode 100644
index 0000000..9c80050
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/rust-cycle-breaker-union.schema/default/TopLevel.cr
@@ -0,0 +1,21 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : Next?
+
+  property value : String
+end
+
+class Node
+  include JSON::Serializable
+
+  @[JSON::Field(key: "next")]
+  property node_next : Next?
+
+  property value : String
+end
+
+alias Next = Node | String
diff --git a/head/schema-crystal/test/inputs/schema/schema-constraints.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/schema-constraints.schema/default/TopLevel.cr
new file mode 100644
index 0000000..d12e9e3
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/schema-constraints.schema/default/TopLevel.cr
@@ -0,0 +1,10 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "minMaxLength")]
+  property min_max_length : String
+
+  property percent : Float64
+end
diff --git a/head/schema-crystal/test/inputs/schema/simple-ref.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/simple-ref.schema/default/TopLevel.cr
new file mode 100644
index 0000000..1537ac5
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/simple-ref.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+# A recursive class type
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "next")]
+  property top_level_next : TopLevel?
+end
diff --git a/head/schema-crystal/test/inputs/schema/strict-optional.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/strict-optional.schema/default/TopLevel.cr
new file mode 100644
index 0000000..bcc65c8
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/strict-optional.schema/default/TopLevel.cr
@@ -0,0 +1,7 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property foo : Float64?
+end
diff --git a/head/schema-crystal/test/inputs/schema/top-level-array.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/top-level-array.schema/default/TopLevel.cr
new file mode 100644
index 0000000..eda2a8a
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/top-level-array.schema/default/TopLevel.cr
@@ -0,0 +1,11 @@
+require "json"
+
+alias TopLevel = Array(TextClassificationOutputElement)
+
+class TextClassificationOutputElement
+  include JSON::Serializable
+
+  property label : String
+
+  property score : Float64
+end
diff --git a/head/schema-crystal/test/inputs/schema/top-level-primitive-array.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/top-level-primitive-array.schema/default/TopLevel.cr
new file mode 100644
index 0000000..2f8f42d
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/top-level-primitive-array.schema/default/TopLevel.cr
@@ -0,0 +1,3 @@
+require "json"
+
+alias TopLevel = Array(String)
diff --git a/head/schema-crystal/test/inputs/schema/top-level-primitive.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/top-level-primitive.schema/default/TopLevel.cr
new file mode 100644
index 0000000..c5d3d5b
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/top-level-primitive.schema/default/TopLevel.cr
@@ -0,0 +1,3 @@
+require "json"
+
+alias TopLevel = Float64
diff --git a/head/schema-crystal/test/inputs/schema/tuple.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/tuple.schema/default/TopLevel.cr
new file mode 100644
index 0000000..2f2a411
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/tuple.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property tuple : Array(TupleElement)
+end
+
+alias TupleElement = Int32 | Bool
diff --git a/head/schema-crystal/test/inputs/schema/unevaluated-properties.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/unevaluated-properties.schema/default/TopLevel.cr
new file mode 100644
index 0000000..9c408be
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/unevaluated-properties.schema/default/TopLevel.cr
@@ -0,0 +1,32 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property config : Config?
+end
+
+class Config
+  include JSON::Serializable
+
+  property closed : Closed?
+
+  property name : String?
+
+  property settings : Hash(String, Array(Item))?
+end
+
+class ClosedClass
+  include JSON::Serializable
+
+end
+
+class Item
+  include JSON::Serializable
+
+  property key : String
+
+  property value : String
+end
+
+alias Closed = Array(JSON::Any?) | ClosedClass | Float64 | Int32 | Bool | String
diff --git a/head/schema-crystal/test/inputs/schema/union-int-double.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/union-int-double.schema/default/TopLevel.cr
new file mode 100644
index 0000000..29121bc
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/union-int-double.schema/default/TopLevel.cr
@@ -0,0 +1,9 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property value : ValueUnion
+end
+
+alias ValueUnion = Float64 | String
diff --git a/head/schema-crystal/test/inputs/schema/union-list.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/union-list.schema/default/TopLevel.cr
new file mode 100644
index 0000000..a23ff7b
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/union-list.schema/default/TopLevel.cr
@@ -0,0 +1,16 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  property list : UnionList
+end
+
+class UnionListClass
+  include JSON::Serializable
+
+  @[JSON::Field(key: "next")]
+  property union_list_next : UnionList
+end
+
+alias UnionList = Float64 | UnionListClass
diff --git a/head/schema-crystal/test/inputs/schema/union.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/union.schema/default/TopLevel.cr
new file mode 100644
index 0000000..c80d934
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/union.schema/default/TopLevel.cr
@@ -0,0 +1,13 @@
+require "json"
+
+alias TopLevel = Array(TopLevelElement)
+
+class TopLevelElement
+  include JSON::Serializable
+
+  property one : Int32?
+
+  property two : Bool
+
+  property three : Float64?
+end
diff --git a/head/schema-crystal/test/inputs/schema/uuid.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/uuid.schema/default/TopLevel.cr
new file mode 100644
index 0000000..d3af4c0
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/uuid.schema/default/TopLevel.cr
@@ -0,0 +1,20 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  @[JSON::Field(key: "arrNullable")]
+  property arr_nullable : Array(String?)?
+
+  @[JSON::Field(key: "arrOne")]
+  property arr_one : Array(String)?
+
+  property nullable : String?
+
+  property one : String
+
+  property optional : String?
+
+  @[JSON::Field(key: "unionWithEnum")]
+  property union_with_enum : String
+end
diff --git a/head/schema-crystal/test/inputs/schema/vega-lite.schema/default/TopLevel.cr b/head/schema-crystal/test/inputs/schema/vega-lite.schema/default/TopLevel.cr
new file mode 100644
index 0000000..745c94b
--- /dev/null
+++ b/head/schema-crystal/test/inputs/schema/vega-lite.schema/default/TopLevel.cr
@@ -0,0 +1,5057 @@
+require "json"
+
+class TopLevel
+  include JSON::Serializable
+
+  # URL to [JSON schema](http://json-schema.org/) for a Vega-Lite specification. Unless you
+  # have a reason to change this, use `https://vega.github.io/schema/vega-lite/v2.json`.
+  # Setting the `$schema` property allows automatic validation and autocomplete in editors
+  # that support JSON schema.
+  @[JSON::Field(key: "$schema")]
+  property schema : String?
+
+  # Sets how the visualization size should be determined. If a string, should be one of
+  # `"pad"`, `"fit"` or `"none"`.
+  # Object values can additionally specify parameters for content sizing and automatic
+  # resizing.
+  # `"fit"` is only supported for single and layered views that don't use `rangeStep`.
+  #
+  # __Default value__: `pad`
+  property autosize : Autosize?
+
+  # CSS color property to use as the background of visualization.
+  #
+  # __Default value:__ none (transparent)
+  property background : String?
+
+  # Vega-Lite configuration object.  This property can only be defined at the top-level of a
+  # specification.
+  property config : Config?
+
+  # An object describing the data source
+  property data : Data?
+
+  # Description of this mark for commenting purpose.
+  property description : String?
+
+  # A key-value mapping between encoding channels and definition of fields.
+  property encoding : EncodingWithFacet?
+
+  # The height of a visualization.
+  #
+  # __Default value:__
+  # - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its y-channel has a
+  # [continuous scale](scale.html#continuous), the height will be the value of
+  # [`config.view.height`](spec.html#config).
+  # - For y-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
+  # value or unspecified, the height is [determined by the range step, paddings, and the
+  # cardinality of the field mapped to y-channel](scale.html#band). Otherwise, if the
+  # `rangeStep` is `null`, the height will be the value of
+  # [`config.view.height`](spec.html#config).
+  # - If no field is mapped to `y` channel, the `height` will be the value of `rangeStep`.
+  #
+  # __Note__: For plots with [`row` and `column` channels](encoding.html#facet), this
+  # represents the height of a single view.
+  #
+  # __See also:__ The documentation for [width and height](size.html) contains more examples.
+  property height : Float64?
+
+  # A string describing the mark type (one of `"bar"`, `"circle"`, `"square"`, `"tick"`,
+  # `"line"`,
+  # * `"area"`, `"point"`, `"rule"`, `"geoshape"`, and `"text"`) or a [mark definition
+  # object](mark.html#mark-def).
+  property mark : AnyMark?
+
+  # Name of the visualization for later reference.
+  property name : String?
+
+  # The default visualization padding, in pixels, from the edge of the visualization canvas
+  # to the data rectangle.  If a number, specifies padding for all sides.
+  # If an object, the value should have the format `{"left": 5, "top": 5, "right": 5,
+  # "bottom": 5}` to specify padding for each side of the visualization.
+  #
+  # __Default value__: `5`
+  property padding : Padding?
+
+  # An object defining properties of geographic projection.
+  #
+  # Works with `"geoshape"` marks and `"point"` or `"line"` marks that have a channel (one or
+  # more of `"X"`, `"X2"`, `"Y"`, `"Y2"`) with type `"latitude"`, or `"longitude"`.
+  property projection : Projection?
+
+  # A key-value mapping between selection names and definitions.
+  property selection : Hash(String, SelectionDef)?
+
+  # Title for the plot.
+  property title : Title?
+
+  # An array of data transformations such as filter and new field calculation.
+  property transform : Array(Transform)?
+
+  # The width of a visualization.
+  #
+  # __Default value:__ This will be determined by the following rules:
+  #
+  # - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its x-channel has a
+  # [continuous scale](scale.html#continuous), the width will be the value of
+  # [`config.view.width`](spec.html#config).
+  # - For x-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
+  # value or unspecified, the width is [determined by the range step, paddings, and the
+  # cardinality of the field mapped to x-channel](scale.html#band).   Otherwise, if the
+  # `rangeStep` is `null`, the width will be the value of
+  # [`config.view.width`](spec.html#config).
+  # - If no field is mapped to `x` channel, the `width` will be the value of
+  # [`config.scale.textXRangeStep`](size.html#default-width-and-height) for `text` mark and
+  # the value of `rangeStep` for other marks.
+  #
+  # __Note:__ For plots with [`row` and `column` channels](encoding.html#facet), this
+  # represents the width of a single view.
+  #
+  # __See also:__ The documentation for [width and height](size.html) contains more examples.
+  property width : Float64?
+
+  # Layer or single view specifications to be layered.
+  #
+  # __Note__: Specifications inside `layer` cannot use `row` and `column` channels as
+  # layering facet specifications is not allowed.
+  property layer : Array(LayerSpec)?
+
+  # Scale, axis, and legend resolutions for layers.
+  #
+  # Scale, axis, and legend resolutions for facets.
+  #
+  # Scale and legend resolutions for repeated charts.
+  #
+  # Scale, axis, and legend resolutions for vertically concatenated charts.
+  #
+  # Scale, axis, and legend resolutions for horizontally concatenated charts.
+  property resolve : Resolve?
+
+  # An object that describes mappings between `row` and `column` channels and their field
+  # definitions.
+  property facet : FacetMapping?
+
+  # A specification of the view that gets faceted.
+  property spec : SpecElement?
+
+  # An object that describes what fields should be repeated into views that are laid out as a
+  # `row` or `column`.
+  property repeat : Repeat?
+
+  # A list of views that should be concatenated and put into a column.
+  property vconcat : Array(SpecElement)?
+
+  # A list of views that should be concatenated and put into a row.
+  property hconcat : Array(SpecElement)?
+end
+
+class AutoSizeParams
+  include JSON::Serializable
+
+  # Determines how size calculation should be performed, one of `"content"` or `"padding"`.
+  # The default setting (`"content"`) interprets the width and height settings as the data
+  # rectangle (plotting) dimensions, to which padding is then added. In contrast, the
+  # `"padding"` setting includes the padding within the view size calculations, such that the
+  # width and height settings indicate the **total** intended size of the view.
+  #
+  # __Default value__: `"content"`
+  property contains : String?
+
+  # A boolean flag indicating if autosize layout should be re-calculated on every view
+  # update.
+  #
+  # __Default value__: `false`
+  property resize : Bool?
+
+  # The sizing format type. One of `"pad"`, `"fit"` or `"none"`. See the [autosize
+  # type](https://vega.github.io/vega-lite/docs/size.html#autosize) documentation for
+  # descriptions of each.
+  #
+  # __Default value__: `"pad"`
+  @[JSON::Field(key: "type")]
+  property auto_size_params_type : String?
+end
+
+# Vega-Lite configuration object.  This property can only be defined at the top-level of a
+# specification.
+class Config
+  include JSON::Serializable
+
+  # Area-Specific Config
+  property area : MarkConfig?
+
+  # Sets how the visualization size should be determined. If a string, should be one of
+  # `"pad"`, `"fit"` or `"none"`.
+  # Object values can additionally specify parameters for content sizing and automatic
+  # resizing.
+  # `"fit"` is only supported for single and layered views that don't use `rangeStep`.
+  #
+  # __Default value__: `pad`
+  property autosize : Autosize?
+
+  # Axis configuration, which determines default properties for all `x` and `y`
+  # [axes](axis.html). For a full list of axis configuration options, please see the
+  # [corresponding section of the axis documentation](axis.html#config).
+  property axis : AxisConfig?
+
+  # Specific axis config for axes with "band" scales.
+  @[JSON::Field(key: "axisBand")]
+  property axis_band : VgAxisConfig?
+
+  # Specific axis config for x-axis along the bottom edge of the chart.
+  @[JSON::Field(key: "axisBottom")]
+  property axis_bottom : VgAxisConfig?
+
+  # Specific axis config for y-axis along the left edge of the chart.
+  @[JSON::Field(key: "axisLeft")]
+  property axis_left : VgAxisConfig?
+
+  # Specific axis config for y-axis along the right edge of the chart.
+  @[JSON::Field(key: "axisRight")]
+  property axis_right : VgAxisConfig?
+
+  # Specific axis config for x-axis along the top edge of the chart.
+  @[JSON::Field(key: "axisTop")]
+  property axis_top : VgAxisConfig?
+
+  # X-axis specific config.
+  @[JSON::Field(key: "axisX")]
+  property axis_x : VgAxisConfig?
+
+  # Y-axis specific config.
+  @[JSON::Field(key: "axisY")]
+  property axis_y : VgAxisConfig?
+
+  # CSS color property to use as the background of visualization.
+  #
+  # __Default value:__ none (transparent)
+  property background : String?
+
+  # Bar-Specific Config
+  property bar : BarConfig?
+
+  # Circle-Specific Config
+  property circle : MarkConfig?
+
+  # Default axis and legend title for count fields.
+  #
+  # __Default value:__ `'Number of Records'`.
+  @[JSON::Field(key: "countTitle")]
+  property count_title : String?
+
+  # Defines how Vega-Lite generates title for fields.  There are three possible styles:
+  #
+  # - `"verbal"` (Default) - displays function in a verbal style (e.g., "Sum of field",
+  # "Year-month of date", "field (binned)").
+  # - `"function"` - displays function using parentheses and capitalized texts (e.g.,
+  # "SUM(field)", "YEARMONTH(date)", "BIN(field)").
+  # - `"plain"` - displays only the field name without functions (e.g., "field", "date",
+  # "field").
+  @[JSON::Field(key: "fieldTitle")]
+  property field_title : String?
+
+  # Geoshape-Specific Config
+  property geoshape : MarkConfig?
+
+  # Defines how Vega-Lite should handle invalid values (`null` and `NaN`).
+  # - If set to `"filter"` (default), all data items with null values are filtered.
+  # - If `null`, all data items are included. In this case, invalid values will be
+  # interpreted as zeroes.
+  @[JSON::Field(key: "invalidValues")]
+  property invalid_values : String?
+
+  # Legend configuration, which determines default properties for all [legends](legend.html).
+  # For a full list of legend configuration options, please see the [corresponding section of
+  # in the legend documentation](legend.html#config).
+  property legend : LegendConfig?
+
+  # Line-Specific Config
+  property line : MarkConfig?
+
+  # Mark Config
+  property mark : MarkConfig?
+
+  # D3 Number format for axis labels and text tables. For example "s" for SI units. Use [D3's
+  # number format pattern](https://github.com/d3/d3-format#locale_format).
+  @[JSON::Field(key: "numberFormat")]
+  property number_format : String?
+
+  # The default visualization padding, in pixels, from the edge of the visualization canvas
+  # to the data rectangle.  If a number, specifies padding for all sides.
+  # If an object, the value should have the format `{"left": 5, "top": 5, "right": 5,
+  # "bottom": 5}` to specify padding for each side of the visualization.
+  #
+  # __Default value__: `5`
+  property padding : Padding?
+
+  # Point-Specific Config
+  property point : MarkConfig?
+
+  # Projection configuration, which determines default properties for all
+  # [projections](projection.html). For a full list of projection configuration options,
+  # please see the [corresponding section of the projection
+  # documentation](projection.html#config).
+  property projection : ProjectionConfig?
+
+  # An object hash that defines default range arrays or schemes for using with scales.
+  # For a full list of scale range configuration options, please see the [corresponding
+  # section of the scale documentation](scale.html#config).
+  property range : Hash(String, RangeConfigValue)?
+
+  # Rect-Specific Config
+  property rect : MarkConfig?
+
+  # Rule-Specific Config
+  property rule : MarkConfig?
+
+  # Scale configuration determines default properties for all [scales](scale.html). For a
+  # full list of scale configuration options, please see the [corresponding section of the
+  # scale documentation](scale.html#config).
+  property scale : ScaleConfig?
+
+  # An object hash for defining default properties for each type of selections.
+  property selection : SelectionConfig?
+
+  # Square-Specific Config
+  property square : MarkConfig?
+
+  # Default stack offset for stackable mark.
+  property stack : String?
+
+  # An object hash that defines key-value mappings to determine default properties for marks
+  # with a given [style](mark.html#mark-def).  The keys represent styles names; the value are
+  # valid [mark configuration objects](mark.html#config).
+  property style : Hash(String, VgMarkConfig)?
+
+  # Text-Specific Config
+  property text : TextConfig?
+
+  # Tick-Specific Config
+  property tick : TickConfig?
+
+  # Default datetime format for axis and legend labels. The format can be set directly on
+  # each axis and legend. Use [D3's time format
+  # pattern](https://github.com/d3/d3-time-format#locale_format).
+  #
+  # __Default value:__ `'%b %d, %Y'`.
+  @[JSON::Field(key: "timeFormat")]
+  property time_format : String?
+
+  # Title configuration, which determines default properties for all [titles](title.html).
+  # For a full list of title configuration options, please see the [corresponding section of
+  # the title documentation](title.html#config).
+  property title : VgTitleConfig?
+
+  # Default properties for [single view plots](spec.html#single).
+  property view : ViewConfig?
+end
+
+# Area-Specific Config
+#
+# Circle-Specific Config
+#
+# Geoshape-Specific Config
+#
+# Line-Specific Config
+#
+# Mark Config
+#
+# Point-Specific Config
+#
+# Rect-Specific Config
+#
+# Rule-Specific Config
+#
+# Square-Specific Config
+class MarkConfig
+  include JSON::Serializable
+
+  # The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
+  property align : String?
+
+  # The rotation angle of the text, in degrees.
+  property angle : Float64?
+
+  # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
+  #
+  # __Default value:__ `"middle"`
+  property baseline : String?
+
+  # Default color.  Note that `fill` and `stroke` have higher precedence than `color` and
+  # will override `color`.
+  #
+  # __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property color : String?
+
+  # The mouse cursor used over the mark. Any valid [CSS cursor
+  # type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
+  property cursor : String?
+
+  # The horizontal offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dx : Float64?
+
+  # The vertical offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dy : Float64?
+
+  # Default Fill Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property fill : String?
+
+  # Whether the mark's color should be used as fill color instead of stroke color.
+  #
+  # __Default value:__ `true` for all marks except `point` and `false` for `point`.
+  #
+  # __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property filled : Bool?
+
+  # The fill opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "fillOpacity")]
+  property fill_opacity : Float64?
+
+  # The typeface to set the text in (e.g., `"Helvetica Neue"`).
+  property font : String?
+
+  # The font size, in pixels.
+  @[JSON::Field(key: "fontSize")]
+  property font_size : Float64?
+
+  # The font style (e.g., `"italic"`).
+  @[JSON::Field(key: "fontStyle")]
+  property font_style : String?
+
+  # The font weight (e.g., `"bold"`).
+  @[JSON::Field(key: "fontWeight")]
+  property font_weight : FontWeightUnion?
+
+  # A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
+  property href : String?
+
+  # The line interpolation method to use for line and area marks. One of the following:
+  # - `"linear"`: piecewise linear segments, as in a polyline.
+  # - `"linear-closed"`: close the linear segments to form a polygon.
+  # - `"step"`: alternate between horizontal and vertical segments, as in a step function.
+  # - `"step-before"`: alternate between vertical and horizontal segments, as in a step
+  # function.
+  # - `"step-after"`: alternate between horizontal and vertical segments, as in a step
+  # function.
+  # - `"basis"`: a B-spline, with control point duplication on the ends.
+  # - `"basis-open"`: an open B-spline; may not intersect the start or end.
+  # - `"basis-closed"`: a closed B-spline, as in a loop.
+  # - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
+  # - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
+  # will intersect other control points.
+  # - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
+  # - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
+  # spline.
+  # - `"monotone"`: cubic interpolation that preserves monotonicity in y.
+  property interpolate : String?
+
+  # The maximum length of the text mark in pixels (default 0, indicating no limit). The text
+  # value will be automatically truncated if the rendered size exceeds the limit.
+  property limit : Float64?
+
+  # The overall opacity (value between [0,1]).
+  #
+  # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
+  # `square` marks or layered `bar` charts and `1` otherwise.
+  property opacity : Float64?
+
+  # The orientation of a non-stacked bar, tick, area, and line charts.
+  # The value is either horizontal (default) or vertical.
+  # - For bar, rule and tick, this determines whether the size of the bar and tick
+  # should be applied to x or y dimension.
+  # - For area, this property determines the orient property of the Vega output.
+  # - For line, this property determines the sort order of the points in the line
+  # if `config.sortLineBy` is not specified.
+  # For stacked charts, this is always determined by the orientation of the stack;
+  # therefore explicitly specified value will be ignored.
+  property orient : String?
+
+  # Polar coordinate radial offset, in pixels, of the text label from the origin determined
+  # by the `x` and `y` properties.
+  property radius : Float64?
+
+  # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
+  # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
+  #
+  # __Default value:__ `"circle"`
+  property shape : String?
+
+  # The pixel area each the point/circle/square.
+  # For example: in the case of circles, the radius is determined in part by the square root
+  # of the size value.
+  #
+  # __Default value:__ `30`
+  property size : Float64?
+
+  # Default Stroke Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property stroke : String?
+
+  # An array of alternating stroke, space lengths for creating dashed or dotted lines.
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # The offset (in pixels) into which to begin drawing with the stroke dash array.
+  @[JSON::Field(key: "strokeDashOffset")]
+  property stroke_dash_offset : Float64?
+
+  # The stroke opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "strokeOpacity")]
+  property stroke_opacity : Float64?
+
+  # The stroke width, in pixels.
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+
+  # Depending on the interpolation type, sets the tension parameter (for line and area marks).
+  property tension : Float64?
+
+  # Placeholder text if the `text` channel is not specified
+  property text : String?
+
+  # Polar coordinate angle, in radians, of the text label from the origin determined by the
+  # `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
+  # `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
+  # indicating "north".
+  property theta : Float64?
+end
+
+# Axis configuration, which determines default properties for all `x` and `y`
+# [axes](axis.html). For a full list of axis configuration options, please see the
+# [corresponding section of the axis documentation](axis.html#config).
+class AxisConfig
+  include JSON::Serializable
+
+  # An interpolation fraction indicating where, for `band` scales, axis ticks should be
+  # positioned. A value of `0` places ticks at the left edge of their bands. A value of `0.5`
+  # places ticks in the middle of their bands.
+  @[JSON::Field(key: "bandPosition")]
+  property band_position : Float64?
+
+  # A boolean flag indicating if the domain (the axis baseline) should be included as part of
+  # the axis.
+  #
+  # __Default value:__ `true`
+  property domain : Bool?
+
+  # Color of axis domain line.
+  #
+  # __Default value:__  (none, using Vega default).
+  @[JSON::Field(key: "domainColor")]
+  property domain_color : String?
+
+  # Stroke width of axis domain line
+  #
+  # __Default value:__  (none, using Vega default).
+  @[JSON::Field(key: "domainWidth")]
+  property domain_width : Float64?
+
+  # A boolean flag indicating if grid lines should be included as part of the axis
+  #
+  # __Default value:__ `true` for [continuous scales](scale.html#continuous) that are not
+  # binned; otherwise, `false`.
+  property grid : Bool?
+
+  # Color of gridlines.
+  @[JSON::Field(key: "gridColor")]
+  property grid_color : String?
+
+  # The offset (in pixels) into which to begin drawing with the grid dash array.
+  @[JSON::Field(key: "gridDash")]
+  property grid_dash : Array(Float64)?
+
+  # The stroke opacity of grid (value between [0,1])
+  #
+  # __Default value:__ (`1` by default)
+  @[JSON::Field(key: "gridOpacity")]
+  property grid_opacity : Float64?
+
+  # The grid width, in pixels.
+  @[JSON::Field(key: "gridWidth")]
+  property grid_width : Float64?
+
+  # The rotation angle of the axis labels.
+  #
+  # __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
+  @[JSON::Field(key: "labelAngle")]
+  property label_angle : Float64?
+
+  # Indicates if labels should be hidden if they exceed the axis range. If `false `(the
+  # default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
+  # they exceed the axis range by more than 1 pixel. If this property is a number, it
+  # specifies the pixel tolerance: the maximum amount by which a label bounding box may
+  # exceed the axis range.
+  #
+  # __Default value:__ `false`.
+  @[JSON::Field(key: "labelBound")]
+  property label_bound : Label?
+
+  # The color of the tick label, can be in hex color code or regular color name.
+  @[JSON::Field(key: "labelColor")]
+  property label_color : String?
+
+  # Indicates if the first and last axis labels should be aligned flush with the scale range.
+  # Flush alignment for a horizontal axis will left-align the first label and right-align the
+  # last label. For vertical axes, bottom and top text baselines are applied instead. If this
+  # property is a number, it also indicates the number of pixels by which to offset the first
+  # and last labels; for example, a value of 2 will flush-align the first and last labels and
+  # also push them 2 pixels outward from the center of the axis. The additional adjustment
+  # can sometimes help the labels better visually group with corresponding axis ticks.
+  #
+  # __Default value:__ `true` for axis of a continuous x-scale. Otherwise, `false`.
+  @[JSON::Field(key: "labelFlush")]
+  property label_flush : Label?
+
+  # The font of the tick label.
+  @[JSON::Field(key: "labelFont")]
+  property label_font : String?
+
+  # The font size of the label, in pixels.
+  @[JSON::Field(key: "labelFontSize")]
+  property label_font_size : Float64?
+
+  # Maximum allowed pixel width of axis tick labels.
+  @[JSON::Field(key: "labelLimit")]
+  property label_limit : Float64?
+
+  # The strategy to use for resolving overlap of axis labels. If `false` (the default), no
+  # overlap reduction is attempted. If set to `true` or `"parity"`, a strategy of removing
+  # every other label is used (this works well for standard linear axes). If set to
+  # `"greedy"`, a linear scan of the labels is performed, removing any labels that overlaps
+  # with the last visible label (this often works better for log-scaled axes).
+  #
+  # __Default value:__ `true` for non-nominal fields with non-log scales; `"greedy"` for log
+  # scales; otherwise `false`.
+  @[JSON::Field(key: "labelOverlap")]
+  property label_overlap : LabelOverlapUnion?
+
+  # The padding, in pixels, between axis and text labels.
+  @[JSON::Field(key: "labelPadding")]
+  property label_padding : Float64?
+
+  # A boolean flag indicating if labels should be included as part of the axis.
+  #
+  # __Default value:__  `true`.
+  property labels : Bool?
+
+  # The maximum extent in pixels that axis ticks and labels should use. This determines a
+  # maximum offset value for axis titles.
+  #
+  # __Default value:__ `undefined`.
+  @[JSON::Field(key: "maxExtent")]
+  property max_extent : Float64?
+
+  # The minimum extent in pixels that axis ticks and labels should use. This determines a
+  # minimum offset value for axis titles.
+  #
+  # __Default value:__ `30` for y-axis; `undefined` for x-axis.
+  @[JSON::Field(key: "minExtent")]
+  property min_extent : Float64?
+
+  # Whether month names and weekday names should be abbreviated.
+  #
+  # __Default value:__  `false`
+  @[JSON::Field(key: "shortTimeLabels")]
+  property short_time_labels : Bool?
+
+  # The color of the axis's tick.
+  @[JSON::Field(key: "tickColor")]
+  property tick_color : String?
+
+  # Boolean flag indicating if pixel position values should be rounded to the nearest integer.
+  @[JSON::Field(key: "tickRound")]
+  property tick_round : Bool?
+
+  # Boolean value that determines whether the axis should include ticks.
+  property ticks : Bool?
+
+  # The size in pixels of axis ticks.
+  @[JSON::Field(key: "tickSize")]
+  property tick_size : Float64?
+
+  # The width, in pixels, of ticks.
+  @[JSON::Field(key: "tickWidth")]
+  property tick_width : Float64?
+
+  # Horizontal text alignment of axis titles.
+  @[JSON::Field(key: "titleAlign")]
+  property title_align : String?
+
+  # Angle in degrees of axis titles.
+  @[JSON::Field(key: "titleAngle")]
+  property title_angle : Float64?
+
+  # Vertical text baseline for axis titles.
+  @[JSON::Field(key: "titleBaseline")]
+  property title_baseline : String?
+
+  # Color of the title, can be in hex color code or regular color name.
+  @[JSON::Field(key: "titleColor")]
+  property title_color : String?
+
+  # Font of the title. (e.g., `"Helvetica Neue"`).
+  @[JSON::Field(key: "titleFont")]
+  property title_font : String?
+
+  # Font size of the title.
+  @[JSON::Field(key: "titleFontSize")]
+  property title_font_size : Float64?
+
+  # Font weight of the title. (e.g., `"bold"`).
+  @[JSON::Field(key: "titleFontWeight")]
+  property title_font_weight : TitleFontWeightUnion?
+
+  # Maximum allowed pixel width of axis titles.
+  @[JSON::Field(key: "titleLimit")]
+  property title_limit : Float64?
+
+  # Max length for axis title if the title is automatically generated from the field's
+  # description.
+  @[JSON::Field(key: "titleMaxLength")]
+  property title_max_length : Float64?
+
+  # The padding, in pixels, between title and axis.
+  @[JSON::Field(key: "titlePadding")]
+  property title_padding : Float64?
+
+  # X-coordinate of the axis title relative to the axis group.
+  @[JSON::Field(key: "titleX")]
+  property title_x : Float64?
+
+  # Y-coordinate of the axis title relative to the axis group.
+  @[JSON::Field(key: "titleY")]
+  property title_y : Float64?
+end
+
+# Specific axis config for axes with "band" scales.
+#
+# Specific axis config for x-axis along the bottom edge of the chart.
+#
+# Specific axis config for y-axis along the left edge of the chart.
+#
+# Specific axis config for y-axis along the right edge of the chart.
+#
+# Specific axis config for x-axis along the top edge of the chart.
+#
+# X-axis specific config.
+#
+# Y-axis specific config.
+class VgAxisConfig
+  include JSON::Serializable
+
+  # An interpolation fraction indicating where, for `band` scales, axis ticks should be
+  # positioned. A value of `0` places ticks at the left edge of their bands. A value of `0.5`
+  # places ticks in the middle of their bands.
+  @[JSON::Field(key: "bandPosition")]
+  property band_position : Float64?
+
+  # A boolean flag indicating if the domain (the axis baseline) should be included as part of
+  # the axis.
+  #
+  # __Default value:__ `true`
+  property domain : Bool?
+
+  # Color of axis domain line.
+  #
+  # __Default value:__  (none, using Vega default).
+  @[JSON::Field(key: "domainColor")]
+  property domain_color : String?
+
+  # Stroke width of axis domain line
+  #
+  # __Default value:__  (none, using Vega default).
+  @[JSON::Field(key: "domainWidth")]
+  property domain_width : Float64?
+
+  # A boolean flag indicating if grid lines should be included as part of the axis
+  #
+  # __Default value:__ `true` for [continuous scales](scale.html#continuous) that are not
+  # binned; otherwise, `false`.
+  property grid : Bool?
+
+  # Color of gridlines.
+  @[JSON::Field(key: "gridColor")]
+  property grid_color : String?
+
+  # The offset (in pixels) into which to begin drawing with the grid dash array.
+  @[JSON::Field(key: "gridDash")]
+  property grid_dash : Array(Float64)?
+
+  # The stroke opacity of grid (value between [0,1])
+  #
+  # __Default value:__ (`1` by default)
+  @[JSON::Field(key: "gridOpacity")]
+  property grid_opacity : Float64?
+
+  # The grid width, in pixels.
+  @[JSON::Field(key: "gridWidth")]
+  property grid_width : Float64?
+
+  # The rotation angle of the axis labels.
+  #
+  # __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
+  @[JSON::Field(key: "labelAngle")]
+  property label_angle : Float64?
+
+  # Indicates if labels should be hidden if they exceed the axis range. If `false `(the
+  # default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
+  # they exceed the axis range by more than 1 pixel. If this property is a number, it
+  # specifies the pixel tolerance: the maximum amount by which a label bounding box may
+  # exceed the axis range.
+  #
+  # __Default value:__ `false`.
+  @[JSON::Field(key: "labelBound")]
+  property label_bound : Label?
+
+  # The color of the tick label, can be in hex color code or regular color name.
+  @[JSON::Field(key: "labelColor")]
+  property label_color : String?
+
+  # Indicates if the first and last axis labels should be aligned flush with the scale range.
+  # Flush alignment for a horizontal axis will left-align the first label and right-align the
+  # last label. For vertical axes, bottom and top text baselines are applied instead. If this
+  # property is a number, it also indicates the number of pixels by which to offset the first
+  # and last labels; for example, a value of 2 will flush-align the first and last labels and
+  # also push them 2 pixels outward from the center of the axis. The additional adjustment
+  # can sometimes help the labels better visually group with corresponding axis ticks.
+  #
+  # __Default value:__ `true` for axis of a continuous x-scale. Otherwise, `false`.
+  @[JSON::Field(key: "labelFlush")]
+  property label_flush : Label?
+
+  # The font of the tick label.
+  @[JSON::Field(key: "labelFont")]
+  property label_font : String?
+
+  # The font size of the label, in pixels.
+  @[JSON::Field(key: "labelFontSize")]
+  property label_font_size : Float64?
+
+  # Maximum allowed pixel width of axis tick labels.
+  @[JSON::Field(key: "labelLimit")]
+  property label_limit : Float64?
+
+  # The strategy to use for resolving overlap of axis labels. If `false` (the default), no
+  # overlap reduction is attempted. If set to `true` or `"parity"`, a strategy of removing
+  # every other label is used (this works well for standard linear axes). If set to
+  # `"greedy"`, a linear scan of the labels is performed, removing any labels that overlaps
+  # with the last visible label (this often works better for log-scaled axes).
+  #
+  # __Default value:__ `true` for non-nominal fields with non-log scales; `"greedy"` for log
+  # scales; otherwise `false`.
+  @[JSON::Field(key: "labelOverlap")]
+  property label_overlap : LabelOverlapUnion?
+
+  # The padding, in pixels, between axis and text labels.
+  @[JSON::Field(key: "labelPadding")]
+  property label_padding : Float64?
+
+  # A boolean flag indicating if labels should be included as part of the axis.
+  #
+  # __Default value:__  `true`.
+  property labels : Bool?
+
+  # The maximum extent in pixels that axis ticks and labels should use. This determines a
+  # maximum offset value for axis titles.
+  #
+  # __Default value:__ `undefined`.
+  @[JSON::Field(key: "maxExtent")]
+  property max_extent : Float64?
+
+  # The minimum extent in pixels that axis ticks and labels should use. This determines a
+  # minimum offset value for axis titles.
+  #
+  # __Default value:__ `30` for y-axis; `undefined` for x-axis.
+  @[JSON::Field(key: "minExtent")]
+  property min_extent : Float64?
+
+  # The color of the axis's tick.
+  @[JSON::Field(key: "tickColor")]
+  property tick_color : String?
+
+  # Boolean flag indicating if pixel position values should be rounded to the nearest integer.
+  @[JSON::Field(key: "tickRound")]
+  property tick_round : Bool?
+
+  # Boolean value that determines whether the axis should include ticks.
+  property ticks : Bool?
+
+  # The size in pixels of axis ticks.
+  @[JSON::Field(key: "tickSize")]
+  property tick_size : Float64?
+
+  # The width, in pixels, of ticks.
+  @[JSON::Field(key: "tickWidth")]
+  property tick_width : Float64?
+
+  # Horizontal text alignment of axis titles.
+  @[JSON::Field(key: "titleAlign")]
+  property title_align : String?
+
+  # Angle in degrees of axis titles.
+  @[JSON::Field(key: "titleAngle")]
+  property title_angle : Float64?
+
+  # Vertical text baseline for axis titles.
+  @[JSON::Field(key: "titleBaseline")]
+  property title_baseline : String?
+
+  # Color of the title, can be in hex color code or regular color name.
+  @[JSON::Field(key: "titleColor")]
+  property title_color : String?
+
+  # Font of the title. (e.g., `"Helvetica Neue"`).
+  @[JSON::Field(key: "titleFont")]
+  property title_font : String?
+
+  # Font size of the title.
+  @[JSON::Field(key: "titleFontSize")]
+  property title_font_size : Float64?
+
+  # Font weight of the title. (e.g., `"bold"`).
+  @[JSON::Field(key: "titleFontWeight")]
+  property title_font_weight : TitleFontWeightUnion?
+
+  # Maximum allowed pixel width of axis titles.
+  @[JSON::Field(key: "titleLimit")]
+  property title_limit : Float64?
+
+  # Max length for axis title if the title is automatically generated from the field's
+  # description.
+  @[JSON::Field(key: "titleMaxLength")]
+  property title_max_length : Float64?
+
+  # The padding, in pixels, between title and axis.
+  @[JSON::Field(key: "titlePadding")]
+  property title_padding : Float64?
+
+  # X-coordinate of the axis title relative to the axis group.
+  @[JSON::Field(key: "titleX")]
+  property title_x : Float64?
+
+  # Y-coordinate of the axis title relative to the axis group.
+  @[JSON::Field(key: "titleY")]
+  property title_y : Float64?
+end
+
+# Bar-Specific Config
+class BarConfig
+  include JSON::Serializable
+
+  # The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
+  property align : String?
+
+  # The rotation angle of the text, in degrees.
+  property angle : Float64?
+
+  # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
+  #
+  # __Default value:__ `"middle"`
+  property baseline : String?
+
+  # Offset between bar for binned field.  Ideal value for this is either 0 (Preferred by
+  # statisticians) or 1 (Vega-Lite Default, D3 example style).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "binSpacing")]
+  property bin_spacing : Float64?
+
+  # Default color.  Note that `fill` and `stroke` have higher precedence than `color` and
+  # will override `color`.
+  #
+  # __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property color : String?
+
+  # The default size of the bars on continuous scales.
+  #
+  # __Default value:__ `5`
+  @[JSON::Field(key: "continuousBandSize")]
+  property continuous_band_size : Float64?
+
+  # The mouse cursor used over the mark. Any valid [CSS cursor
+  # type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
+  property cursor : String?
+
+  # The size of the bars.  If unspecified, the default size is  `bandSize-1`,
+  # which provides 1 pixel offset between bars.
+  @[JSON::Field(key: "discreteBandSize")]
+  property discrete_band_size : Float64?
+
+  # The horizontal offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dx : Float64?
+
+  # The vertical offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dy : Float64?
+
+  # Default Fill Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property fill : String?
+
+  # Whether the mark's color should be used as fill color instead of stroke color.
+  #
+  # __Default value:__ `true` for all marks except `point` and `false` for `point`.
+  #
+  # __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property filled : Bool?
+
+  # The fill opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "fillOpacity")]
+  property fill_opacity : Float64?
+
+  # The typeface to set the text in (e.g., `"Helvetica Neue"`).
+  property font : String?
+
+  # The font size, in pixels.
+  @[JSON::Field(key: "fontSize")]
+  property font_size : Float64?
+
+  # The font style (e.g., `"italic"`).
+  @[JSON::Field(key: "fontStyle")]
+  property font_style : String?
+
+  # The font weight (e.g., `"bold"`).
+  @[JSON::Field(key: "fontWeight")]
+  property font_weight : FontWeightUnion?
+
+  # A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
+  property href : String?
+
+  # The line interpolation method to use for line and area marks. One of the following:
+  # - `"linear"`: piecewise linear segments, as in a polyline.
+  # - `"linear-closed"`: close the linear segments to form a polygon.
+  # - `"step"`: alternate between horizontal and vertical segments, as in a step function.
+  # - `"step-before"`: alternate between vertical and horizontal segments, as in a step
+  # function.
+  # - `"step-after"`: alternate between horizontal and vertical segments, as in a step
+  # function.
+  # - `"basis"`: a B-spline, with control point duplication on the ends.
+  # - `"basis-open"`: an open B-spline; may not intersect the start or end.
+  # - `"basis-closed"`: a closed B-spline, as in a loop.
+  # - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
+  # - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
+  # will intersect other control points.
+  # - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
+  # - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
+  # spline.
+  # - `"monotone"`: cubic interpolation that preserves monotonicity in y.
+  property interpolate : String?
+
+  # The maximum length of the text mark in pixels (default 0, indicating no limit). The text
+  # value will be automatically truncated if the rendered size exceeds the limit.
+  property limit : Float64?
+
+  # The overall opacity (value between [0,1]).
+  #
+  # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
+  # `square` marks or layered `bar` charts and `1` otherwise.
+  property opacity : Float64?
+
+  # The orientation of a non-stacked bar, tick, area, and line charts.
+  # The value is either horizontal (default) or vertical.
+  # - For bar, rule and tick, this determines whether the size of the bar and tick
+  # should be applied to x or y dimension.
+  # - For area, this property determines the orient property of the Vega output.
+  # - For line, this property determines the sort order of the points in the line
+  # if `config.sortLineBy` is not specified.
+  # For stacked charts, this is always determined by the orientation of the stack;
+  # therefore explicitly specified value will be ignored.
+  property orient : String?
+
+  # Polar coordinate radial offset, in pixels, of the text label from the origin determined
+  # by the `x` and `y` properties.
+  property radius : Float64?
+
+  # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
+  # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
+  #
+  # __Default value:__ `"circle"`
+  property shape : String?
+
+  # The pixel area each the point/circle/square.
+  # For example: in the case of circles, the radius is determined in part by the square root
+  # of the size value.
+  #
+  # __Default value:__ `30`
+  property size : Float64?
+
+  # Default Stroke Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property stroke : String?
+
+  # An array of alternating stroke, space lengths for creating dashed or dotted lines.
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # The offset (in pixels) into which to begin drawing with the stroke dash array.
+  @[JSON::Field(key: "strokeDashOffset")]
+  property stroke_dash_offset : Float64?
+
+  # The stroke opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "strokeOpacity")]
+  property stroke_opacity : Float64?
+
+  # The stroke width, in pixels.
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+
+  # Depending on the interpolation type, sets the tension parameter (for line and area marks).
+  property tension : Float64?
+
+  # Placeholder text if the `text` channel is not specified
+  property text : String?
+
+  # Polar coordinate angle, in radians, of the text label from the origin determined by the
+  # `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
+  # `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
+  # indicating "north".
+  property theta : Float64?
+end
+
+# Legend configuration, which determines default properties for all [legends](legend.html).
+# For a full list of legend configuration options, please see the [corresponding section of
+# in the legend documentation](legend.html#config).
+class LegendConfig
+  include JSON::Serializable
+
+  # Corner radius for the full legend.
+  @[JSON::Field(key: "cornerRadius")]
+  property corner_radius : Float64?
+
+  # Padding (in pixels) between legend entries in a symbol legend.
+  @[JSON::Field(key: "entryPadding")]
+  property entry_padding : Float64?
+
+  # Background fill color for the full legend.
+  @[JSON::Field(key: "fillColor")]
+  property fill_color : String?
+
+  # The height of the gradient, in pixels.
+  @[JSON::Field(key: "gradientHeight")]
+  property gradient_height : Float64?
+
+  # Text baseline for color ramp gradient labels.
+  @[JSON::Field(key: "gradientLabelBaseline")]
+  property gradient_label_baseline : String?
+
+  # The maximum allowed length in pixels of color ramp gradient labels.
+  @[JSON::Field(key: "gradientLabelLimit")]
+  property gradient_label_limit : Float64?
+
+  # Vertical offset in pixels for color ramp gradient labels.
+  @[JSON::Field(key: "gradientLabelOffset")]
+  property gradient_label_offset : Float64?
+
+  # The color of the gradient stroke, can be in hex color code or regular color name.
+  @[JSON::Field(key: "gradientStrokeColor")]
+  property gradient_stroke_color : String?
+
+  # The width of the gradient stroke, in pixels.
+  @[JSON::Field(key: "gradientStrokeWidth")]
+  property gradient_stroke_width : Float64?
+
+  # The width of the gradient, in pixels.
+  @[JSON::Field(key: "gradientWidth")]
+  property gradient_width : Float64?
+
+  # The alignment of the legend label, can be left, middle or right.
+  @[JSON::Field(key: "labelAlign")]
+  property label_align : String?
+
+  # The position of the baseline of legend label, can be top, middle or bottom.
+  @[JSON::Field(key: "labelBaseline")]
+  property label_baseline : String?
+
+  # The color of the legend label, can be in hex color code or regular color name.
+  @[JSON::Field(key: "labelColor")]
+  property label_color : String?
+
+  # The font of the legend label.
+  @[JSON::Field(key: "labelFont")]
+  property label_font : String?
+
+  # The font size of legend label.
+  #
+  # __Default value:__ `10`.
+  @[JSON::Field(key: "labelFontSize")]
+  property label_font_size : Float64?
+
+  # Maximum allowed pixel width of axis tick labels.
+  @[JSON::Field(key: "labelLimit")]
+  property label_limit : Float64?
+
+  # The offset of the legend label.
+  @[JSON::Field(key: "labelOffset")]
+  property label_offset : Float64?
+
+  # The offset, in pixels, by which to displace the legend from the edge of the enclosing
+  # group or data rectangle.
+  #
+  # __Default value:__  `0`
+  property offset : Float64?
+
+  # The orientation of the legend, which determines how the legend is positioned within the
+  # scene. One of "left", "right", "top-left", "top-right", "bottom-left", "bottom-right",
+  # "none".
+  #
+  # __Default value:__ `"right"`
+  property orient : String?
+
+  # The padding, in pixels, between the legend and axis.
+  property padding : Float64?
+
+  # Whether month names and weekday names should be abbreviated.
+  #
+  # __Default value:__  `false`
+  @[JSON::Field(key: "shortTimeLabels")]
+  property short_time_labels : Bool?
+
+  # Border stroke color for the full legend.
+  @[JSON::Field(key: "strokeColor")]
+  property stroke_color : String?
+
+  # Border stroke dash pattern for the full legend.
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # Border stroke width for the full legend.
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+
+  # The color of the legend symbol,
+  @[JSON::Field(key: "symbolColor")]
+  property symbol_color : String?
+
+  # The size of the legend symbol, in pixels.
+  @[JSON::Field(key: "symbolSize")]
+  property symbol_size : Float64?
+
+  # The width of the symbol's stroke.
+  @[JSON::Field(key: "symbolStrokeWidth")]
+  property symbol_stroke_width : Float64?
+
+  # Default shape type (such as "circle") for legend symbols.
+  @[JSON::Field(key: "symbolType")]
+  property symbol_type : String?
+
+  # Horizontal text alignment for legend titles.
+  @[JSON::Field(key: "titleAlign")]
+  property title_align : String?
+
+  # Vertical text baseline for legend titles.
+  @[JSON::Field(key: "titleBaseline")]
+  property title_baseline : String?
+
+  # The color of the legend title, can be in hex color code or regular color name.
+  @[JSON::Field(key: "titleColor")]
+  property title_color : String?
+
+  # The font of the legend title.
+  @[JSON::Field(key: "titleFont")]
+  property title_font : String?
+
+  # The font size of the legend title.
+  @[JSON::Field(key: "titleFontSize")]
+  property title_font_size : Float64?
+
+  # The font weight of the legend title.
+  @[JSON::Field(key: "titleFontWeight")]
+  property title_font_weight : TitleFontWeightUnion?
+
+  # Maximum allowed pixel width of axis titles.
+  @[JSON::Field(key: "titleLimit")]
+  property title_limit : Float64?
+
+  # The padding, in pixels, between title and legend.
+  @[JSON::Field(key: "titlePadding")]
+  property title_padding : Float64?
+end
+
+class PaddingClass
+  include JSON::Serializable
+
+  property bottom : Float64?
+
+  property left : Float64?
+
+  property right : Float64?
+
+  property top : Float64?
+end
+
+# Projection configuration, which determines default properties for all
+# [projections](projection.html). For a full list of projection configuration options,
+# please see the [corresponding section of the projection
+# documentation](projection.html#config).
+#
+# Any property of Projection can be in config
+class ProjectionConfig
+  include JSON::Serializable
+
+  # Sets the projection’s center to the specified center, a two-element array of longitude
+  # and latitude in degrees.
+  #
+  # __Default value:__ `[0, 0]`
+  property center : Array(Float64)?
+
+  # Sets the projection’s clipping circle radius to the specified angle in degrees. If
+  # `null`, switches to [antimeridian](http://bl.ocks.org/mbostock/3788999) cutting rather
+  # than small-circle clipping.
+  @[JSON::Field(key: "clipAngle")]
+  property clip_angle : Float64?
+
+  # Sets the projection’s viewport clip extent to the specified bounds in pixels. The extent
+  # bounds are specified as an array `[[x0, y0], [x1, y1]]`, where `x0` is the left-side of
+  # the viewport, `y0` is the top, `x1` is the right and `y1` is the bottom. If `null`, no
+  # viewport clipping is performed.
+  @[JSON::Field(key: "clipExtent")]
+  property clip_extent : Array(Array(Float64))?
+
+  property coefficient : Float64?
+
+  property distance : Float64?
+
+  property fraction : Float64?
+
+  property lobes : Float64?
+
+  property parallel : Float64?
+
+  # Sets the threshold for the projection’s [adaptive
+  # resampling](http://bl.ocks.org/mbostock/3795544) to the specified value in pixels. This
+  # value corresponds to the [Douglas–Peucker
+  # distance](http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm).
+  # If precision is not specified, returns the projection’s current resampling precision
+  # which defaults to `√0.5 ≅ 0.70710…`.
+  property precision : Hash(String, PrecisionValue)?
+
+  property radius : Float64?
+
+  property ratio : Float64?
+
+  # Sets the projection’s three-axis rotation to the specified angles, which must be a two-
+  # or three-element array of numbers [`lambda`, `phi`, `gamma`] specifying the rotation
+  # angles in degrees about each spherical axis. (These correspond to yaw, pitch and roll.)
+  #
+  # __Default value:__ `[0, 0, 0]`
+  property rotate : Array(Float64)?
+
+  property spacing : Float64?
+
+  property tilt : Float64?
+
+  # The cartographic projection to use. This value is case-insensitive, for example
+  # `"albers"` and `"Albers"` indicate the same projection type. You can find all valid
+  # projection types [in the
+  # documentation](https://vega.github.io/vega-lite/docs/projection.html#projection-types).
+  #
+  # __Default value:__ `mercator`
+  @[JSON::Field(key: "type")]
+  property projection_config_type : String?
+end
+
+class VgScheme
+  include JSON::Serializable
+
+  property count : Float64?
+
+  property extent : Array(Float64)?
+
+  property scheme : String?
+
+  property step : Float64?
+end
+
+# Scale configuration determines default properties for all [scales](scale.html). For a
+# full list of scale configuration options, please see the [corresponding section of the
+# scale documentation](scale.html#config).
+class ScaleConfig
+  include JSON::Serializable
+
+  # Default inner padding for `x` and `y` band-ordinal scales.
+  #
+  # __Default value:__ `0.1`
+  @[JSON::Field(key: "bandPaddingInner")]
+  property band_padding_inner : Float64?
+
+  # Default outer padding for `x` and `y` band-ordinal scales.
+  # If not specified, by default, band scale's paddingOuter is paddingInner/2.
+  @[JSON::Field(key: "bandPaddingOuter")]
+  property band_padding_outer : Float64?
+
+  # If true, values that exceed the data domain are clamped to either the minimum or maximum
+  # range value
+  property clamp : Bool?
+
+  # Default padding for continuous scales.
+  #
+  # __Default:__ `5` for continuous x-scale of a vertical bar and continuous y-scale of a
+  # horizontal bar.; `0` otherwise.
+  @[JSON::Field(key: "continuousPadding")]
+  property continuous_padding : Float64?
+
+  # The default max value for mapping quantitative fields to bar's size/bandSize.
+  #
+  # If undefined (default), we will use the scale's `rangeStep` - 1.
+  @[JSON::Field(key: "maxBandSize")]
+  property max_band_size : Float64?
+
+  # The default max value for mapping quantitative fields to text's size/fontSize.
+  #
+  # __Default value:__ `40`
+  @[JSON::Field(key: "maxFontSize")]
+  property max_font_size : Float64?
+
+  # Default max opacity for mapping a field to opacity.
+  #
+  # __Default value:__ `0.8`
+  @[JSON::Field(key: "maxOpacity")]
+  property max_opacity : Float64?
+
+  # Default max value for point size scale.
+  @[JSON::Field(key: "maxSize")]
+  property max_size : Float64?
+
+  # Default max strokeWidth for strokeWidth  (or rule/line's size) scale.
+  #
+  # __Default value:__ `4`
+  @[JSON::Field(key: "maxStrokeWidth")]
+  property max_stroke_width : Float64?
+
+  # The default min value for mapping quantitative fields to bar and tick's size/bandSize
+  # scale with zero=false.
+  #
+  # __Default value:__ `2`
+  @[JSON::Field(key: "minBandSize")]
+  property min_band_size : Float64?
+
+  # The default min value for mapping quantitative fields to tick's size/fontSize scale with
+  # zero=false
+  #
+  # __Default value:__ `8`
+  @[JSON::Field(key: "minFontSize")]
+  property min_font_size : Float64?
+
+  # Default minimum opacity for mapping a field to opacity.
+  #
+  # __Default value:__ `0.3`
+  @[JSON::Field(key: "minOpacity")]
+  property min_opacity : Float64?
+
+  # Default minimum value for point size scale with zero=false.
+  #
+  # __Default value:__ `9`
+  @[JSON::Field(key: "minSize")]
+  property min_size : Float64?
+
+  # Default minimum strokeWidth for strokeWidth (or rule/line's size) scale with zero=false.
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "minStrokeWidth")]
+  property min_stroke_width : Float64?
+
+  # Default outer padding for `x` and `y` point-ordinal scales.
+  #
+  # __Default value:__ `0.5`
+  @[JSON::Field(key: "pointPadding")]
+  property point_padding : Float64?
+
+  # Default range step for band and point scales of (1) the `y` channel
+  # and (2) the `x` channel when the mark is not `text`.
+  #
+  # __Default value:__ `21`
+  @[JSON::Field(key: "rangeStep")]
+  property range_step : Float64?
+
+  # If true, rounds numeric output values to integers.
+  # This can be helpful for snapping to the pixel grid.
+  # (Only available for `x`, `y`, and `size` scales.)
+  property round : Bool?
+
+  # Default range step for `x` band and point scales of text marks.
+  #
+  # __Default value:__ `90`
+  @[JSON::Field(key: "textXRangeStep")]
+  property text_x_range_step : Float64?
+
+  # Use the source data range before aggregation as scale domain instead of aggregated data
+  # for aggregate axis.
+  #
+  # This is equivalent to setting `domain` to `"unaggregate"` for aggregated _quantitative_
+  # fields by default.
+  #
+  # This property only works with aggregate functions that produce values within the raw data
+  # domain (`"mean"`, `"average"`, `"median"`, `"q1"`, `"q3"`, `"min"`, `"max"`). For other
+  # aggregations that produce values outside of the raw data domain (e.g. `"count"`,
+  # `"sum"`), this property is ignored.
+  #
+  # __Default value:__ `false`
+  @[JSON::Field(key: "useUnaggregatedDomain")]
+  property use_unaggregated_domain : Bool?
+end
+
+# An object hash for defining default properties for each type of selections.
+class SelectionConfig
+  include JSON::Serializable
+
+  # The default definition for an [`interval`](selection.html#type) selection. All properties
+  # and transformations
+  # for an interval selection definition (except `type`) may be specified here.
+  #
+  # For instance, setting `interval` to `{"translate": false}` disables the ability to move
+  # interval selections by default.
+  property interval : IntervalSelectionConfig?
+
+  # The default definition for a [`multi`](selection.html#type) selection. All properties and
+  # transformations
+  # for a multi selection definition (except `type`) may be specified here.
+  #
+  # For instance, setting `multi` to `{"toggle": "event.altKey"}` adds additional values to
+  # multi selections when clicking with the alt-key pressed by default.
+  property multi : MultiSelectionConfig?
+
+  # The default definition for a [`single`](selection.html#type) selection. All properties
+  # and transformations
+  # for a single selection definition (except `type`) may be specified here.
+  #
+  # For instance, setting `single` to `{"on": "dblclick"}` populates single selections on
+  # double-click by default.
+  property single : SingleSelectionConfig?
+end
+
+# The default definition for an [`interval`](selection.html#type) selection. All properties
+# and transformations
+# for an interval selection definition (except `type`) may be specified here.
+#
+# For instance, setting `interval` to `{"translate": false}` disables the ability to move
+# interval selections by default.
+class IntervalSelectionConfig
+  include JSON::Serializable
+
+  # Establishes a two-way binding between the interval selection and the scales
+  # used within the same view. This allows a user to interactively pan and
+  # zoom the view.
+  property bind : String?
+
+  # By default, all data values are considered to lie within an empty selection.
+  # When set to `none`, empty selections contain no data values.
+  property empty : String?
+
+  # An array of encoding channels. The corresponding data field values
+  # must match for a data tuple to fall within the selection.
+  property encodings : Array(String)?
+
+  # An array of field names whose values must match for a data tuple to
+  # fall within the selection.
+  property fields : Array(String)?
+
+  # An interval selection also adds a rectangle mark to depict the
+  # extents of the interval. The `mark` property can be used to customize the
+  # appearance of the mark.
+  property mark : BrushConfig?
+
+  # A [Vega event stream](https://vega.github.io/vega/docs/event-streams/) (object or
+  # selector) that triggers the selection.
+  # For interval selections, the event stream must specify a [start and
+  # end](https://vega.github.io/vega/docs/event-streams/#between-filters).
+  property on : JSON::Any?
+
+  # With layered and multi-view displays, a strategy that determines how
+  # selections' data queries are resolved when applied in a filter transform,
+  # conditional encoding rule, or scale domain.
+  property resolve : String?
+
+  # When truthy, allows a user to interactively move an interval selection
+  # back-and-forth. Can be `true`, `false` (to disable panning), or a
+  # [Vega event stream definition](https://vega.github.io/vega/docs/event-streams/)
+  # which must include a start and end event to trigger continuous panning.
+  #
+  # __Default value:__ `true`, which corresponds to
+  # `[mousedown, window:mouseup] > window:mousemove!` which corresponds to
+  # clicks and dragging within an interval selection to reposition it.
+  property translate : Translate?
+
+  # When truthy, allows a user to interactively resize an interval selection.
+  # Can be `true`, `false` (to disable zooming), or a [Vega event stream
+  # definition](https://vega.github.io/vega/docs/event-streams/). Currently,
+  # only `wheel` events are supported.
+  #
+  #
+  # __Default value:__ `true`, which corresponds to `wheel!`.
+  property zoom : Translate?
+end
+
+# An interval selection also adds a rectangle mark to depict the
+# extents of the interval. The `mark` property can be used to customize the
+# appearance of the mark.
+class BrushConfig
+  include JSON::Serializable
+
+  # The fill color of the interval mark.
+  #
+  # __Default value:__ `#333333`
+  property fill : String?
+
+  # The fill opacity of the interval mark (a value between 0 and 1).
+  #
+  # __Default value:__ `0.125`
+  @[JSON::Field(key: "fillOpacity")]
+  property fill_opacity : Float64?
+
+  # The stroke color of the interval mark.
+  #
+  # __Default value:__ `#ffffff`
+  property stroke : String?
+
+  # An array of alternating stroke and space lengths,
+  # for creating dashed or dotted lines.
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # The offset (in pixels) with which to begin drawing the stroke dash array.
+  @[JSON::Field(key: "strokeDashOffset")]
+  property stroke_dash_offset : Float64?
+
+  # The stroke opacity of the interval mark (a value between 0 and 1).
+  @[JSON::Field(key: "strokeOpacity")]
+  property stroke_opacity : Float64?
+
+  # The stroke width of the interval mark.
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+end
+
+# The default definition for a [`multi`](selection.html#type) selection. All properties and
+# transformations
+# for a multi selection definition (except `type`) may be specified here.
+#
+# For instance, setting `multi` to `{"toggle": "event.altKey"}` adds additional values to
+# multi selections when clicking with the alt-key pressed by default.
+class MultiSelectionConfig
+  include JSON::Serializable
+
+  # By default, all data values are considered to lie within an empty selection.
+  # When set to `none`, empty selections contain no data values.
+  property empty : String?
+
+  # An array of encoding channels. The corresponding data field values
+  # must match for a data tuple to fall within the selection.
+  property encodings : Array(String)?
+
+  # An array of field names whose values must match for a data tuple to
+  # fall within the selection.
+  property fields : Array(String)?
+
+  # When true, an invisible voronoi diagram is computed to accelerate discrete
+  # selection. The data value _nearest_ the mouse cursor is added to the selection.
+  #
+  # See the [nearest transform](nearest.html) documentation for more information.
+  property nearest : Bool?
+
+  # A [Vega event stream](https://vega.github.io/vega/docs/event-streams/) (object or
+  # selector) that triggers the selection.
+  # For interval selections, the event stream must specify a [start and
+  # end](https://vega.github.io/vega/docs/event-streams/#between-filters).
+  property on : JSON::Any?
+
+  # With layered and multi-view displays, a strategy that determines how
+  # selections' data queries are resolved when applied in a filter transform,
+  # conditional encoding rule, or scale domain.
+  property resolve : String?
+
+  # Controls whether data values should be toggled or only ever inserted into
+  # multi selections. Can be `true`, `false` (for insertion only), or a
+  # [Vega expression](https://vega.github.io/vega/docs/expressions/).
+  #
+  # __Default value:__ `true`, which corresponds to `event.shiftKey` (i.e.,
+  # data values are toggled when a user interacts with the shift-key pressed).
+  #
+  # See the [toggle transform](toggle.html) documentation for more information.
+  property toggle : Translate?
+end
+
+# The default definition for a [`single`](selection.html#type) selection. All properties
+# and transformations
+# for a single selection definition (except `type`) may be specified here.
+#
+# For instance, setting `single` to `{"on": "dblclick"}` populates single selections on
+# double-click by default.
+class SingleSelectionConfig
+  include JSON::Serializable
+
+  # Establish a two-way binding between a single selection and input elements
+  # (also known as dynamic query widgets). A binding takes the form of
+  # Vega's [input element binding definition](https://vega.github.io/vega/docs/signals/#bind)
+  # or can be a mapping between projected field/encodings and binding definitions.
+  #
+  # See the [bind transform](bind.html) documentation for more information.
+  property bind : Hash(String, VgBinding)?
+
+  # By default, all data values are considered to lie within an empty selection.
+  # When set to `none`, empty selections contain no data values.
+  property empty : String?
+
+  # An array of encoding channels. The corresponding data field values
+  # must match for a data tuple to fall within the selection.
+  property encodings : Array(String)?
+
+  # An array of field names whose values must match for a data tuple to
+  # fall within the selection.
+  property fields : Array(String)?
+
+  # When true, an invisible voronoi diagram is computed to accelerate discrete
+  # selection. The data value _nearest_ the mouse cursor is added to the selection.
+  #
+  # See the [nearest transform](nearest.html) documentation for more information.
+  property nearest : Bool?
+
+  # A [Vega event stream](https://vega.github.io/vega/docs/event-streams/) (object or
+  # selector) that triggers the selection.
+  # For interval selections, the event stream must specify a [start and
+  # end](https://vega.github.io/vega/docs/event-streams/#between-filters).
+  property on : JSON::Any?
+
+  # With layered and multi-view displays, a strategy that determines how
+  # selections' data queries are resolved when applied in a filter transform,
+  # conditional encoding rule, or scale domain.
+  property resolve : String?
+end
+
+class VgBinding
+  include JSON::Serializable
+
+  property element : String?
+
+  property input : String
+
+  property options : Array(String)?
+
+  property max : Float64?
+
+  property min : Float64?
+
+  property step : Float64?
+end
+
+class VgMarkConfig
+  include JSON::Serializable
+
+  # The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
+  property align : String?
+
+  # The rotation angle of the text, in degrees.
+  property angle : Float64?
+
+  # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
+  #
+  # __Default value:__ `"middle"`
+  property baseline : String?
+
+  # The mouse cursor used over the mark. Any valid [CSS cursor
+  # type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
+  property cursor : String?
+
+  # The horizontal offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dx : Float64?
+
+  # The vertical offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dy : Float64?
+
+  # Default Fill Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property fill : String?
+
+  # The fill opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "fillOpacity")]
+  property fill_opacity : Float64?
+
+  # The typeface to set the text in (e.g., `"Helvetica Neue"`).
+  property font : String?
+
+  # The font size, in pixels.
+  @[JSON::Field(key: "fontSize")]
+  property font_size : Float64?
+
+  # The font style (e.g., `"italic"`).
+  @[JSON::Field(key: "fontStyle")]
+  property font_style : String?
+
+  # The font weight (e.g., `"bold"`).
+  @[JSON::Field(key: "fontWeight")]
+  property font_weight : FontWeightUnion?
+
+  # A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
+  property href : String?
+
+  # The line interpolation method to use for line and area marks. One of the following:
+  # - `"linear"`: piecewise linear segments, as in a polyline.
+  # - `"linear-closed"`: close the linear segments to form a polygon.
+  # - `"step"`: alternate between horizontal and vertical segments, as in a step function.
+  # - `"step-before"`: alternate between vertical and horizontal segments, as in a step
+  # function.
+  # - `"step-after"`: alternate between horizontal and vertical segments, as in a step
+  # function.
+  # - `"basis"`: a B-spline, with control point duplication on the ends.
+  # - `"basis-open"`: an open B-spline; may not intersect the start or end.
+  # - `"basis-closed"`: a closed B-spline, as in a loop.
+  # - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
+  # - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
+  # will intersect other control points.
+  # - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
+  # - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
+  # spline.
+  # - `"monotone"`: cubic interpolation that preserves monotonicity in y.
+  property interpolate : String?
+
+  # The maximum length of the text mark in pixels (default 0, indicating no limit). The text
+  # value will be automatically truncated if the rendered size exceeds the limit.
+  property limit : Float64?
+
+  # The overall opacity (value between [0,1]).
+  #
+  # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
+  # `square` marks or layered `bar` charts and `1` otherwise.
+  property opacity : Float64?
+
+  # The orientation of a non-stacked bar, tick, area, and line charts.
+  # The value is either horizontal (default) or vertical.
+  # - For bar, rule and tick, this determines whether the size of the bar and tick
+  # should be applied to x or y dimension.
+  # - For area, this property determines the orient property of the Vega output.
+  # - For line, this property determines the sort order of the points in the line
+  # if `config.sortLineBy` is not specified.
+  # For stacked charts, this is always determined by the orientation of the stack;
+  # therefore explicitly specified value will be ignored.
+  property orient : String?
+
+  # Polar coordinate radial offset, in pixels, of the text label from the origin determined
+  # by the `x` and `y` properties.
+  property radius : Float64?
+
+  # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
+  # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
+  #
+  # __Default value:__ `"circle"`
+  property shape : String?
+
+  # The pixel area each the point/circle/square.
+  # For example: in the case of circles, the radius is determined in part by the square root
+  # of the size value.
+  #
+  # __Default value:__ `30`
+  property size : Float64?
+
+  # Default Stroke Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property stroke : String?
+
+  # An array of alternating stroke, space lengths for creating dashed or dotted lines.
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # The offset (in pixels) into which to begin drawing with the stroke dash array.
+  @[JSON::Field(key: "strokeDashOffset")]
+  property stroke_dash_offset : Float64?
+
+  # The stroke opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "strokeOpacity")]
+  property stroke_opacity : Float64?
+
+  # The stroke width, in pixels.
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+
+  # Depending on the interpolation type, sets the tension parameter (for line and area marks).
+  property tension : Float64?
+
+  # Placeholder text if the `text` channel is not specified
+  property text : String?
+
+  # Polar coordinate angle, in radians, of the text label from the origin determined by the
+  # `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
+  # `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
+  # indicating "north".
+  property theta : Float64?
+end
+
+# Text-Specific Config
+class TextConfig
+  include JSON::Serializable
+
+  # The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
+  property align : String?
+
+  # The rotation angle of the text, in degrees.
+  property angle : Float64?
+
+  # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
+  #
+  # __Default value:__ `"middle"`
+  property baseline : String?
+
+  # Default color.  Note that `fill` and `stroke` have higher precedence than `color` and
+  # will override `color`.
+  #
+  # __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property color : String?
+
+  # The mouse cursor used over the mark. Any valid [CSS cursor
+  # type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
+  property cursor : String?
+
+  # The horizontal offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dx : Float64?
+
+  # The vertical offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dy : Float64?
+
+  # Default Fill Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property fill : String?
+
+  # Whether the mark's color should be used as fill color instead of stroke color.
+  #
+  # __Default value:__ `true` for all marks except `point` and `false` for `point`.
+  #
+  # __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property filled : Bool?
+
+  # The fill opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "fillOpacity")]
+  property fill_opacity : Float64?
+
+  # The typeface to set the text in (e.g., `"Helvetica Neue"`).
+  property font : String?
+
+  # The font size, in pixels.
+  @[JSON::Field(key: "fontSize")]
+  property font_size : Float64?
+
+  # The font style (e.g., `"italic"`).
+  @[JSON::Field(key: "fontStyle")]
+  property font_style : String?
+
+  # The font weight (e.g., `"bold"`).
+  @[JSON::Field(key: "fontWeight")]
+  property font_weight : FontWeightUnion?
+
+  # A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
+  property href : String?
+
+  # The line interpolation method to use for line and area marks. One of the following:
+  # - `"linear"`: piecewise linear segments, as in a polyline.
+  # - `"linear-closed"`: close the linear segments to form a polygon.
+  # - `"step"`: alternate between horizontal and vertical segments, as in a step function.
+  # - `"step-before"`: alternate between vertical and horizontal segments, as in a step
+  # function.
+  # - `"step-after"`: alternate between horizontal and vertical segments, as in a step
+  # function.
+  # - `"basis"`: a B-spline, with control point duplication on the ends.
+  # - `"basis-open"`: an open B-spline; may not intersect the start or end.
+  # - `"basis-closed"`: a closed B-spline, as in a loop.
+  # - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
+  # - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
+  # will intersect other control points.
+  # - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
+  # - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
+  # spline.
+  # - `"monotone"`: cubic interpolation that preserves monotonicity in y.
+  property interpolate : String?
+
+  # The maximum length of the text mark in pixels (default 0, indicating no limit). The text
+  # value will be automatically truncated if the rendered size exceeds the limit.
+  property limit : Float64?
+
+  # The overall opacity (value between [0,1]).
+  #
+  # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
+  # `square` marks or layered `bar` charts and `1` otherwise.
+  property opacity : Float64?
+
+  # The orientation of a non-stacked bar, tick, area, and line charts.
+  # The value is either horizontal (default) or vertical.
+  # - For bar, rule and tick, this determines whether the size of the bar and tick
+  # should be applied to x or y dimension.
+  # - For area, this property determines the orient property of the Vega output.
+  # - For line, this property determines the sort order of the points in the line
+  # if `config.sortLineBy` is not specified.
+  # For stacked charts, this is always determined by the orientation of the stack;
+  # therefore explicitly specified value will be ignored.
+  property orient : String?
+
+  # Polar coordinate radial offset, in pixels, of the text label from the origin determined
+  # by the `x` and `y` properties.
+  property radius : Float64?
+
+  # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
+  # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
+  #
+  # __Default value:__ `"circle"`
+  property shape : String?
+
+  # Whether month names and weekday names should be abbreviated.
+  @[JSON::Field(key: "shortTimeLabels")]
+  property short_time_labels : Bool?
+
+  # The pixel area each the point/circle/square.
+  # For example: in the case of circles, the radius is determined in part by the square root
+  # of the size value.
+  #
+  # __Default value:__ `30`
+  property size : Float64?
+
+  # Default Stroke Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property stroke : String?
+
+  # An array of alternating stroke, space lengths for creating dashed or dotted lines.
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # The offset (in pixels) into which to begin drawing with the stroke dash array.
+  @[JSON::Field(key: "strokeDashOffset")]
+  property stroke_dash_offset : Float64?
+
+  # The stroke opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "strokeOpacity")]
+  property stroke_opacity : Float64?
+
+  # The stroke width, in pixels.
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+
+  # Depending on the interpolation type, sets the tension parameter (for line and area marks).
+  property tension : Float64?
+
+  # Placeholder text if the `text` channel is not specified
+  property text : String?
+
+  # Polar coordinate angle, in radians, of the text label from the origin determined by the
+  # `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
+  # `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
+  # indicating "north".
+  property theta : Float64?
+end
+
+# Tick-Specific Config
+class TickConfig
+  include JSON::Serializable
+
+  # The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
+  property align : String?
+
+  # The rotation angle of the text, in degrees.
+  property angle : Float64?
+
+  # The width of the ticks.
+  #
+  # __Default value:__  2/3 of rangeStep.
+  @[JSON::Field(key: "bandSize")]
+  property band_size : Float64?
+
+  # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
+  #
+  # __Default value:__ `"middle"`
+  property baseline : String?
+
+  # Default color.  Note that `fill` and `stroke` have higher precedence than `color` and
+  # will override `color`.
+  #
+  # __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property color : String?
+
+  # The mouse cursor used over the mark. Any valid [CSS cursor
+  # type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
+  property cursor : String?
+
+  # The horizontal offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dx : Float64?
+
+  # The vertical offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dy : Float64?
+
+  # Default Fill Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property fill : String?
+
+  # Whether the mark's color should be used as fill color instead of stroke color.
+  #
+  # __Default value:__ `true` for all marks except `point` and `false` for `point`.
+  #
+  # __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property filled : Bool?
+
+  # The fill opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "fillOpacity")]
+  property fill_opacity : Float64?
+
+  # The typeface to set the text in (e.g., `"Helvetica Neue"`).
+  property font : String?
+
+  # The font size, in pixels.
+  @[JSON::Field(key: "fontSize")]
+  property font_size : Float64?
+
+  # The font style (e.g., `"italic"`).
+  @[JSON::Field(key: "fontStyle")]
+  property font_style : String?
+
+  # The font weight (e.g., `"bold"`).
+  @[JSON::Field(key: "fontWeight")]
+  property font_weight : FontWeightUnion?
+
+  # A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
+  property href : String?
+
+  # The line interpolation method to use for line and area marks. One of the following:
+  # - `"linear"`: piecewise linear segments, as in a polyline.
+  # - `"linear-closed"`: close the linear segments to form a polygon.
+  # - `"step"`: alternate between horizontal and vertical segments, as in a step function.
+  # - `"step-before"`: alternate between vertical and horizontal segments, as in a step
+  # function.
+  # - `"step-after"`: alternate between horizontal and vertical segments, as in a step
+  # function.
+  # - `"basis"`: a B-spline, with control point duplication on the ends.
+  # - `"basis-open"`: an open B-spline; may not intersect the start or end.
+  # - `"basis-closed"`: a closed B-spline, as in a loop.
+  # - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
+  # - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
+  # will intersect other control points.
+  # - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
+  # - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
+  # spline.
+  # - `"monotone"`: cubic interpolation that preserves monotonicity in y.
+  property interpolate : String?
+
+  # The maximum length of the text mark in pixels (default 0, indicating no limit). The text
+  # value will be automatically truncated if the rendered size exceeds the limit.
+  property limit : Float64?
+
+  # The overall opacity (value between [0,1]).
+  #
+  # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
+  # `square` marks or layered `bar` charts and `1` otherwise.
+  property opacity : Float64?
+
+  # The orientation of a non-stacked bar, tick, area, and line charts.
+  # The value is either horizontal (default) or vertical.
+  # - For bar, rule and tick, this determines whether the size of the bar and tick
+  # should be applied to x or y dimension.
+  # - For area, this property determines the orient property of the Vega output.
+  # - For line, this property determines the sort order of the points in the line
+  # if `config.sortLineBy` is not specified.
+  # For stacked charts, this is always determined by the orientation of the stack;
+  # therefore explicitly specified value will be ignored.
+  property orient : String?
+
+  # Polar coordinate radial offset, in pixels, of the text label from the origin determined
+  # by the `x` and `y` properties.
+  property radius : Float64?
+
+  # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
+  # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
+  #
+  # __Default value:__ `"circle"`
+  property shape : String?
+
+  # The pixel area each the point/circle/square.
+  # For example: in the case of circles, the radius is determined in part by the square root
+  # of the size value.
+  #
+  # __Default value:__ `30`
+  property size : Float64?
+
+  # Default Stroke Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property stroke : String?
+
+  # An array of alternating stroke, space lengths for creating dashed or dotted lines.
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # The offset (in pixels) into which to begin drawing with the stroke dash array.
+  @[JSON::Field(key: "strokeDashOffset")]
+  property stroke_dash_offset : Float64?
+
+  # The stroke opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "strokeOpacity")]
+  property stroke_opacity : Float64?
+
+  # The stroke width, in pixels.
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+
+  # Depending on the interpolation type, sets the tension parameter (for line and area marks).
+  property tension : Float64?
+
+  # Placeholder text if the `text` channel is not specified
+  property text : String?
+
+  # Polar coordinate angle, in radians, of the text label from the origin determined by the
+  # `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
+  # `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
+  # indicating "north".
+  property theta : Float64?
+
+  # Thickness of the tick mark.
+  #
+  # __Default value:__  `1`
+  property thickness : Float64?
+end
+
+# Title configuration, which determines default properties for all [titles](title.html).
+# For a full list of title configuration options, please see the [corresponding section of
+# the title documentation](title.html#config).
+class VgTitleConfig
+  include JSON::Serializable
+
+  # The anchor position for placing the title. One of `"start"`, `"middle"`, or `"end"`. For
+  # example, with an orientation of top these anchor positions map to a left-, center-, or
+  # right-aligned title.
+  #
+  # __Default value:__ `"middle"` for [single](spec.html) and [layered](layer.html) views.
+  # `"start"` for other composite views.
+  #
+  # __Note:__ [For now](https://github.com/vega/vega-lite/issues/2875), `anchor` is only
+  # customizable only for [single](spec.html) and [layered](layer.html) views.  For other
+  # composite views, `anchor` is always `"start"`.
+  property anchor : String?
+
+  # Angle in degrees of title text.
+  property angle : Float64?
+
+  # Vertical text baseline for title text.
+  property baseline : String?
+
+  # Text color for title text.
+  property color : String?
+
+  # Font name for title text.
+  property font : String?
+
+  # Font size in pixels for title text.
+  #
+  # __Default value:__ `10`.
+  @[JSON::Field(key: "fontSize")]
+  property font_size : Float64?
+
+  # Font weight for title text.
+  @[JSON::Field(key: "fontWeight")]
+  property font_weight : FontWeightUnion?
+
+  # The maximum allowed length in pixels of legend labels.
+  property limit : Float64?
+
+  # Offset in pixels of the title from the chart body and axes.
+  property offset : Float64?
+
+  # Default title orientation ("top", "bottom", "left", or "right")
+  property orient : String?
+end
+
+# Default properties for [single view plots](spec.html#single).
+class ViewConfig
+  include JSON::Serializable
+
+  # Whether the view should be clipped.
+  property clip : Bool?
+
+  # The fill color.
+  #
+  # __Default value:__ (none)
+  property fill : String?
+
+  # The fill opacity (value between [0,1]).
+  #
+  # __Default value:__ (none)
+  @[JSON::Field(key: "fillOpacity")]
+  property fill_opacity : Float64?
+
+  # The default height of the single plot or each plot in a trellis plot when the
+  # visualization has a continuous (non-ordinal) y-scale with `rangeStep` = `null`.
+  #
+  # __Default value:__ `200`
+  property height : Float64?
+
+  # The stroke color.
+  #
+  # __Default value:__ (none)
+  property stroke : String?
+
+  # An array of alternating stroke, space lengths for creating dashed or dotted lines.
+  #
+  # __Default value:__ (none)
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # The offset (in pixels) into which to begin drawing with the stroke dash array.
+  #
+  # __Default value:__ (none)
+  @[JSON::Field(key: "strokeDashOffset")]
+  property stroke_dash_offset : Float64?
+
+  # The stroke opacity (value between [0,1]).
+  #
+  # __Default value:__ (none)
+  @[JSON::Field(key: "strokeOpacity")]
+  property stroke_opacity : Float64?
+
+  # The stroke width, in pixels.
+  #
+  # __Default value:__ (none)
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+
+  # The default width of the single plot or each plot in a trellis plot when the
+  # visualization has a continuous (non-ordinal) x-scale or ordinal x-scale with `rangeStep`
+  # = `null`.
+  #
+  # __Default value:__ `200`
+  property width : Float64?
+end
+
+# An object describing the data source
+#
+# Secondary data source to lookup in.
+class Data
+  include JSON::Serializable
+
+  # An object that specifies the format for parsing the data file.
+  #
+  # An object that specifies the format for parsing the data values.
+  #
+  # An object that specifies the format for parsing the data.
+  property format : DataFormat?
+
+  # An URL from which to load the data set. Use the `format.type` property
+  # to ensure the loaded data is correctly parsed.
+  property url : String?
+
+  # The full data set, included inline. This can be an array of objects or primitive values
+  # or a string.
+  # Arrays of primitive values are ingested as objects with a `data` property. Strings are
+  # parsed according to the specified format type.
+  property values : Values?
+
+  # Provide a placeholder name and bind data at runtime.
+  property name : String?
+end
+
+# An object that specifies the format for parsing the data file.
+#
+# An object that specifies the format for parsing the data values.
+#
+# An object that specifies the format for parsing the data.
+class DataFormat
+  include JSON::Serializable
+
+  # If set to auto (the default), perform automatic type inference to determine the desired
+  # data types.
+  # Alternatively, a parsing directive object can be provided for explicit data types. Each
+  # property of the object corresponds to a field name, and the value to the desired data
+  # type (one of `"number"`, `"boolean"` or `"date"`).
+  # For example, `"parse": {"modified_on": "date"}` parses the `modified_on` field in each
+  # input record a Date value.
+  #
+  # For `"date"`, we parse data based using Javascript's
+  # [`Date.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse).
+  # For Specific date formats can be provided (e.g., `{foo: 'date:"%m%d%Y"'}`), using the
+  # [d3-time-format syntax](https://github.com/d3/d3-time-format#locale_format). UTC date
+  # format parsing is supported similarly (e.g., `{foo: 'utc:"%m%d%Y"'}`). See more about
+  # [UTC time](timeunit.html#utc)
+  property parse : ParseUnion?
+
+  # Type of input data: `"json"`, `"csv"`, `"tsv"`.
+  # The default format type is determined by the extension of the file URL.
+  # If no extension is detected, `"json"` will be used by default.
+  @[JSON::Field(key: "type")]
+  property data_format_type : String?
+
+  # The JSON property containing the desired data.
+  # This parameter can be used when the loaded JSON file may have surrounding structure or
+  # meta-data.
+  # For example `"property": "values.features"` is equivalent to retrieving
+  # `json.values.features`
+  # from the loaded JSON object.
+  property property : String?
+
+  # The name of the TopoJSON object set to convert to a GeoJSON feature collection.
+  # For example, in a map of the world, there may be an object set named `"countries"`.
+  # Using the feature property, we can extract this set and generate a GeoJSON feature object
+  # for each country.
+  property feature : String?
+
+  # The name of the TopoJSON object set to convert to mesh.
+  # Similar to the `feature` option, `mesh` extracts a named TopoJSON object set.
+  # Unlike the `feature` option, the corresponding geo data is returned as a single, unified
+  # mesh instance, not as individual GeoJSON features.
+  # Extracting a mesh is useful for more efficiently drawing borders or other geographic
+  # elements that you do not need to associate with specific regions such as individual
+  # countries, states or counties.
+  property mesh : String?
+end
+
+# A key-value mapping between encoding channels and definition of fields.
+class EncodingWithFacet
+  include JSON::Serializable
+
+  # Color of the marks – either fill or stroke color based on mark type.
+  # By default, `color` represents fill color for `"area"`, `"bar"`, `"tick"`,
+  # `"text"`, `"circle"`, and `"square"` / stroke color for `"line"` and `"point"`.
+  #
+  # __Default value:__ If undefined, the default color depends on [mark
+  # config](config.html#mark)'s `color` property.
+  #
+  # _Note:_ See the scale documentation for more information about customizing [color
+  # scheme](scale.html#scheme).
+  property color : MarkPropDefWithCondition?
+
+  # Horizontal facets for trellis plots.
+  property column : FacetFieldDef?
+
+  # Additional levels of detail for grouping data in aggregate views and
+  # in line and area marks without mapping data to a specific visual channel.
+  property detail : Detail?
+
+  # A URL to load upon mouse click.
+  property href : DefWithCondition?
+
+  # Opacity of the marks – either can be a value or a range.
+  #
+  # __Default value:__ If undefined, the default opacity depends on [mark
+  # config](config.html#mark)'s `opacity` property.
+  property opacity : MarkPropDefWithCondition?
+
+  # Stack order for stacked marks or order of data points in line marks for connected scatter
+  # plots.
+  #
+  # __Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating
+  # additional aggregation grouping.
+  property order : Order?
+
+  # Vertical facets for trellis plots.
+  property row : FacetFieldDef?
+
+  # For `point` marks the supported values are
+  # `"circle"` (default), `"square"`, `"cross"`, `"diamond"`, `"triangle-up"`,
+  # or `"triangle-down"`, or else a custom SVG path string.
+  # For `geoshape` marks it should be a field definition of the geojson data
+  #
+  # __Default value:__ If undefined, the default shape depends on [mark
+  # config](config.html#point-config)'s `shape` property.
+  property shape : MarkPropDefWithCondition?
+
+  # Size of the mark.
+  # - For `"point"`, `"square"` and `"circle"`, – the symbol size, or pixel area of the mark.
+  # - For `"bar"` and `"tick"` – the bar and tick's size.
+  # - For `"text"` – the text's font size.
+  # - Size is currently unsupported for `"line"`, `"area"`, and `"rect"`.
+  property size : MarkPropDefWithCondition?
+
+  # Text of the `text` mark.
+  property text : TextDefWithCondition?
+
+  # The tooltip text to show upon mouse hover.
+  property tooltip : TextDefWithCondition?
+
+  # X coordinates of the marks, or width of horizontal `"bar"` and `"area"`.
+  property x : XClass?
+
+  # X2 coordinates for ranged  `"area"`, `"bar"`, `"rect"`, and  `"rule"`.
+  property x2 : X2Class?
+
+  # Y coordinates of the marks, or height of vertical `"bar"` and `"area"`.
+  property y : XClass?
+
+  # Y2 coordinates for ranged  `"area"`, `"bar"`, `"rect"`, and  `"rule"`.
+  property y2 : X2Class?
+end
+
+# Color of the marks – either fill or stroke color based on mark type.
+# By default, `color` represents fill color for `"area"`, `"bar"`, `"tick"`,
+# `"text"`, `"circle"`, and `"square"` / stroke color for `"line"` and `"point"`.
+#
+# __Default value:__ If undefined, the default color depends on [mark
+# config](config.html#mark)'s `color` property.
+#
+# _Note:_ See the scale documentation for more information about customizing [color
+# scheme](scale.html#scheme).
+#
+# Opacity of the marks – either can be a value or a range.
+#
+# __Default value:__ If undefined, the default opacity depends on [mark
+# config](config.html#mark)'s `opacity` property.
+#
+# For `point` marks the supported values are
+# `"circle"` (default), `"square"`, `"cross"`, `"diamond"`, `"triangle-up"`,
+# or `"triangle-down"`, or else a custom SVG path string.
+# For `geoshape` marks it should be a field definition of the geojson data
+#
+# __Default value:__ If undefined, the default shape depends on [mark
+# config](config.html#point-config)'s `shape` property.
+#
+# Size of the mark.
+# - For `"point"`, `"square"` and `"circle"`, – the symbol size, or pixel area of the mark.
+# - For `"bar"` and `"tick"` – the bar and tick's size.
+# - For `"text"` – the text's font size.
+# - Size is currently unsupported for `"line"`, `"area"`, and `"rect"`.
+#
+# A FieldDef with Condition<ValueDef>
+# {
+# condition: {value: ...},
+# field: ...,
+# ...
+# }
+#
+# A ValueDef with Condition<ValueDef | FieldDef>
+# {
+# condition: {field: ...} | {value: ...},
+# value: ...,
+# }
+class MarkPropDefWithCondition
+  include JSON::Serializable
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # One or more value definition(s) with a selection predicate.
+  #
+  # __Note:__ A field definition's `condition` property can only contain [value
+  # definitions](encoding.html#value-def)
+  # since Vega-Lite only allows at most one encoded field per encoding channel.
+  #
+  # A field definition or one or more value definition(s) with a selection predicate.
+  property condition : ColorCondition?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # An object defining properties of the legend.
+  # If `null`, the legend for the encoding channel will be removed.
+  #
+  # __Default value:__ If undefined, default [legend properties](legend.html) are applied.
+  property legend : Legend?
+
+  # An object defining properties of the channel's scale, which is the function that
+  # transforms values in the data domain (numbers, dates, strings, etc) to visual values
+  # (pixels, colors, sizes) of the encoding channels.
+  #
+  # __Default value:__ If undefined, default [scale properties](scale.html) are applied.
+  property scale : Scale?
+
+  # Sort order for the encoded field.
+  # Supported `sort` values include `"ascending"`, `"descending"` and `null` (no sorting).
+  # For fields with discrete domains, `sort` can also be a [sort field definition
+  # object](sort.html#sort-field).
+  #
+  # __Default value:__ `"ascending"`
+  property sort : SortUnion?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property mark_prop_def_with_condition_type : String?
+
+  # A constant value in visual domain.
+  property value : ConditionalPredicateValueDefValue?
+end
+
+# Binning properties or boolean flag for determining whether to bin data or not.
+class BinParams
+  include JSON::Serializable
+
+  # The number base to use for automatic bin determination (default is base 10).
+  #
+  # __Default value:__ `10`
+  property base : Float64?
+
+  # Scale factors indicating allowable subdivisions. The default value is [5, 2], which
+  # indicates that for base 10 numbers (the default base), the method may consider dividing
+  # bin sizes by 5 and/or 2. For example, for an initial step size of 10, the method can
+  # check if bin sizes of 2 (= 10/5), 5 (= 10/2), or 1 (= 10/(5*2)) might also satisfy the
+  # given constraints.
+  #
+  # __Default value:__ `[5, 2]`
+  property divide : Array(Float64)?
+
+  # A two-element (`[min, max]`) array indicating the range of desired bin values.
+  property extent : Array(Float64)?
+
+  # Maximum number of bins.
+  #
+  # __Default value:__ `6` for `row`, `column` and `shape` channels; `10` for other channels
+  property maxbins : Float64?
+
+  # A minimum allowable step size (particularly useful for integer values).
+  property minstep : Float64?
+
+  # If true (the default), attempts to make the bin boundaries use human-friendly boundaries,
+  # such as multiples of ten.
+  property nice : Bool?
+
+  # An exact step size to use between bins.
+  #
+  # __Note:__ If provided, options such as maxbins will be ignored.
+  property step : Float64?
+
+  # An array of allowable step sizes to choose from.
+  property steps : Array(Float64)?
+end
+
+class ConditionalValueDef
+  include JSON::Serializable
+
+  property test : ConditionalValueDefLogicalOperandPredicate?
+
+  # A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
+  # `0` to `1` for opacity).
+  property value : ConditionalValueDefValue
+
+  # A [selection name](selection.html), or a series of [composed
+  # selections](selection.html#compose).
+  property selection : ConditionalValueDefSelectionOperand?
+end
+
+class Selection
+  include JSON::Serializable
+
+  property not : ConditionalValueDefSelectionOperand?
+
+  property and : Array(SelectionOperandElement)?
+
+  property or : Array(SelectionOperandElement)?
+end
+
+class Predicate
+  include JSON::Serializable
+
+  property not : ConditionalValueDefLogicalOperandPredicate?
+
+  property and : Array(LogicalOperandPredicateElement)?
+
+  property or : Array(LogicalOperandPredicateElement)?
+
+  # The value that the field should be equal to.
+  property equal : EqualUnion?
+
+  # Field to be filtered.
+  #
+  # Field to be filtered
+  property field : String?
+
+  # Time unit for the field to be filtered.
+  #
+  # time unit for the field to be filtered.
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # An array of inclusive minimum and maximum values
+  # for a field value of a data item to be included in the filtered data.
+  property range : Array(RangeElement?)?
+
+  # A set of values that the `field`'s value should be a member of,
+  # for a data item included in the filtered data.
+  @[JSON::Field(key: "oneOf")]
+  property one_of : Array(OneOfElement)?
+
+  # Filter using a selection name.
+  property selection : ConditionalValueDefSelectionOperand?
+end
+
+# Object for defining datetime in Vega-Lite Filter.
+# If both month and quarter are provided, month has higher precedence.
+# `day` cannot be combined with other date.
+# We accept string for month and day names.
+class DateTime
+  include JSON::Serializable
+
+  # Integer value representing the date from 1-31.
+  property date : Float64?
+
+  # Value representing the day of a week.  This can be one of: (1) integer value -- `1`
+  # represents Monday; (2) case-insensitive day name (e.g., `"Monday"`);  (3)
+  # case-insensitive, 3-character short day name (e.g., `"Mon"`).   <br/> **Warning:** A
+  # DateTime definition object with `day`** should not be combined with `year`, `quarter`,
+  # `month`, or `date`.
+  property day : Day?
+
+  # Integer value representing the hour of a day from 0-23.
+  property hours : Float64?
+
+  # Integer value representing the millisecond segment of time.
+  property milliseconds : Float64?
+
+  # Integer value representing the minute segment of time from 0-59.
+  property minutes : Float64?
+
+  # One of: (1) integer value representing the month from `1`-`12`. `1` represents January;
+  # (2) case-insensitive month name (e.g., `"January"`);  (3) case-insensitive, 3-character
+  # short month name (e.g., `"Jan"`).
+  property month : Month?
+
+  # Integer value representing the quarter of the year (from 1-4).
+  property quarter : Float64?
+
+  # Integer value representing the second segment (0-59) of a time value
+  property seconds : Float64?
+
+  # A boolean flag indicating if date time is in utc time. If false, the date time is in
+  # local time
+  property utc : Bool?
+
+  # Integer value representing the year.
+  property year : Float64?
+end
+
+class ConditionalPredicateMarkPropFieldDefClass
+  include JSON::Serializable
+
+  property test : ConditionalValueDefLogicalOperandPredicate?
+
+  # A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
+  # `0` to `1` for opacity).
+  property value : ConditionalPredicateValueDefValue?
+
+  # A [selection name](selection.html), or a series of [composed
+  # selections](selection.html#compose).
+  property selection : ConditionalValueDefSelectionOperand?
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # An object defining properties of the legend.
+  # If `null`, the legend for the encoding channel will be removed.
+  #
+  # __Default value:__ If undefined, default [legend properties](legend.html) are applied.
+  property legend : Legend?
+
+  # An object defining properties of the channel's scale, which is the function that
+  # transforms values in the data domain (numbers, dates, strings, etc) to visual values
+  # (pixels, colors, sizes) of the encoding channels.
+  #
+  # __Default value:__ If undefined, default [scale properties](scale.html) are applied.
+  property scale : Scale?
+
+  # Sort order for the encoded field.
+  # Supported `sort` values include `"ascending"`, `"descending"` and `null` (no sorting).
+  # For fields with discrete domains, `sort` can also be a [sort field definition
+  # object](sort.html#sort-field).
+  #
+  # __Default value:__ `"ascending"`
+  property sort : SortUnion?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property conditional_def_type : String?
+end
+
+# Reference to a repeated value.
+class RepeatRef
+  include JSON::Serializable
+
+  property repeat : String
+end
+
+# Properties of a legend or boolean flag for determining whether to show it.
+class Legend
+  include JSON::Serializable
+
+  # Padding (in pixels) between legend entries in a symbol legend.
+  @[JSON::Field(key: "entryPadding")]
+  property entry_padding : Float64?
+
+  # The formatting pattern for labels. This is D3's [number format
+  # pattern](https://github.com/d3/d3-format#locale_format) for quantitative fields and D3's
+  # [time format pattern](https://github.com/d3/d3-time-format#locale_format) for time
+  # field.
+  #
+  # See the [format documentation](format.html) for more information.
+  #
+  # __Default value:__  derived from [numberFormat](config.html#format) config for
+  # quantitative fields and from [timeFormat](config.html#format) config for temporal fields.
+  property format : String?
+
+  # The offset, in pixels, by which to displace the legend from the edge of the enclosing
+  # group or data rectangle.
+  #
+  # __Default value:__  `0`
+  property offset : Float64?
+
+  # The orientation of the legend, which determines how the legend is positioned within the
+  # scene. One of "left", "right", "top-left", "top-right", "bottom-left", "bottom-right",
+  # "none".
+  #
+  # __Default value:__ `"right"`
+  property orient : String?
+
+  # The padding, in pixels, between the legend and axis.
+  property padding : Float64?
+
+  # The desired number of tick values for quantitative legends.
+  @[JSON::Field(key: "tickCount")]
+  property tick_count : Float64?
+
+  # A title for the field. If `null`, the title will be removed.
+  #
+  # __Default value:__  derived from the field's name and transformation function
+  # (`aggregate`, `bin` and `timeUnit`).  If the field has an aggregate function, the
+  # function is displayed as a part of the title (e.g., `"Sum of Profit"`). If the field is
+  # binned or has a time unit applied, the applied function will be denoted in parentheses
+  # (e.g., `"Profit (binned)"`, `"Transaction Date (year-month)"`).  Otherwise, the title is
+  # simply the field name.
+  #
+  # __Note__: You can customize the default field title format by providing the [`fieldTitle`
+  # property in the [config](config.html) or [`fieldTitle` function via the `compile`
+  # function's options](compile.html#field-title).
+  property title : String?
+
+  # The type of the legend. Use `"symbol"` to create a discrete legend and `"gradient"` for a
+  # continuous color gradient.
+  #
+  # __Default value:__ `"gradient"` for non-binned quantitative fields and temporal fields;
+  # `"symbol"` otherwise.
+  @[JSON::Field(key: "type")]
+  property legend_type : String?
+
+  # Explicitly set the visible legend values.
+  property values : Array(LegendValue)?
+
+  # A non-positive integer indicating z-index of the legend.
+  # If zindex is 0, legend should be drawn behind all chart elements.
+  # To put them in front, use zindex = 1.
+  property zindex : Float64?
+end
+
+# An object defining properties of the channel's scale, which is the function that
+# transforms values in the data domain (numbers, dates, strings, etc) to visual values
+# (pixels, colors, sizes) of the encoding channels.
+#
+# __Default value:__ If undefined, default [scale properties](scale.html) are applied.
+class Scale
+  include JSON::Serializable
+
+  # The logarithm base of the `log` scale (default `10`).
+  property base : Float64?
+
+  # If `true`, values that exceed the data domain are clamped to either the minimum or
+  # maximum range value
+  #
+  # __Default value:__ derived from the [scale config](config.html#scale-config)'s `clamp`
+  # (`true` by default).
+  property clamp : Bool?
+
+  # Customized domain values.
+  #
+  # For _quantitative_ fields, `domain` can take the form of a two-element array with minimum
+  # and maximum values.  [Piecewise scales](scale.html#piecewise) can be created by providing
+  # a `domain` with more than two entries.
+  # If the input field is aggregated, `domain` can also be a string value `"unaggregated"`,
+  # indicating that the domain should include the raw data values prior to the aggregation.
+  #
+  # For _temporal_ fields, `domain` can be a two-element array minimum and maximum values, in
+  # the form of either timestamps or the [DateTime definition objects](types.html#datetime).
+  #
+  # For _ordinal_ and _nominal_ fields, `domain` can be an array that lists valid input
+  # values.
+  #
+  # The `selection` property can be used to [interactively
+  # determine](selection.html#scale-domains) the scale domain.
+  property domain : DomainUnion?
+
+  # The exponent of the `pow` scale.
+  property exponent : Float64?
+
+  # The interpolation method for range values. By default, a general interpolator for
+  # numbers, dates, strings and colors (in RGB space) is used. For color ranges, this
+  # property allows interpolation in alternative color spaces. Legal values include `rgb`,
+  # `hsl`, `hsl-long`, `lab`, `hcl`, `hcl-long`, `cubehelix` and `cubehelix-long` ('-long'
+  # variants use longer paths in polar coordinate spaces). If object-valued, this property
+  # accepts an object with a string-valued _type_ property and an optional numeric _gamma_
+  # property applicable to rgb and cubehelix interpolators. For more, see the [d3-interpolate
+  # documentation](https://github.com/d3/d3-interpolate).
+  #
+  # __Note:__ Sequential scales do not support `interpolate` as they have a fixed
+  # interpolator.  Since Vega-Lite uses sequential scales for quantitative fields by default,
+  # you have to set the scale `type` to other quantitative scale type such as `"linear"` to
+  # customize `interpolate`.
+  property interpolate : InterpolateUnion?
+
+  # Extending the domain so that it starts and ends on nice round values. This method
+  # typically modifies the scale’s domain, and may only extend the bounds to the nearest
+  # round value. Nicing is useful if the domain is computed from data and may be irregular.
+  # For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2,
+  # 1.0]_.
+  #
+  # For quantitative scales such as linear, `nice` can be either a boolean flag or a number.
+  # If `nice` is a number, it will represent a desired tick count. This allows greater
+  # control over the step size used to extend the bounds, guaranteeing that the returned
+  # ticks will exactly cover the domain.
+  #
+  # For temporal fields with time and utc scales, the `nice` value can be a string indicating
+  # the desired time interval. Legal values are `"millisecond"`, `"second"`, `"minute"`,
+  # `"hour"`, `"day"`, `"week"`, `"month"`, and `"year"`. Alternatively, `time` and `utc`
+  # scales can accept an object-valued interval specifier of the form `{"interval": "month",
+  # "step": 3}`, which includes a desired number of interval steps. Here, the domain would
+  # snap to quarter (Jan, Apr, Jul, Oct) boundaries.
+  #
+  # __Default value:__ `true` for unbinned _quantitative_ fields; `false` otherwise.
+  property nice : NiceUnion?
+
+  # For _[continuous](scale.html#continuous)_ scales, expands the scale domain to accommodate
+  # the specified number of pixels on each of the scale range. The scale range must represent
+  # pixels for this parameter to function as intended. Padding adjustment is performed prior
+  # to all other adjustments, including the effects of the zero, nice, domainMin, and
+  # domainMax properties.
+  #
+  # For _[band](scale.html#band)_ scales, shortcut for setting `paddingInner` and
+  # `paddingOuter` to the same value.
+  #
+  # For _[point](scale.html#point)_ scales, alias for `paddingOuter`.
+  #
+  # __Default value:__ For _continuous_ scales, derived from the [scale
+  # config](scale.html#config)'s `continuousPadding`.
+  # For _band and point_ scales, see `paddingInner` and `paddingOuter`.
+  property padding : Float64?
+
+  # The inner padding (spacing) within each band step of band scales, as a fraction of the
+  # step size. This value must lie in the range [0,1].
+  #
+  # For point scale, this property is invalid as point scales do not have internal band
+  # widths (only step sizes between bands).
+  #
+  # __Default value:__ derived from the [scale config](scale.html#config)'s
+  # `bandPaddingInner`.
+  @[JSON::Field(key: "paddingInner")]
+  property padding_inner : Float64?
+
+  # The outer padding (spacing) at the ends of the range of band and point scales,
+  # as a fraction of the step size. This value must lie in the range [0,1].
+  #
+  # __Default value:__ derived from the [scale config](scale.html#config)'s
+  # `bandPaddingOuter` for band scales and `pointPadding` for point scales.
+  @[JSON::Field(key: "paddingOuter")]
+  property padding_outer : Float64?
+
+  # The range of the scale. One of:
+  #
+  # - A string indicating a [pre-defined named scale range](scale.html#range-config) (e.g.,
+  # example, `"symbol"`, or `"diverging"`).
+  #
+  # - For [continuous scales](scale.html#continuous), two-element array indicating  minimum
+  # and maximum values, or an array with more than two entries for specifying a [piecewise
+  # scale](scale.html#piecewise).
+  #
+  # - For [discrete](scale.html#discrete) and [discretizing](scale.html#discretizing) scales,
+  # an array of desired output values.
+  #
+  # __Notes:__
+  #
+  # 1) For [sequential](scale.html#sequential), [ordinal](scale.html#ordinal), and
+  # discretizing color scales, you can also specify a color [`scheme`](scale.html#scheme)
+  # instead of `range`.
+  #
+  # 2) Any directly specified `range` for `x` and `y` channels will be ignored. Range can be
+  # customized via the view's corresponding [size](size.html) (`width` and `height`) or via
+  # [range steps and paddings properties](#range-step) for [band](#band) and [point](#point)
+  # scales.
+  property range : ScaleRange?
+
+  # The distance between the starts of adjacent bands or points in [band](scale.html#band)
+  # and [point](scale.html#point) scales.
+  #
+  # If `rangeStep` is `null` or if the view contains the scale's corresponding
+  # [size](size.html) (`width` for `x` scales and `height` for `y` scales), `rangeStep` will
+  # be automatically determined to fit the size of the view.
+  #
+  # __Default value:__  derived the [scale config](config.html#scale-config)'s
+  # `textXRangeStep` (`90` by default) for x-scales of `text` marks and `rangeStep` (`21` by
+  # default) for x-scales of other marks and y-scales.
+  #
+  # __Warning__: If `rangeStep` is `null` and the cardinality of the scale's domain is higher
+  # than `width` or `height`, the rangeStep might become less than one pixel and the mark
+  # might not appear correctly.
+  @[JSON::Field(key: "rangeStep")]
+  property range_step : Float64?
+
+  # If `true`, rounds numeric output values to integers. This can be helpful for snapping to
+  # the pixel grid.
+  #
+  # __Default value:__ `false`.
+  property round : Bool?
+
+  # A string indicating a color [scheme](scale.html#scheme) name (e.g., `"category10"` or
+  # `"viridis"`) or a [scheme parameter object](scale.html#scheme-params).
+  #
+  # Discrete color schemes may be used with [discrete](scale.html#discrete) or
+  # [discretizing](scale.html#discretizing) scales. Continuous color schemes are intended for
+  # use with [sequential](scales.html#sequential) scales.
+  #
+  # For the full list of supported scheme, please refer to the [Vega
+  # Scheme](https://vega.github.io/vega/docs/schemes/#reference) reference.
+  property scheme : Scheme?
+
+  # The type of scale.  Vega-Lite supports the following categories of scale types:
+  #
+  # 1) [**Continuous Scales**](scale.html#continuous) -- mapping continuous domains to
+  # continuous output ranges ([`"linear"`](scale.html#linear), [`"pow"`](scale.html#pow),
+  # [`"sqrt"`](scale.html#sqrt), [`"log"`](scale.html#log), [`"time"`](scale.html#time),
+  # [`"utc"`](scale.html#utc), [`"sequential"`](scale.html#sequential)).
+  #
+  # 2) [**Discrete Scales**](scale.html#discrete) -- mapping discrete domains to discrete
+  # ([`"ordinal"`](scale.html#ordinal)) or continuous ([`"band"`](scale.html#band) and
+  # [`"point"`](scale.html#point)) output ranges.
+  #
+  # 3) [**Discretizing Scales**](scale.html#discretizing) -- mapping continuous domains to
+  # discrete output ranges ([`"bin-linear"`](scale.html#bin-linear) and
+  # [`"bin-ordinal"`](scale.html#bin-ordinal)).
+  #
+  # __Default value:__ please see the [scale type table](scale.html#type).
+  @[JSON::Field(key: "type")]
+  property scale_type : String?
+
+  # If `true`, ensures that a zero baseline value is included in the scale domain.
+  #
+  # __Default value:__ `true` for x and y channels if the quantitative field is not binned
+  # and no custom `domain` is provided; `false` otherwise.
+  #
+  # __Note:__ Log, time, and utc scales do not support `zero`.
+  property zero : Bool?
+end
+
+class DomainClass
+  include JSON::Serializable
+
+  # The field name to extract selected values for, when a selection is
+  # [projected](project.html)
+  # over multiple fields or encodings.
+  property field : String?
+
+  # The name of a selection.
+  property selection : String
+
+  # The encoding channel to extract selected values for, when a selection is
+  # [projected](project.html)
+  # over multiple fields or encodings.
+  property encoding : String?
+end
+
+class InterpolateParams
+  include JSON::Serializable
+
+  property gamma : Float64?
+
+  @[JSON::Field(key: "type")]
+  property interpolate_params_type : String
+end
+
+class NiceClass
+  include JSON::Serializable
+
+  property interval : String
+
+  property step : Float64
+end
+
+class SchemeParams
+  include JSON::Serializable
+
+  # For sequential and diverging schemes only, determines the extent of the color range to
+  # use. For example `[0.2, 1]` will rescale the color scheme such that color values in the
+  # range _[0, 0.2)_ are excluded from the scheme.
+  property extent : Array(Float64)?
+
+  # A color scheme name for sequential/ordinal scales (e.g., `"category10"` or `"viridis"`).
+  #
+  # For the full list of supported scheme, please refer to the [Vega
+  # Scheme](https://vega.github.io/vega/docs/schemes/#reference) reference.
+  property name : String
+end
+
+class SortField
+  include JSON::Serializable
+
+  # The data [field](field.html) to sort by.
+  #
+  # __Default value:__ If unspecified, defaults to the field specified in the outer data
+  # reference.
+  property field : Field?
+
+  # An [aggregate operation](aggregate.html#ops) to perform on the field prior to sorting
+  # (e.g., `"count"`, `"mean"` and `"median"`).
+  # This property is required in cases where the sort field and the data reference field do
+  # not match.
+  # The input data objects will be aggregated, grouped by the encoded data field.
+  #
+  # For a full list of operations, please see the documentation for
+  # [aggregate](aggregate.html#ops).
+  property op : String
+
+  # The sort order. One of `"ascending"` (default) or `"descending"`.
+  property order : String?
+end
+
+# Horizontal facets for trellis plots.
+#
+# Vertical facets for trellis plots.
+class FacetFieldDef
+  include JSON::Serializable
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # An object defining properties of a facet's header.
+  property header : Header?
+
+  # Sort order for a facet field.
+  # This can be `"ascending"`, `"descending"`.
+  property sort : String?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property facet_field_def_type : String
+end
+
+# An object defining properties of a facet's header.
+#
+# Headers of row / column channels for faceted plots.
+class Header
+  include JSON::Serializable
+
+  # The formatting pattern for labels. This is D3's [number format
+  # pattern](https://github.com/d3/d3-format#locale_format) for quantitative fields and D3's
+  # [time format pattern](https://github.com/d3/d3-time-format#locale_format) for time
+  # field.
+  #
+  # See the [format documentation](format.html) for more information.
+  #
+  # __Default value:__  derived from [numberFormat](config.html#format) config for
+  # quantitative fields and from [timeFormat](config.html#format) config for temporal fields.
+  property format : String?
+
+  # The rotation angle of the header labels.
+  #
+  # __Default value:__ `0`.
+  @[JSON::Field(key: "labelAngle")]
+  property label_angle : Float64?
+
+  # A title for the field. If `null`, the title will be removed.
+  #
+  # __Default value:__  derived from the field's name and transformation function
+  # (`aggregate`, `bin` and `timeUnit`).  If the field has an aggregate function, the
+  # function is displayed as a part of the title (e.g., `"Sum of Profit"`). If the field is
+  # binned or has a time unit applied, the applied function will be denoted in parentheses
+  # (e.g., `"Profit (binned)"`, `"Transaction Date (year-month)"`).  Otherwise, the title is
+  # simply the field name.
+  #
+  # __Note__: You can customize the default field title format by providing the [`fieldTitle`
+  # property in the [config](config.html) or [`fieldTitle` function via the `compile`
+  # function's options](compile.html#field-title).
+  property title : String?
+end
+
+# Definition object for a data field, its type and transformation of an encoding channel.
+class FieldDef
+  include JSON::Serializable
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property field_def_type : String
+end
+
+# A URL to load upon mouse click.
+#
+# A FieldDef with Condition<ValueDef>
+# {
+# condition: {value: ...},
+# field: ...,
+# ...
+# }
+#
+# A ValueDef with Condition<ValueDef | FieldDef>
+# {
+# condition: {field: ...} | {value: ...},
+# value: ...,
+# }
+class DefWithCondition
+  include JSON::Serializable
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # One or more value definition(s) with a selection predicate.
+  #
+  # __Note:__ A field definition's `condition` property can only contain [value
+  # definitions](encoding.html#value-def)
+  # since Vega-Lite only allows at most one encoded field per encoding channel.
+  #
+  # A field definition or one or more value definition(s) with a selection predicate.
+  property condition : HrefCondition?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property def_with_condition_type : String?
+
+  # A constant value in visual domain.
+  property value : ConditionalPredicateValueDefValue?
+end
+
+class ConditionalPredicateFieldDefClass
+  include JSON::Serializable
+
+  property test : ConditionalValueDefLogicalOperandPredicate?
+
+  # A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
+  # `0` to `1` for opacity).
+  property value : ConditionalPredicateValueDefValue?
+
+  # A [selection name](selection.html), or a series of [composed
+  # selections](selection.html#compose).
+  property selection : ConditionalValueDefSelectionOperand?
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property conditional_def_type : String?
+end
+
+class OrderFieldDef
+  include JSON::Serializable
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # The sort order. One of `"ascending"` (default) or `"descending"`.
+  property sort : String?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property order_field_def_type : String
+end
+
+# Text of the `text` mark.
+#
+# The tooltip text to show upon mouse hover.
+#
+# A FieldDef with Condition<ValueDef>
+# {
+# condition: {value: ...},
+# field: ...,
+# ...
+# }
+#
+# A ValueDef with Condition<ValueDef | FieldDef>
+# {
+# condition: {field: ...} | {value: ...},
+# value: ...,
+# }
+class TextDefWithCondition
+  include JSON::Serializable
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # One or more value definition(s) with a selection predicate.
+  #
+  # __Note:__ A field definition's `condition` property can only contain [value
+  # definitions](encoding.html#value-def)
+  # since Vega-Lite only allows at most one encoded field per encoding channel.
+  #
+  # A field definition or one or more value definition(s) with a selection predicate.
+  property condition : TextCondition?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # The [formatting pattern](format.html) for a text field. If not defined, this will be
+  # determined automatically.
+  property format : String?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property text_def_with_condition_type : String?
+
+  # A constant value in visual domain.
+  property value : ConditionalPredicateValueDefValue?
+end
+
+class ConditionalPredicateTextFieldDefClass
+  include JSON::Serializable
+
+  property test : ConditionalValueDefLogicalOperandPredicate?
+
+  # A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
+  # `0` to `1` for opacity).
+  property value : ConditionalPredicateValueDefValue?
+
+  # A [selection name](selection.html), or a series of [composed
+  # selections](selection.html#compose).
+  property selection : ConditionalValueDefSelectionOperand?
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # The [formatting pattern](format.html) for a text field. If not defined, this will be
+  # determined automatically.
+  property format : String?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property conditional_def_type : String?
+end
+
+# X coordinates of the marks, or width of horizontal `"bar"` and `"area"`.
+#
+# Y coordinates of the marks, or height of vertical `"bar"` and `"area"`.
+#
+# Definition object for a constant value of an encoding channel.
+class XClass
+  include JSON::Serializable
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # An object defining properties of axis's gridlines, ticks and labels.
+  # If `null`, the axis for the encoding channel will be removed.
+  #
+  # __Default value:__ If undefined, default [axis properties](axis.html) are applied.
+  property axis : Axis?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # An object defining properties of the channel's scale, which is the function that
+  # transforms values in the data domain (numbers, dates, strings, etc) to visual values
+  # (pixels, colors, sizes) of the encoding channels.
+  #
+  # __Default value:__ If undefined, default [scale properties](scale.html) are applied.
+  property scale : Scale?
+
+  # Sort order for the encoded field.
+  # Supported `sort` values include `"ascending"`, `"descending"` and `null` (no sorting).
+  # For fields with discrete domains, `sort` can also be a [sort field definition
+  # object](sort.html#sort-field).
+  #
+  # __Default value:__ `"ascending"`
+  property sort : SortUnion?
+
+  # Type of stacking offset if the field should be stacked.
+  # `stack` is only applicable for `x` and `y` channels with continuous domains.
+  # For example, `stack` of `y` can be used to customize stacking for a vertical bar chart.
+  #
+  # `stack` can be one of the following values:
+  #
+  # - `"zero"`: stacking with baseline offset at zero value of the scale (for creating
+  # typical stacked [bar](stack.html#bar) and [area](stack.html#area) chart).
+  # - `"normalize"` - stacking with normalized domain (for creating [normalized stacked bar
+  # and area charts](stack.html#normalized). <br/>
+  # - `"center"` - stacking with center baseline (for [streamgraph](stack.html#streamgraph)).
+  # - `null` - No-stacking. This will produce layered [bar](stack.html#layered-bar-chart) and
+  # area chart.
+  #
+  # __Default value:__ `zero` for plots with all of the following conditions are true:
+  #
+  # 1. The mark is `bar` or `area`;
+  # 2. The stacked measure channel (x or y) has a linear scale;
+  # 3. At least one of non-position channels mapped to an unaggregated field that is
+  # different from x and y.  Otherwise, `null` by default.
+  property stack : String?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property def_type : String?
+
+  # A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
+  # `0` to `1` for opacity).
+  property value : ConditionalPredicateValueDefValue?
+end
+
+class Axis
+  include JSON::Serializable
+
+  # A boolean flag indicating if the domain (the axis baseline) should be included as part of
+  # the axis.
+  #
+  # __Default value:__ `true`
+  property domain : Bool?
+
+  # The formatting pattern for labels. This is D3's [number format
+  # pattern](https://github.com/d3/d3-format#locale_format) for quantitative fields and D3's
+  # [time format pattern](https://github.com/d3/d3-time-format#locale_format) for time
+  # field.
+  #
+  # See the [format documentation](format.html) for more information.
+  #
+  # __Default value:__  derived from [numberFormat](config.html#format) config for
+  # quantitative fields and from [timeFormat](config.html#format) config for temporal fields.
+  property format : String?
+
+  # A boolean flag indicating if grid lines should be included as part of the axis
+  #
+  # __Default value:__ `true` for [continuous scales](scale.html#continuous) that are not
+  # binned; otherwise, `false`.
+  property grid : Bool?
+
+  # The rotation angle of the axis labels.
+  #
+  # __Default value:__ `-90` for nominal and ordinal fields; `0` otherwise.
+  @[JSON::Field(key: "labelAngle")]
+  property label_angle : Float64?
+
+  # Indicates if labels should be hidden if they exceed the axis range. If `false `(the
+  # default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
+  # they exceed the axis range by more than 1 pixel. If this property is a number, it
+  # specifies the pixel tolerance: the maximum amount by which a label bounding box may
+  # exceed the axis range.
+  #
+  # __Default value:__ `false`.
+  @[JSON::Field(key: "labelBound")]
+  property label_bound : Label?
+
+  # Indicates if the first and last axis labels should be aligned flush with the scale range.
+  # Flush alignment for a horizontal axis will left-align the first label and right-align the
+  # last label. For vertical axes, bottom and top text baselines are applied instead. If this
+  # property is a number, it also indicates the number of pixels by which to offset the first
+  # and last labels; for example, a value of 2 will flush-align the first and last labels and
+  # also push them 2 pixels outward from the center of the axis. The additional adjustment
+  # can sometimes help the labels better visually group with corresponding axis ticks.
+  #
+  # __Default value:__ `true` for axis of a continuous x-scale. Otherwise, `false`.
+  @[JSON::Field(key: "labelFlush")]
+  property label_flush : Label?
+
+  # The strategy to use for resolving overlap of axis labels. If `false` (the default), no
+  # overlap reduction is attempted. If set to `true` or `"parity"`, a strategy of removing
+  # every other label is used (this works well for standard linear axes). If set to
+  # `"greedy"`, a linear scan of the labels is performed, removing any labels that overlaps
+  # with the last visible label (this often works better for log-scaled axes).
+  #
+  # __Default value:__ `true` for non-nominal fields with non-log scales; `"greedy"` for log
+  # scales; otherwise `false`.
+  @[JSON::Field(key: "labelOverlap")]
+  property label_overlap : LabelOverlapUnion?
+
+  # The padding, in pixels, between axis and text labels.
+  @[JSON::Field(key: "labelPadding")]
+  property label_padding : Float64?
+
+  # A boolean flag indicating if labels should be included as part of the axis.
+  #
+  # __Default value:__  `true`.
+  property labels : Bool?
+
+  # The maximum extent in pixels that axis ticks and labels should use. This determines a
+  # maximum offset value for axis titles.
+  #
+  # __Default value:__ `undefined`.
+  @[JSON::Field(key: "maxExtent")]
+  property max_extent : Float64?
+
+  # The minimum extent in pixels that axis ticks and labels should use. This determines a
+  # minimum offset value for axis titles.
+  #
+  # __Default value:__ `30` for y-axis; `undefined` for x-axis.
+  @[JSON::Field(key: "minExtent")]
+  property min_extent : Float64?
+
+  # The offset, in pixels, by which to displace the axis from the edge of the enclosing group
+  # or data rectangle.
+  #
+  # __Default value:__ derived from the [axis config](config.html#facet-scale-config)'s
+  # `offset` (`0` by default)
+  property offset : Float64?
+
+  # The orientation of the axis. One of `"top"`, `"bottom"`, `"left"` or `"right"`. The
+  # orientation can be used to further specialize the axis type (e.g., a y axis oriented for
+  # the right edge of the chart).
+  #
+  # __Default value:__ `"bottom"` for x-axes and `"left"` for y-axes.
+  property orient : String?
+
+  # The anchor position of the axis in pixels. For x-axis with top or bottom orientation,
+  # this sets the axis group x coordinate. For y-axis with left or right orientation, this
+  # sets the axis group y coordinate.
+  #
+  # __Default value__: `0`
+  property position : Float64?
+
+  # A desired number of ticks, for axes visualizing quantitative scales. The resulting number
+  # may be different so that values are "nice" (multiples of 2, 5, 10) and lie within the
+  # underlying scale's range.
+  @[JSON::Field(key: "tickCount")]
+  property tick_count : Float64?
+
+  # Boolean value that determines whether the axis should include ticks.
+  property ticks : Bool?
+
+  # The size in pixels of axis ticks.
+  @[JSON::Field(key: "tickSize")]
+  property tick_size : Float64?
+
+  # A title for the field. If `null`, the title will be removed.
+  #
+  # __Default value:__  derived from the field's name and transformation function
+  # (`aggregate`, `bin` and `timeUnit`).  If the field has an aggregate function, the
+  # function is displayed as a part of the title (e.g., `"Sum of Profit"`). If the field is
+  # binned or has a time unit applied, the applied function will be denoted in parentheses
+  # (e.g., `"Profit (binned)"`, `"Transaction Date (year-month)"`).  Otherwise, the title is
+  # simply the field name.
+  #
+  # __Note__: You can customize the default field title format by providing the [`fieldTitle`
+  # property in the [config](config.html) or [`fieldTitle` function via the `compile`
+  # function's options](compile.html#field-title).
+  property title : String?
+
+  # Max length for axis title if the title is automatically generated from the field's
+  # description.
+  @[JSON::Field(key: "titleMaxLength")]
+  property title_max_length : Float64?
+
+  # The padding, in pixels, between title and axis.
+  @[JSON::Field(key: "titlePadding")]
+  property title_padding : Float64?
+
+  # Explicitly set the visible axis tick values.
+  property values : Array(AxisValue)?
+
+  # A non-positive integer indicating z-index of the axis.
+  # If zindex is 0, axes should be drawn behind all chart elements.
+  # To put them in front, use `"zindex = 1"`.
+  #
+  # __Default value:__ `1` (in front of the marks) for actual axis and `0` (behind the marks)
+  # for grids.
+  property zindex : Float64?
+end
+
+# X2 coordinates for ranged  `"area"`, `"bar"`, `"rect"`, and  `"rule"`.
+#
+# Y2 coordinates for ranged  `"area"`, `"bar"`, `"rect"`, and  `"rule"`.
+#
+# Definition object for a data field, its type and transformation of an encoding channel.
+#
+# Definition object for a constant value of an encoding channel.
+class X2Class
+  include JSON::Serializable
+
+  # Aggregation function for the field
+  # (e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).
+  #
+  # __Default value:__ `undefined` (None)
+  property aggregate : String?
+
+  # A flag for binning a `quantitative` field, or [an object defining binning
+  # parameters](bin.html#params).
+  # If `true`, default [binning parameters](bin.html) will be applied.
+  #
+  # __Default value:__ `false`
+  property bin : Bin?
+
+  # __Required.__ A string defining the name of the field from which to pull a data value
+  # or an object defining iterated values from the [`repeat`](repeat.html) operator.
+  #
+  # __Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects
+  # (e.g., `"field": "foo.bar"` and `"field": "foo['bar']"`).
+  # If field names contain dots or brackets but are not nested, you can use `\\` to escape
+  # dots and brackets (e.g., `"a\\.b"` and `"a\\[0\\]"`).
+  # See more details about escaping in the [field documentation](field.html).
+  #
+  # __Note:__ `field` is not required if `aggregate` is `count`.
+  property field : Field?
+
+  # Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.
+  # or [a temporal field that gets casted as ordinal](type.html#cast).
+  #
+  # __Default value:__ `undefined` (None)
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # The encoded field's type of measurement (`"quantitative"`, `"temporal"`, `"ordinal"`, or
+  # `"nominal"`).
+  # It can also be a geo type (`"latitude"`, `"longitude"`, and `"geojson"`) when a
+  # [geographic projection](projection.html) is applied.
+  @[JSON::Field(key: "type")]
+  property def_type : String?
+
+  # A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
+  # `0` to `1` for opacity).
+  property value : ConditionalPredicateValueDefValue?
+end
+
+# An object that describes mappings between `row` and `column` channels and their field
+# definitions.
+class FacetMapping
+  include JSON::Serializable
+
+  # Horizontal facets for trellis plots.
+  property column : FacetFieldDef?
+
+  # Vertical facets for trellis plots.
+  property row : FacetFieldDef?
+end
+
+# Unit spec that can have a composite mark.
+class SpecElement
+  include JSON::Serializable
+
+  # An object describing the data source
+  property data : Data?
+
+  # Description of this mark for commenting purpose.
+  property description : String?
+
+  # The height of a visualization.
+  #
+  # __Default value:__
+  # - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its y-channel has a
+  # [continuous scale](scale.html#continuous), the height will be the value of
+  # [`config.view.height`](spec.html#config).
+  # - For y-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
+  # value or unspecified, the height is [determined by the range step, paddings, and the
+  # cardinality of the field mapped to y-channel](scale.html#band). Otherwise, if the
+  # `rangeStep` is `null`, the height will be the value of
+  # [`config.view.height`](spec.html#config).
+  # - If no field is mapped to `y` channel, the `height` will be the value of `rangeStep`.
+  #
+  # __Note__: For plots with [`row` and `column` channels](encoding.html#facet), this
+  # represents the height of a single view.
+  #
+  # __See also:__ The documentation for [width and height](size.html) contains more examples.
+  property height : Float64?
+
+  # Layer or single view specifications to be layered.
+  #
+  # __Note__: Specifications inside `layer` cannot use `row` and `column` channels as
+  # layering facet specifications is not allowed.
+  property layer : Array(LayerSpec)?
+
+  # Name of the visualization for later reference.
+  property name : String?
+
+  # Scale, axis, and legend resolutions for layers.
+  #
+  # Scale, axis, and legend resolutions for facets.
+  #
+  # Scale and legend resolutions for repeated charts.
+  #
+  # Scale, axis, and legend resolutions for vertically concatenated charts.
+  #
+  # Scale, axis, and legend resolutions for horizontally concatenated charts.
+  property resolve : Resolve?
+
+  # Title for the plot.
+  property title : Title?
+
+  # An array of data transformations such as filter and new field calculation.
+  property transform : Array(Transform)?
+
+  # The width of a visualization.
+  #
+  # __Default value:__ This will be determined by the following rules:
+  #
+  # - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its x-channel has a
+  # [continuous scale](scale.html#continuous), the width will be the value of
+  # [`config.view.width`](spec.html#config).
+  # - For x-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
+  # value or unspecified, the width is [determined by the range step, paddings, and the
+  # cardinality of the field mapped to x-channel](scale.html#band).   Otherwise, if the
+  # `rangeStep` is `null`, the width will be the value of
+  # [`config.view.width`](spec.html#config).
+  # - If no field is mapped to `x` channel, the `width` will be the value of
+  # [`config.scale.textXRangeStep`](size.html#default-width-and-height) for `text` mark and
+  # the value of `rangeStep` for other marks.
+  #
+  # __Note:__ For plots with [`row` and `column` channels](encoding.html#facet), this
+  # represents the width of a single view.
+  #
+  # __See also:__ The documentation for [width and height](size.html) contains more examples.
+  property width : Float64?
+
+  # A key-value mapping between encoding channels and definition of fields.
+  property encoding : Encoding?
+
+  # A string describing the mark type (one of `"bar"`, `"circle"`, `"square"`, `"tick"`,
+  # `"line"`,
+  # * `"area"`, `"point"`, `"rule"`, `"geoshape"`, and `"text"`) or a [mark definition
+  # object](mark.html#mark-def).
+  property mark : AnyMark?
+
+  # An object defining properties of geographic projection.
+  #
+  # Works with `"geoshape"` marks and `"point"` or `"line"` marks that have a channel (one or
+  # more of `"X"`, `"X2"`, `"Y"`, `"Y2"`) with type `"latitude"`, or `"longitude"`.
+  property projection : Projection?
+
+  # A key-value mapping between selection names and definitions.
+  property selection : Hash(String, SelectionDef)?
+
+  # An object that describes mappings between `row` and `column` channels and their field
+  # definitions.
+  property facet : FacetMapping?
+
+  # A specification of the view that gets faceted.
+  property spec : SpecElement?
+
+  # An object that describes what fields should be repeated into views that are laid out as a
+  # `row` or `column`.
+  property repeat : Repeat?
+
+  # A list of views that should be concatenated and put into a column.
+  property vconcat : Array(SpecElement)?
+
+  # A list of views that should be concatenated and put into a row.
+  property hconcat : Array(SpecElement)?
+end
+
+# A key-value mapping between encoding channels and definition of fields.
+class Encoding
+  include JSON::Serializable
+
+  # Color of the marks – either fill or stroke color based on mark type.
+  # By default, `color` represents fill color for `"area"`, `"bar"`, `"tick"`,
+  # `"text"`, `"circle"`, and `"square"` / stroke color for `"line"` and `"point"`.
+  #
+  # __Default value:__ If undefined, the default color depends on [mark
+  # config](config.html#mark)'s `color` property.
+  #
+  # _Note:_ See the scale documentation for more information about customizing [color
+  # scheme](scale.html#scheme).
+  property color : MarkPropDefWithCondition?
+
+  # Additional levels of detail for grouping data in aggregate views and
+  # in line and area marks without mapping data to a specific visual channel.
+  property detail : Detail?
+
+  # A URL to load upon mouse click.
+  property href : DefWithCondition?
+
+  # Opacity of the marks – either can be a value or a range.
+  #
+  # __Default value:__ If undefined, the default opacity depends on [mark
+  # config](config.html#mark)'s `opacity` property.
+  property opacity : MarkPropDefWithCondition?
+
+  # Stack order for stacked marks or order of data points in line marks for connected scatter
+  # plots.
+  #
+  # __Note__: In aggregate plots, `order` field should be `aggregate`d to avoid creating
+  # additional aggregation grouping.
+  property order : Order?
+
+  # For `point` marks the supported values are
+  # `"circle"` (default), `"square"`, `"cross"`, `"diamond"`, `"triangle-up"`,
+  # or `"triangle-down"`, or else a custom SVG path string.
+  # For `geoshape` marks it should be a field definition of the geojson data
+  #
+  # __Default value:__ If undefined, the default shape depends on [mark
+  # config](config.html#point-config)'s `shape` property.
+  property shape : MarkPropDefWithCondition?
+
+  # Size of the mark.
+  # - For `"point"`, `"square"` and `"circle"`, – the symbol size, or pixel area of the mark.
+  # - For `"bar"` and `"tick"` – the bar and tick's size.
+  # - For `"text"` – the text's font size.
+  # - Size is currently unsupported for `"line"`, `"area"`, and `"rect"`.
+  property size : MarkPropDefWithCondition?
+
+  # Text of the `text` mark.
+  property text : TextDefWithCondition?
+
+  # The tooltip text to show upon mouse hover.
+  property tooltip : TextDefWithCondition?
+
+  # X coordinates of the marks, or width of horizontal `"bar"` and `"area"`.
+  property x : XClass?
+
+  # X2 coordinates for ranged  `"area"`, `"bar"`, `"rect"`, and  `"rule"`.
+  property x2 : X2Class?
+
+  # Y coordinates of the marks, or height of vertical `"bar"` and `"area"`.
+  property y : XClass?
+
+  # Y2 coordinates for ranged  `"area"`, `"bar"`, `"rect"`, and  `"rule"`.
+  property y2 : X2Class?
+end
+
+# Unit spec that can have a composite mark.
+class LayerSpec
+  include JSON::Serializable
+
+  # An object describing the data source
+  property data : Data?
+
+  # Description of this mark for commenting purpose.
+  property description : String?
+
+  # The height of a visualization.
+  #
+  # __Default value:__
+  # - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its y-channel has a
+  # [continuous scale](scale.html#continuous), the height will be the value of
+  # [`config.view.height`](spec.html#config).
+  # - For y-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
+  # value or unspecified, the height is [determined by the range step, paddings, and the
+  # cardinality of the field mapped to y-channel](scale.html#band). Otherwise, if the
+  # `rangeStep` is `null`, the height will be the value of
+  # [`config.view.height`](spec.html#config).
+  # - If no field is mapped to `y` channel, the `height` will be the value of `rangeStep`.
+  #
+  # __Note__: For plots with [`row` and `column` channels](encoding.html#facet), this
+  # represents the height of a single view.
+  #
+  # __See also:__ The documentation for [width and height](size.html) contains more examples.
+  property height : Float64?
+
+  # Layer or single view specifications to be layered.
+  #
+  # __Note__: Specifications inside `layer` cannot use `row` and `column` channels as
+  # layering facet specifications is not allowed.
+  property layer : Array(LayerSpec)?
+
+  # Name of the visualization for later reference.
+  property name : String?
+
+  # Scale, axis, and legend resolutions for layers.
+  property resolve : Resolve?
+
+  # Title for the plot.
+  property title : Title?
+
+  # An array of data transformations such as filter and new field calculation.
+  property transform : Array(Transform)?
+
+  # The width of a visualization.
+  #
+  # __Default value:__ This will be determined by the following rules:
+  #
+  # - If a view's [`autosize`](size.html#autosize) type is `"fit"` or its x-channel has a
+  # [continuous scale](scale.html#continuous), the width will be the value of
+  # [`config.view.width`](spec.html#config).
+  # - For x-axis with a band or point scale: if [`rangeStep`](scale.html#band) is a numeric
+  # value or unspecified, the width is [determined by the range step, paddings, and the
+  # cardinality of the field mapped to x-channel](scale.html#band).   Otherwise, if the
+  # `rangeStep` is `null`, the width will be the value of
+  # [`config.view.width`](spec.html#config).
+  # - If no field is mapped to `x` channel, the `width` will be the value of
+  # [`config.scale.textXRangeStep`](size.html#default-width-and-height) for `text` mark and
+  # the value of `rangeStep` for other marks.
+  #
+  # __Note:__ For plots with [`row` and `column` channels](encoding.html#facet), this
+  # represents the width of a single view.
+  #
+  # __See also:__ The documentation for [width and height](size.html) contains more examples.
+  property width : Float64?
+
+  # A key-value mapping between encoding channels and definition of fields.
+  property encoding : Encoding?
+
+  # A string describing the mark type (one of `"bar"`, `"circle"`, `"square"`, `"tick"`,
+  # `"line"`,
+  # * `"area"`, `"point"`, `"rule"`, `"geoshape"`, and `"text"`) or a [mark definition
+  # object](mark.html#mark-def).
+  property mark : AnyMark?
+
+  # An object defining properties of geographic projection.
+  #
+  # Works with `"geoshape"` marks and `"point"` or `"line"` marks that have a channel (one or
+  # more of `"X"`, `"X2"`, `"Y"`, `"Y2"`) with type `"latitude"`, or `"longitude"`.
+  property projection : Projection?
+
+  # A key-value mapping between selection names and definitions.
+  property selection : Hash(String, SelectionDef)?
+end
+
+class MarkDef
+  include JSON::Serializable
+
+  # The horizontal alignment of the text. One of `"left"`, `"right"`, `"center"`.
+  property align : String?
+
+  # The rotation angle of the text, in degrees.
+  property angle : Float64?
+
+  # The vertical alignment of the text. One of `"top"`, `"middle"`, `"bottom"`.
+  #
+  # __Default value:__ `"middle"`
+  property baseline : String?
+
+  # Whether a mark be clipped to the enclosing group’s width and height.
+  property clip : Bool?
+
+  # Default color.  Note that `fill` and `stroke` have higher precedence than `color` and
+  # will override `color`.
+  #
+  # __Default value:__ <span style="color: #4682b4;">&#9632;</span> `"#4682b4"`
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property color : String?
+
+  # The mouse cursor used over the mark. Any valid [CSS cursor
+  # type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Values) can be used.
+  property cursor : String?
+
+  # The horizontal offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dx : Float64?
+
+  # The vertical offset, in pixels, between the text label and its anchor point. The offset
+  # is applied after rotation by the _angle_ property.
+  property dy : Float64?
+
+  # Default Fill Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property fill : String?
+
+  # Whether the mark's color should be used as fill color instead of stroke color.
+  #
+  # __Default value:__ `true` for all marks except `point` and `false` for `point`.
+  #
+  # __Applicable for:__ `bar`, `point`, `circle`, `square`, and `area` marks.
+  #
+  # __Note:__ This property cannot be used in a [style config](mark.html#style-config).
+  property filled : Bool?
+
+  # The fill opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "fillOpacity")]
+  property fill_opacity : Float64?
+
+  # The typeface to set the text in (e.g., `"Helvetica Neue"`).
+  property font : String?
+
+  # The font size, in pixels.
+  @[JSON::Field(key: "fontSize")]
+  property font_size : Float64?
+
+  # The font style (e.g., `"italic"`).
+  @[JSON::Field(key: "fontStyle")]
+  property font_style : String?
+
+  # The font weight (e.g., `"bold"`).
+  @[JSON::Field(key: "fontWeight")]
+  property font_weight : FontWeightUnion?
+
+  # A URL to load upon mouse click. If defined, the mark acts as a hyperlink.
+  property href : String?
+
+  # The line interpolation method to use for line and area marks. One of the following:
+  # - `"linear"`: piecewise linear segments, as in a polyline.
+  # - `"linear-closed"`: close the linear segments to form a polygon.
+  # - `"step"`: alternate between horizontal and vertical segments, as in a step function.
+  # - `"step-before"`: alternate between vertical and horizontal segments, as in a step
+  # function.
+  # - `"step-after"`: alternate between horizontal and vertical segments, as in a step
+  # function.
+  # - `"basis"`: a B-spline, with control point duplication on the ends.
+  # - `"basis-open"`: an open B-spline; may not intersect the start or end.
+  # - `"basis-closed"`: a closed B-spline, as in a loop.
+  # - `"cardinal"`: a Cardinal spline, with control point duplication on the ends.
+  # - `"cardinal-open"`: an open Cardinal spline; may not intersect the start or end, but
+  # will intersect other control points.
+  # - `"cardinal-closed"`: a closed Cardinal spline, as in a loop.
+  # - `"bundle"`: equivalent to basis, except the tension parameter is used to straighten the
+  # spline.
+  # - `"monotone"`: cubic interpolation that preserves monotonicity in y.
+  property interpolate : String?
+
+  # The maximum length of the text mark in pixels (default 0, indicating no limit). The text
+  # value will be automatically truncated if the rendered size exceeds the limit.
+  property limit : Float64?
+
+  # The overall opacity (value between [0,1]).
+  #
+  # __Default value:__ `0.7` for non-aggregate plots with `point`, `tick`, `circle`, or
+  # `square` marks or layered `bar` charts and `1` otherwise.
+  property opacity : Float64?
+
+  # The orientation of a non-stacked bar, tick, area, and line charts.
+  # The value is either horizontal (default) or vertical.
+  # - For bar, rule and tick, this determines whether the size of the bar and tick
+  # should be applied to x or y dimension.
+  # - For area, this property determines the orient property of the Vega output.
+  # - For line, this property determines the sort order of the points in the line
+  # if `config.sortLineBy` is not specified.
+  # For stacked charts, this is always determined by the orientation of the stack;
+  # therefore explicitly specified value will be ignored.
+  property orient : String?
+
+  # Polar coordinate radial offset, in pixels, of the text label from the origin determined
+  # by the `x` and `y` properties.
+  property radius : Float64?
+
+  # The default symbol shape to use. One of: `"circle"` (default), `"square"`, `"cross"`,
+  # `"diamond"`, `"triangle-up"`, or `"triangle-down"`, or a custom SVG path.
+  #
+  # __Default value:__ `"circle"`
+  property shape : String?
+
+  # The pixel area each the point/circle/square.
+  # For example: in the case of circles, the radius is determined in part by the square root
+  # of the size value.
+  #
+  # __Default value:__ `30`
+  property size : Float64?
+
+  # Default Stroke Color.  This has higher precedence than config.color
+  #
+  # __Default value:__ (None)
+  property stroke : String?
+
+  # An array of alternating stroke, space lengths for creating dashed or dotted lines.
+  @[JSON::Field(key: "strokeDash")]
+  property stroke_dash : Array(Float64)?
+
+  # The offset (in pixels) into which to begin drawing with the stroke dash array.
+  @[JSON::Field(key: "strokeDashOffset")]
+  property stroke_dash_offset : Float64?
+
+  # The stroke opacity (value between [0,1]).
+  #
+  # __Default value:__ `1`
+  @[JSON::Field(key: "strokeOpacity")]
+  property stroke_opacity : Float64?
+
+  # The stroke width, in pixels.
+  @[JSON::Field(key: "strokeWidth")]
+  property stroke_width : Float64?
+
+  # A string or array of strings indicating the name of custom styles to apply to the mark. A
+  # style is a named collection of mark property defaults defined within the [style
+  # configuration](mark.html#style-config). If style is an array, later styles will override
+  # earlier styles. Any [mark properties](encoding.html#mark-prop) explicitly defined within
+  # the `encoding` will override a style default.
+  #
+  # __Default value:__ The mark's name.  For example, a bar mark will have style `"bar"` by
+  # default.
+  # __Note:__ Any specified style will augment the default style. For example, a bar mark
+  # with `"style": "foo"` will receive from `config.style.bar` and `config.style.foo` (the
+  # specified style `"foo"` has higher precedence).
+  property style : Style?
+
+  # Depending on the interpolation type, sets the tension parameter (for line and area marks).
+  property tension : Float64?
+
+  # Placeholder text if the `text` channel is not specified
+  property text : String?
+
+  # Polar coordinate angle, in radians, of the text label from the origin determined by the
+  # `x` and `y` properties. Values for `theta` follow the same convention of `arc` mark
+  # `startAngle` and `endAngle` properties: angles are measured in radians, with `0`
+  # indicating "north".
+  property theta : Float64?
+
+  # The mark type.
+  # One of `"bar"`, `"circle"`, `"square"`, `"tick"`, `"line"`,
+  # `"area"`, `"point"`, `"geoshape"`, `"rule"`, and `"text"`.
+  @[JSON::Field(key: "type")]
+  property mark_def_type : String
+end
+
+# An object defining properties of geographic projection.
+#
+# Works with `"geoshape"` marks and `"point"` or `"line"` marks that have a channel (one or
+# more of `"X"`, `"X2"`, `"Y"`, `"Y2"`) with type `"latitude"`, or `"longitude"`.
+class Projection
+  include JSON::Serializable
+
+  # Sets the projection’s center to the specified center, a two-element array of longitude
+  # and latitude in degrees.
+  #
+  # __Default value:__ `[0, 0]`
+  property center : Array(Float64)?
+
+  # Sets the projection’s clipping circle radius to the specified angle in degrees. If
+  # `null`, switches to [antimeridian](http://bl.ocks.org/mbostock/3788999) cutting rather
+  # than small-circle clipping.
+  @[JSON::Field(key: "clipAngle")]
+  property clip_angle : Float64?
+
+  # Sets the projection’s viewport clip extent to the specified bounds in pixels. The extent
+  # bounds are specified as an array `[[x0, y0], [x1, y1]]`, where `x0` is the left-side of
+  # the viewport, `y0` is the top, `x1` is the right and `y1` is the bottom. If `null`, no
+  # viewport clipping is performed.
+  @[JSON::Field(key: "clipExtent")]
+  property clip_extent : Array(Array(Float64))?
+
+  property coefficient : Float64?
+
+  property distance : Float64?
+
+  property fraction : Float64?
+
+  property lobes : Float64?
+
+  property parallel : Float64?
+
+  # Sets the threshold for the projection’s [adaptive
+  # resampling](http://bl.ocks.org/mbostock/3795544) to the specified value in pixels. This
+  # value corresponds to the [Douglas–Peucker
+  # distance](http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm).
+  # If precision is not specified, returns the projection’s current resampling precision
+  # which defaults to `√0.5 ≅ 0.70710…`.
+  property precision : Hash(String, PrecisionValue)?
+
+  property radius : Float64?
+
+  property ratio : Float64?
+
+  # Sets the projection’s three-axis rotation to the specified angles, which must be a two-
+  # or three-element array of numbers [`lambda`, `phi`, `gamma`] specifying the rotation
+  # angles in degrees about each spherical axis. (These correspond to yaw, pitch and roll.)
+  #
+  # __Default value:__ `[0, 0, 0]`
+  property rotate : Array(Float64)?
+
+  property spacing : Float64?
+
+  property tilt : Float64?
+
+  # The cartographic projection to use. This value is case-insensitive, for example
+  # `"albers"` and `"Albers"` indicate the same projection type. You can find all valid
+  # projection types [in the
+  # documentation](https://vega.github.io/vega-lite/docs/projection.html#projection-types).
+  #
+  # __Default value:__ `mercator`
+  @[JSON::Field(key: "type")]
+  property projection_type : String?
+end
+
+# Scale, axis, and legend resolutions for layers.
+#
+# Defines how scales, axes, and legends from different specs should be combined. Resolve is
+# a mapping from `scale`, `axis`, and `legend` to a mapping from channels to resolutions.
+#
+# Scale, axis, and legend resolutions for facets.
+#
+# Scale and legend resolutions for repeated charts.
+#
+# Scale, axis, and legend resolutions for vertically concatenated charts.
+#
+# Scale, axis, and legend resolutions for horizontally concatenated charts.
+class Resolve
+  include JSON::Serializable
+
+  property axis : AxisResolveMap?
+
+  property legend : LegendResolveMap?
+
+  property scale : ScaleResolveMap?
+end
+
+class AxisResolveMap
+  include JSON::Serializable
+
+  property x : String?
+
+  property y : String?
+end
+
+class LegendResolveMap
+  include JSON::Serializable
+
+  property color : String?
+
+  property opacity : String?
+
+  property shape : String?
+
+  property size : String?
+end
+
+class ScaleResolveMap
+  include JSON::Serializable
+
+  property color : String?
+
+  property opacity : String?
+
+  property shape : String?
+
+  property size : String?
+
+  property x : String?
+
+  property y : String?
+end
+
+class SelectionDef
+  include JSON::Serializable
+
+  # Establish a two-way binding between a single selection and input elements
+  # (also known as dynamic query widgets). A binding takes the form of
+  # Vega's [input element binding definition](https://vega.github.io/vega/docs/signals/#bind)
+  # or can be a mapping between projected field/encodings and binding definitions.
+  #
+  # See the [bind transform](bind.html) documentation for more information.
+  #
+  # Establishes a two-way binding between the interval selection and the scales
+  # used within the same view. This allows a user to interactively pan and
+  # zoom the view.
+  property bind : BindUnion?
+
+  # By default, all data values are considered to lie within an empty selection.
+  # When set to `none`, empty selections contain no data values.
+  property empty : String?
+
+  # An array of encoding channels. The corresponding data field values
+  # must match for a data tuple to fall within the selection.
+  property encodings : Array(String)?
+
+  # An array of field names whose values must match for a data tuple to
+  # fall within the selection.
+  property fields : Array(String)?
+
+  # When true, an invisible voronoi diagram is computed to accelerate discrete
+  # selection. The data value _nearest_ the mouse cursor is added to the selection.
+  #
+  # See the [nearest transform](nearest.html) documentation for more information.
+  property nearest : Bool?
+
+  # A [Vega event stream](https://vega.github.io/vega/docs/event-streams/) (object or
+  # selector) that triggers the selection.
+  # For interval selections, the event stream must specify a [start and
+  # end](https://vega.github.io/vega/docs/event-streams/#between-filters).
+  property on : JSON::Any?
+
+  # With layered and multi-view displays, a strategy that determines how
+  # selections' data queries are resolved when applied in a filter transform,
+  # conditional encoding rule, or scale domain.
+  property resolve : String?
+
+  @[JSON::Field(key: "type")]
+  property selection_def_type : String
+
+  # Controls whether data values should be toggled or only ever inserted into
+  # multi selections. Can be `true`, `false` (for insertion only), or a
+  # [Vega expression](https://vega.github.io/vega/docs/expressions/).
+  #
+  # __Default value:__ `true`, which corresponds to `event.shiftKey` (i.e.,
+  # data values are toggled when a user interacts with the shift-key pressed).
+  #
+  # See the [toggle transform](toggle.html) documentation for more information.
+  property toggle : Translate?
+
+  # An interval selection also adds a rectangle mark to depict the
+  # extents of the interval. The `mark` property can be used to customize the
+  # appearance of the mark.
+  property mark : BrushConfig?
+
+  # When truthy, allows a user to interactively move an interval selection
+  # back-and-forth. Can be `true`, `false` (to disable panning), or a
+  # [Vega event stream definition](https://vega.github.io/vega/docs/event-streams/)
+  # which must include a start and end event to trigger continuous panning.
+  #
+  # __Default value:__ `true`, which corresponds to
+  # `[mousedown, window:mouseup] > window:mousemove!` which corresponds to
+  # clicks and dragging within an interval selection to reposition it.
+  property translate : Translate?
+
+  # When truthy, allows a user to interactively resize an interval selection.
+  # Can be `true`, `false` (to disable zooming), or a [Vega event stream
+  # definition](https://vega.github.io/vega/docs/event-streams/). Currently,
+  # only `wheel` events are supported.
+  #
+  #
+  # __Default value:__ `true`, which corresponds to `wheel!`.
+  property zoom : Translate?
+end
+
+class TitleParams
+  include JSON::Serializable
+
+  # The anchor position for placing the title. One of `"start"`, `"middle"`, or `"end"`. For
+  # example, with an orientation of top these anchor positions map to a left-, center-, or
+  # right-aligned title.
+  #
+  # __Default value:__ `"middle"` for [single](spec.html) and [layered](layer.html) views.
+  # `"start"` for other composite views.
+  #
+  # __Note:__ [For now](https://github.com/vega/vega-lite/issues/2875), `anchor` is only
+  # customizable only for [single](spec.html) and [layered](layer.html) views.  For other
+  # composite views, `anchor` is always `"start"`.
+  property anchor : String?
+
+  # The orthogonal offset in pixels by which to displace the title from its position along
+  # the edge of the chart.
+  property offset : Float64?
+
+  # The orientation of the title relative to the chart. One of `"top"` (the default),
+  # `"bottom"`, `"left"`, or `"right"`.
+  property orient : String?
+
+  # A [mark style property](config.html#style) to apply to the title text mark.
+  #
+  # __Default value:__ `"group-title"`.
+  property style : Style?
+
+  # The title text.
+  property text : String
+end
+
+class Transform
+  include JSON::Serializable
+
+  # The `filter` property must be one of the predicate definitions:
+  # (1) an [expression](types.html#expression) string,
+  # where `datum` can be used to refer to the current data object;
+  # (2) one of the field predicates: [equal predicate](filter.html#equal-predicate);
+  # [range predicate](filter.html#range-predicate), [one-of
+  # predicate](filter.html#one-of-predicate);
+  # (3) a [selection predicate](filter.html#selection-predicate);
+  # or (4) a logical operand that combines (1), (2), or (3).
+  property filter : ConditionalValueDefLogicalOperandPredicate?
+
+  # The field for storing the computed formula value.
+  #
+  # The field or fields for storing the computed formula value.
+  # If `from.fields` is specified, the transform will use the same names for `as`.
+  # If `from.fields` is not specified, `as` has to be a string and we put the whole object
+  # into the data under the specified name.
+  #
+  # The output fields at which to write the start and end bin values.
+  #
+  # The output field to write the timeUnit value.
+  @[JSON::Field(key: "as")]
+  property transform_as : Style?
+
+  # A [expression](types.html#expression) string. Use the variable `datum` to refer to the
+  # current data object.
+  property calculate : String?
+
+  # The default value to use if lookup fails.
+  #
+  # __Default value:__ `null`
+  property default : String?
+
+  # Secondary data reference.
+  property from : LookupData?
+
+  # Key in primary data source.
+  property lookup : String?
+
+  # An object indicating bin properties, or simply `true` for using default bin parameters.
+  property bin : Bin?
+
+  # The data field to bin.
+  #
+  # The data field to apply time unit.
+  property field : String?
+
+  # The timeUnit.
+  @[JSON::Field(key: "timeUnit")]
+  property time_unit : String?
+
+  # Array of objects that define fields to aggregate.
+  property aggregate : Array(AggregatedFieldDef)?
+
+  # The data fields to group by. If not specified, a single group containing all data objects
+  # will be used.
+  property groupby : Array(String)?
+end
+
+class AggregatedFieldDef
+  include JSON::Serializable
+
+  # The output field names to use for each aggregated field.
+  @[JSON::Field(key: "as")]
+  property aggregated_field_def_as : String
+
+  # The data field for which to compute aggregate function.
+  property field : String
+
+  # The aggregation operations to apply to the fields, such as sum, average or count.
+  # See the [full list of supported aggregation
+  # operations](https://vega.github.io/vega-lite/docs/aggregate.html#ops)
+  # for more information.
+  property op : String
+end
+
+# Secondary data reference.
+class LookupData
+  include JSON::Serializable
+
+  # Secondary data source to lookup in.
+  property data : Data
+
+  # Fields in foreign data to lookup.
+  # If not specified, the entire object is queried.
+  property fields : Array(String)?
+
+  # Key in data to lookup.
+  property key : String
+end
+
+# An object that describes what fields should be repeated into views that are laid out as a
+# `row` or `column`.
+class Repeat
+  include JSON::Serializable
+
+  # Horizontal repeated views.
+  property column : Array(String)?
+
+  # Vertical repeated views.
+  property row : Array(String)?
+end
+
+# Sets how the visualization size should be determined. If a string, should be one of
+# `"pad"`, `"fit"` or `"none"`.
+# Object values can additionally specify parameters for content sizing and automatic
+# resizing.
+# `"fit"` is only supported for single and layered views that don't use `rangeStep`.
+#
+# __Default value__: `pad`
+alias Autosize = AutoSizeParams | String
+
+# The font weight (e.g., `"bold"`).
+#
+# Font weight for title text.
+alias FontWeightUnion = Float64 | String
+
+# Indicates if labels should be hidden if they exceed the axis range. If `false `(the
+# default) no bounds overlap analysis is performed. If `true`, labels will be hidden if
+# they exceed the axis range by more than 1 pixel. If this property is a number, it
+# specifies the pixel tolerance: the maximum amount by which a label bounding box may
+# exceed the axis range.
+#
+# __Default value:__ `false`.
+#
+# Indicates if the first and last axis labels should be aligned flush with the scale range.
+# Flush alignment for a horizontal axis will left-align the first label and right-align the
+# last label. For vertical axes, bottom and top text baselines are applied instead. If this
+# property is a number, it also indicates the number of pixels by which to offset the first
+# and last labels; for example, a value of 2 will flush-align the first and last labels and
+# also push them 2 pixels outward from the center of the axis. The additional adjustment
+# can sometimes help the labels better visually group with corresponding axis ticks.
+#
+# __Default value:__ `true` for axis of a continuous x-scale. Otherwise, `false`.
+alias Label = Float64 | Bool
+
+alias LabelOverlapUnion = Bool | String
+
+# Font weight of the title. (e.g., `"bold"`).
+#
+# The font weight of the legend title.
+alias TitleFontWeightUnion = Float64 | String
+
+# The default visualization padding, in pixels, from the edge of the visualization canvas
+# to the data rectangle.  If a number, specifies padding for all sides.
+# If an object, the value should have the format `{"left": 5, "top": 5, "right": 5,
+# "bottom": 5}` to specify padding for each side of the visualization.
+#
+# __Default value__: `5`
+alias Padding = Float64 | PaddingClass
+
+# Font weight of the title. (e.g., `"bold"`).
+#
+# The font weight of the legend title.
+alias PrecisionValue = Float64 | String
+
+alias RangeConfigValue = Array(PrecisionValue) | VgScheme
+
+# When truthy, allows a user to interactively move an interval selection
+# back-and-forth. Can be `true`, `false` (to disable panning), or a
+# [Vega event stream definition](https://vega.github.io/vega/docs/event-streams/)
+# which must include a start and end event to trigger continuous panning.
+#
+# __Default value:__ `true`, which corresponds to
+# `[mousedown, window:mouseup] > window:mousemove!` which corresponds to
+# clicks and dragging within an interval selection to reposition it.
+#
+# When truthy, allows a user to interactively resize an interval selection.
+# Can be `true`, `false` (to disable zooming), or a [Vega event stream
+# definition](https://vega.github.io/vega/docs/event-streams/). Currently,
+# only `wheel` events are supported.
+#
+#
+# __Default value:__ `true`, which corresponds to `wheel!`.
+#
+# Controls whether data values should be toggled or only ever inserted into
+# multi selections. Can be `true`, `false` (for insertion only), or a
+# [Vega expression](https://vega.github.io/vega/docs/expressions/).
+#
+# __Default value:__ `true`, which corresponds to `event.shiftKey` (i.e.,
+# data values are toggled when a user interacts with the shift-key pressed).
+#
+# See the [toggle transform](toggle.html) documentation for more information.
+alias Translate = Bool | String
+
+alias ParseUnion = Hash(String, JSON::Any?) | String
+
+# The full data set, included inline. This can be an array of objects or primitive values
+# or a string.
+# Arrays of primitive values are ingested as objects with a `data` property. Strings are
+# parsed according to the specified format type.
+alias Values = Hash(String, JSON::Any?) | String | Array(ValuesValue)
+
+alias ValuesValue = Hash(String, JSON::Any?) | Float64 | Bool | String
+
+alias Bin = BinParams | Bool
+
+alias ColorCondition = ConditionalPredicateMarkPropFieldDefClass | Array(ConditionalValueDef)
+
+# Filter using a selection name.
+#
+# A [selection name](selection.html), or a series of [composed
+# selections](selection.html#compose).
+alias SelectionOperandElement = String | Selection
+
+# Filter using a selection name.
+#
+# A [selection name](selection.html), or a series of [composed
+# selections](selection.html#compose).
+alias ConditionalValueDefSelectionOperand = String | Selection
+
+# The `filter` property must be one of the predicate definitions:
+# (1) an [expression](types.html#expression) string,
+# where `datum` can be used to refer to the current data object;
+# (2) one of the field predicates: [equal predicate](filter.html#equal-predicate);
+# [range predicate](filter.html#range-predicate), [one-of
+# predicate](filter.html#one-of-predicate);
+# (3) a [selection predicate](filter.html#selection-predicate);
+# or (4) a logical operand that combines (1), (2), or (3).
+alias LogicalOperandPredicateElement = Predicate | String
+
+# The `filter` property must be one of the predicate definitions:
+# (1) an [expression](types.html#expression) string,
+# where `datum` can be used to refer to the current data object;
+# (2) one of the field predicates: [equal predicate](filter.html#equal-predicate);
+# [range predicate](filter.html#range-predicate), [one-of
+# predicate](filter.html#one-of-predicate);
+# (3) a [selection predicate](filter.html#selection-predicate);
+# or (4) a logical operand that combines (1), (2), or (3).
+alias ConditionalValueDefLogicalOperandPredicate = Predicate | String
+
+# The value that the field should be equal to.
+alias EqualUnion = DateTime | Float64 | Bool | String
+
+# Value representing the day of a week.  This can be one of: (1) integer value -- `1`
+# represents Monday; (2) case-insensitive day name (e.g., `"Monday"`);  (3)
+# case-insensitive, 3-character short day name (e.g., `"Mon"`).   <br/> **Warning:** A
+# DateTime definition object with `day`** should not be combined with `year`, `quarter`,
+# `month`, or `date`.
+alias Day = Float64 | String
+
+# One of: (1) integer value representing the month from `1`-`12`. `1` represents January;
+# (2) case-insensitive month name (e.g., `"January"`);  (3) case-insensitive, 3-character
+# short month name (e.g., `"Jan"`).
+alias Month = Float64 | String
+
+# The value that the field should be equal to.
+alias OneOfElement = DateTime | Float64 | Bool | String
+
+alias RangeElement = DateTime | Float64
+
+# A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
+# `0` to `1` for opacity).
+#
+# A constant value in visual domain.
+alias ConditionalValueDefValue = Float64 | Bool | String
+
+alias Field = String | RepeatRef
+
+alias LegendValue = DateTime | Float64 | String
+
+# Customized domain values.
+#
+# For _quantitative_ fields, `domain` can take the form of a two-element array with minimum
+# and maximum values.  [Piecewise scales](scale.html#piecewise) can be created by providing
+# a `domain` with more than two entries.
+# If the input field is aggregated, `domain` can also be a string value `"unaggregated"`,
+# indicating that the domain should include the raw data values prior to the aggregation.
+#
+# For _temporal_ fields, `domain` can be a two-element array minimum and maximum values, in
+# the form of either timestamps or the [DateTime definition objects](types.html#datetime).
+#
+# For _ordinal_ and _nominal_ fields, `domain` can be an array that lists valid input
+# values.
+#
+# The `selection` property can be used to [interactively
+# determine](selection.html#scale-domains) the scale domain.
+alias DomainUnion = DomainClass | String | Array(OneOfElement)
+
+# The interpolation method for range values. By default, a general interpolator for
+# numbers, dates, strings and colors (in RGB space) is used. For color ranges, this
+# property allows interpolation in alternative color spaces. Legal values include `rgb`,
+# `hsl`, `hsl-long`, `lab`, `hcl`, `hcl-long`, `cubehelix` and `cubehelix-long` ('-long'
+# variants use longer paths in polar coordinate spaces). If object-valued, this property
+# accepts an object with a string-valued _type_ property and an optional numeric _gamma_
+# property applicable to rgb and cubehelix interpolators. For more, see the [d3-interpolate
+# documentation](https://github.com/d3/d3-interpolate).
+#
+# __Note:__ Sequential scales do not support `interpolate` as they have a fixed
+# interpolator.  Since Vega-Lite uses sequential scales for quantitative fields by default,
+# you have to set the scale `type` to other quantitative scale type such as `"linear"` to
+# customize `interpolate`.
+alias InterpolateUnion = InterpolateParams | String
+
+# Extending the domain so that it starts and ends on nice round values. This method
+# typically modifies the scale’s domain, and may only extend the bounds to the nearest
+# round value. Nicing is useful if the domain is computed from data and may be irregular.
+# For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2,
+# 1.0]_.
+#
+# For quantitative scales such as linear, `nice` can be either a boolean flag or a number.
+# If `nice` is a number, it will represent a desired tick count. This allows greater
+# control over the step size used to extend the bounds, guaranteeing that the returned
+# ticks will exactly cover the domain.
+#
+# For temporal fields with time and utc scales, the `nice` value can be a string indicating
+# the desired time interval. Legal values are `"millisecond"`, `"second"`, `"minute"`,
+# `"hour"`, `"day"`, `"week"`, `"month"`, and `"year"`. Alternatively, `time` and `utc`
+# scales can accept an object-valued interval specifier of the form `{"interval": "month",
+# "step": 3}`, which includes a desired number of interval steps. Here, the domain would
+# snap to quarter (Jan, Apr, Jul, Oct) boundaries.
+#
+# __Default value:__ `true` for unbinned _quantitative_ fields; `false` otherwise.
+alias NiceUnion = Float64 | NiceClass | Bool | String
+
+# The range of the scale. One of:
+#
+# - A string indicating a [pre-defined named scale range](scale.html#range-config) (e.g.,
+# example, `"symbol"`, or `"diverging"`).
+#
+# - For [continuous scales](scale.html#continuous), two-element array indicating  minimum
+# and maximum values, or an array with more than two entries for specifying a [piecewise
+# scale](scale.html#piecewise).
+#
+# - For [discrete](scale.html#discrete) and [discretizing](scale.html#discretizing) scales,
+# an array of desired output values.
+#
+# __Notes:__
+#
+# 1) For [sequential](scale.html#sequential), [ordinal](scale.html#ordinal), and
+# discretizing color scales, you can also specify a color [`scheme`](scale.html#scheme)
+# instead of `range`.
+#
+# 2) Any directly specified `range` for `x` and `y` channels will be ignored. Range can be
+# customized via the view's corresponding [size](size.html) (`width` and `height`) or via
+# [range steps and paddings properties](#range-step) for [band](#band) and [point](#point)
+# scales.
+alias ScaleRange = String | Array(PrecisionValue)
+
+# A string indicating a color [scheme](scale.html#scheme) name (e.g., `"category10"` or
+# `"viridis"`) or a [scheme parameter object](scale.html#scheme-params).
+#
+# Discrete color schemes may be used with [discrete](scale.html#discrete) or
+# [discretizing](scale.html#discretizing) scales. Continuous color schemes are intended for
+# use with [sequential](scales.html#sequential) scales.
+#
+# For the full list of supported scheme, please refer to the [Vega
+# Scheme](https://vega.github.io/vega/docs/schemes/#reference) reference.
+alias Scheme = String | SchemeParams
+
+alias SortUnion = String | SortField
+
+# A constant value in visual domain (e.g., `"red"` / "#0099ff" for color, values between
+# `0` to `1` for opacity).
+#
+# A constant value in visual domain.
+alias ConditionalPredicateValueDefValue = Float64 | Bool | String
+
+alias Detail = FieldDef | Array(FieldDef)
+
+alias HrefCondition = ConditionalPredicateFieldDefClass | Array(ConditionalValueDef)
+
+alias Order = OrderFieldDef | Array(OrderFieldDef)
+
+alias TextCondition = ConditionalPredicateTextFieldDefClass | Array(ConditionalValueDef)
+
+alias AxisValue = DateTime | Float64
+
+# A string describing the mark type (one of `"bar"`, `"circle"`, `"square"`, `"tick"`,
+# `"line"`,
+# * `"area"`, `"point"`, `"rule"`, `"geoshape"`, and `"text"`) or a [mark definition
+# object](mark.html#mark-def).
+alias AnyMark = MarkDef | String
+
+# A string or array of strings indicating the name of custom styles to apply to the mark. A
+# style is a named collection of mark property defaults defined within the [style
+# configuration](mark.html#style-config). If style is an array, later styles will override
+# earlier styles. Any [mark properties](encoding.html#mark-prop) explicitly defined within
+# the `encoding` will override a style default.
+#
+# __Default value:__ The mark's name.  For example, a bar mark will have style `"bar"` by
+# default.
+# __Note:__ Any specified style will augment the default style. For example, a bar mark
+# with `"style": "foo"` will receive from `config.style.bar` and `config.style.foo` (the
+# specified style `"foo"` has higher precedence).
+#
+# A [mark style property](config.html#style) to apply to the title text mark.
+#
+# __Default value:__ `"group-title"`.
+alias Style = String | Array(String)
+
+alias BindUnion = String | Hash(String, VgBinding)
+
+alias Title = String | TitleParams
