├── .git-blame-ignore-revs ├── .githooks └── pre-commit.sh ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature--new-type-support.md └── workflows │ ├── ci.yml │ └── clean.yml ├── .gitignore ├── .mergify.yml ├── .sbtopts ├── .scala-steward.conf ├── .scalafmt.conf ├── CHANGELOG.md ├── LICENSE ├── README.md ├── api └── src │ ├── main │ └── scala │ │ └── json │ │ └── Json.scala │ └── test │ ├── scala-2.11 │ └── com.github.andyglow.jsonschema.model │ │ └── UserProfile.scala │ ├── scala-2.12 │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ ├── NoneForDefaultValueSpec.scala │ │ ├── SchemaMacro212Spec.scala │ │ └── model │ │ └── UserProfile.scala │ ├── scala-2.13 │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ ├── NoneForDefaultValueSpec.scala │ │ ├── SchemaMacro213Spec.scala │ │ └── model │ │ └── UserProfile.scala │ └── scala │ └── com │ ├── example │ └── unrelated │ │ ├── CommentsMain.scala │ │ ├── SchemaMacroNamesSpec.scala │ │ └── models.scala │ └── github │ └── andyglow │ └── jsonschema │ ├── AsDraft04Spec.scala │ ├── AsDraft06Spec.scala │ ├── AsDraft07Spec.scala │ ├── AsDraft09Spec.scala │ ├── AsDraft12Spec.scala │ ├── CRLFSourceSpec.scala │ ├── DefinitionsSpec.scala │ ├── DiscriminatorSpec.scala │ ├── DocumentationSpec.scala │ ├── EnumSpec.scala │ ├── Example.scala │ ├── GenericSchemaMacroSpec.scala │ ├── JsonMatchers.scala │ ├── RWSpec.scala │ ├── RecursiveTypesSpec.scala │ ├── SchemaMacroSpec.scala │ ├── SchemaMatchers.scala │ ├── SumTypeModels.scala │ ├── SumTypeWithConstantsSpec.scala │ ├── SumTypeWithEnumsSpec.scala │ ├── TypeSignatureSpec.scala │ └── model │ ├── Active.scala │ ├── BetaFeature.scala │ ├── Credentials.scala │ ├── Foo.scala │ ├── Notes.scala │ └── Role.scala ├── build.sbt ├── core └── src │ ├── main │ ├── scala-2.11 │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ ├── json │ │ │ └── LowPriorityArrayImplicits.scala │ │ │ └── scalamigration.scala │ ├── scala-2.12 │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ ├── json │ │ │ └── LowPriorityArrayImplicits.scala │ │ │ └── scalamigration.scala │ ├── scala-2.13 │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ ├── json │ │ │ └── LowPriorityArrayImplicits.scala │ │ │ └── scalamigration.scala │ ├── scala-3 │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ ├── json │ │ │ └── LowPriorityArrayImplicits.scala │ │ │ └── scalamigration.scala │ └── scala │ │ ├── com │ │ └── github │ │ │ └── andyglow │ │ │ ├── json │ │ │ ├── Escaped.scala │ │ │ ├── JsonFormatter.scala │ │ │ ├── ToValue.scala │ │ │ ├── Value.scala │ │ │ ├── ValueSchema.scala │ │ │ └── comparison.scala │ │ │ ├── jsonschema │ │ │ ├── AsDraft04.scala │ │ │ ├── AsDraft06.scala │ │ │ ├── AsDraft07.scala │ │ │ ├── AsDraft09.scala │ │ │ ├── AsDraft12.scala │ │ │ ├── AsDraftSupport.scala │ │ │ ├── AsRaw.scala │ │ │ ├── AsTree.scala │ │ │ ├── AsValue.scala │ │ │ ├── AsValueBuilder.scala │ │ │ ├── Constants.scala │ │ │ ├── Post09.scala │ │ │ ├── Pre09.scala │ │ │ ├── SchemaEquality.scala │ │ │ ├── TypeSignature.scala │ │ │ └── package.scala │ │ │ └── tree │ │ │ ├── Renderer.scala │ │ │ ├── Result.scala │ │ │ ├── Tree.scala │ │ │ ├── Truncated.scala │ │ │ └── Util.scala │ │ └── json │ │ ├── Schema.scala │ │ └── schema │ │ ├── Flag.scala │ │ ├── Predef.scala │ │ ├── Version.scala │ │ ├── definition.scala │ │ ├── description.scala │ │ ├── discriminator.scala │ │ ├── discriminatorKey.scala │ │ ├── readOnly.scala │ │ ├── title.scala │ │ ├── typeHint.scala │ │ ├── validation │ │ ├── Def.scala │ │ ├── Instance.scala │ │ └── Magnet.scala │ │ └── writeOnly.scala │ └── test │ ├── scala-2.11 │ └── com │ │ └── github │ │ └── andyglow │ │ └── testsupport.scala │ ├── scala-2.12 │ └── com │ │ └── github │ │ └── andyglow │ │ └── testsupport.scala │ ├── scala-2.13 │ └── com │ │ └── github │ │ └── andyglow │ │ └── testsupport.scala │ └── scala │ ├── com │ └── github │ │ └── andyglow │ │ ├── json │ │ ├── DiffSpec.scala │ │ ├── JsonFormatterSpec.scala │ │ ├── ValueDslSpec.scala │ │ └── ValueSpec.scala │ │ └── jsonschema │ │ └── SchemaEqualitySpec.scala │ └── json │ └── SchemaSpec.scala ├── macros └── src │ └── main │ └── scala │ └── com │ └── github │ └── andyglow │ └── jsonschema │ ├── Macroses.scala │ ├── ScalaParts.scala │ ├── SchemaTypes.scala │ ├── UArrays.scala │ ├── UCommons.scala │ ├── UContext.scala │ ├── UDictionaries.scala │ ├── UEnums.scala │ ├── UFieldDecorations.scala │ ├── UFlags.scala │ ├── UImplicits.scala │ ├── UJsonValueType.scala │ ├── ULogging.scala │ ├── UProductTypes.scala │ ├── URecursiveTypes.scala │ ├── UScalaParsers.scala │ ├── UScaladocs.scala │ ├── USignatures.scala │ ├── USumTypes.scala │ ├── UTypeAnnotations.scala │ ├── UValueTypes.scala │ └── utils │ └── ScalaParser.scala ├── modules ├── cats │ └── src │ │ ├── main │ │ ├── scala-2.12- │ │ │ └── com │ │ │ │ └── github │ │ │ │ └── andyglow │ │ │ │ └── jsonschema │ │ │ │ ├── ScalaVersionSpecificCatsSupport.scala │ │ │ │ └── ScalaVersionSpecificLowPriorityCatsSupport.scala │ │ ├── scala-2.13+ │ │ │ └── com │ │ │ │ └── github │ │ │ │ └── andyglow │ │ │ │ └── jsonschema │ │ │ │ ├── ScalaVersionSpecificCatsSupport.scala │ │ │ │ └── ScalaVersionSpecificLowPriorityCatsSupport.scala │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── CatsSupport.scala │ │ └── test │ │ ├── scala-2.12- │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── ScalaVersionSpecificCatsSupportSpec.scala │ │ ├── scala-2.13+ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── ScalaVersionSpecificCatsSupportSpec.scala │ │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ └── CatsSupportSpec.scala ├── circe-json │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── AsCirce.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ ├── AsCirceSpec.scala │ │ └── UserProfileJson.scala ├── derived │ └── src │ │ ├── main │ │ └── scala │ │ │ └── json │ │ │ └── schema │ │ │ └── derived │ │ │ └── DerivedSchema.scala │ │ └── test │ │ └── scala │ │ └── json │ │ └── schema │ │ └── derived │ │ └── DerivedSchemaSpec.scala ├── enumeratum │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── EnumeratumSupport.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ └── EnumeratumSupportSpec.scala ├── joda-time │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── JodaTimeSupport.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ └── JodaTimeSupportSpec.scala ├── json4s-json │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── AsJson4s.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ ├── AsJson4sSpec.scala │ │ └── UserProfileJson.scala ├── parser │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── github │ │ │ │ └── andyglow │ │ │ │ └── json │ │ │ │ ├── JsonHandler.java │ │ │ │ ├── JsonParseException.java │ │ │ │ └── JsonParser.java │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ ├── json │ │ │ ├── MutableValue.scala │ │ │ └── ParseJson.scala │ │ │ └── jsonschema │ │ │ └── ParseJsonSchema.scala │ │ └── test │ │ ├── resources │ │ ├── examples │ │ ├── i_number_double_huge_neg_exp.json │ │ ├── i_number_huge_exp.json │ │ ├── i_number_neg_int_huge_exp.json │ │ ├── i_number_pos_double_huge_exp.json │ │ ├── i_number_real_neg_overflow.json │ │ ├── i_number_real_pos_overflow.json │ │ ├── i_number_real_underflow.json │ │ ├── i_number_too_big_neg_int.json │ │ ├── i_number_too_big_pos_int.json │ │ ├── i_number_very_big_negative_int.json │ │ ├── i_object_key_lone_2nd_surrogate.json │ │ ├── i_string_1st_surrogate_but_2nd_missing.json │ │ ├── i_string_1st_valid_surrogate_2nd_invalid.json │ │ ├── i_string_UTF-16LE_with_BOM.json │ │ ├── i_string_UTF-8_invalid_sequence.json │ │ ├── i_string_UTF8_surrogate_U+D800.json │ │ ├── i_string_incomplete_surrogate_and_escape_valid.json │ │ ├── i_string_incomplete_surrogate_pair.json │ │ ├── i_string_incomplete_surrogates_escape_valid.json │ │ ├── i_string_invalid_lonely_surrogate.json │ │ ├── i_string_invalid_surrogate.json │ │ ├── i_string_invalid_utf-8.json │ │ ├── i_string_inverted_surrogates_U+1D11E.json │ │ ├── i_string_iso_latin_1.json │ │ ├── i_string_lone_second_surrogate.json │ │ ├── i_string_lone_utf8_continuation_byte.json │ │ ├── i_string_not_in_unicode_range.json │ │ ├── i_string_overlong_sequence_2_bytes.json │ │ ├── i_string_overlong_sequence_6_bytes.json │ │ ├── i_string_overlong_sequence_6_bytes_null.json │ │ ├── i_string_truncated-utf-8.json │ │ ├── i_string_utf16BE_no_BOM.json │ │ ├── i_string_utf16LE_no_BOM.json │ │ ├── i_structure_500_nested_arrays.json │ │ ├── i_structure_UTF-8_BOM_empty_object.json │ │ ├── n_array_1_true_without_comma.json │ │ ├── n_array_a_invalid_utf8.json │ │ ├── n_array_colon_instead_of_comma.json │ │ ├── n_array_comma_after_close.json │ │ ├── n_array_comma_and_number.json │ │ ├── n_array_double_comma.json │ │ ├── n_array_double_extra_comma.json │ │ ├── n_array_extra_close.json │ │ ├── n_array_extra_comma.json │ │ ├── n_array_incomplete.json │ │ ├── n_array_incomplete_invalid_value.json │ │ ├── n_array_inner_array_no_comma.json │ │ ├── n_array_invalid_utf8.json │ │ ├── n_array_items_separated_by_semicolon.json │ │ ├── n_array_just_comma.json │ │ ├── n_array_just_minus.json │ │ ├── n_array_missing_value.json │ │ ├── n_array_newlines_unclosed.json │ │ ├── n_array_number_and_comma.json │ │ ├── n_array_number_and_several_commas.json │ │ ├── n_array_spaces_vertical_tab_formfeed.json │ │ ├── n_array_star_inside.json │ │ ├── n_array_unclosed.json │ │ ├── n_array_unclosed_trailing_comma.json │ │ ├── n_array_unclosed_with_new_lines.json │ │ ├── n_array_unclosed_with_object_inside.json │ │ ├── n_incomplete_false.json │ │ ├── n_incomplete_null.json │ │ ├── n_incomplete_true.json │ │ ├── n_multidigit_number_then_00.json │ │ ├── n_number_++.json │ │ ├── n_number_+1.json │ │ ├── n_number_+Inf.json │ │ ├── n_number_-01.json │ │ ├── n_number_-1.0..json │ │ ├── n_number_-2..json │ │ ├── n_number_-NaN.json │ │ ├── n_number_.-1.json │ │ ├── n_number_.2e-3.json │ │ ├── n_number_0.1.2.json │ │ ├── n_number_0.3e+.json │ │ ├── n_number_0.3e.json │ │ ├── n_number_0.e1.json │ │ ├── n_number_0_capital_E+.json │ │ ├── n_number_0_capital_E.json │ │ ├── n_number_0e+.json │ │ ├── n_number_0e.json │ │ ├── n_number_1.0e+.json │ │ ├── n_number_1.0e-.json │ │ ├── n_number_1.0e.json │ │ ├── n_number_1_000.json │ │ ├── n_number_1eE2.json │ │ ├── n_number_2.e+3.json │ │ ├── n_number_2.e-3.json │ │ ├── n_number_2.e3.json │ │ ├── n_number_9.e+.json │ │ ├── n_number_Inf.json │ │ ├── n_number_NaN.json │ │ ├── n_number_U+FF11_fullwidth_digit_one.json │ │ ├── n_number_expression.json │ │ ├── n_number_hex_1_digit.json │ │ ├── n_number_hex_2_digits.json │ │ ├── n_number_infinity.json │ │ ├── n_number_invalid+-.json │ │ ├── n_number_invalid-negative-real.json │ │ ├── n_number_invalid-utf-8-in-bigger-int.json │ │ ├── n_number_invalid-utf-8-in-exponent.json │ │ ├── n_number_invalid-utf-8-in-int.json │ │ ├── n_number_minus_infinity.json │ │ ├── n_number_minus_sign_with_trailing_garbage.json │ │ ├── n_number_minus_space_1.json │ │ ├── n_number_neg_int_starting_with_zero.json │ │ ├── n_number_neg_real_without_int_part.json │ │ ├── n_number_neg_with_garbage_at_end.json │ │ ├── n_number_real_garbage_after_e.json │ │ ├── n_number_real_with_invalid_utf8_after_e.json │ │ ├── n_number_real_without_fractional_part.json │ │ ├── n_number_starting_with_dot.json │ │ ├── n_number_with_alpha.json │ │ ├── n_number_with_alpha_char.json │ │ ├── n_number_with_leading_zero.json │ │ ├── n_object_bad_value.json │ │ ├── n_object_bracket_key.json │ │ ├── n_object_comma_instead_of_colon.json │ │ ├── n_object_double_colon.json │ │ ├── n_object_emoji.json │ │ ├── n_object_garbage_at_end.json │ │ ├── n_object_key_with_single_quotes.json │ │ ├── n_object_lone_continuation_byte_in_key_and_trailing_comma.json │ │ ├── n_object_missing_colon.json │ │ ├── n_object_missing_key.json │ │ ├── n_object_missing_semicolon.json │ │ ├── n_object_missing_value.json │ │ ├── n_object_no-colon.json │ │ ├── n_object_non_string_key.json │ │ ├── n_object_non_string_key_but_huge_number_instead.json │ │ ├── n_object_repeated_null_null.json │ │ ├── n_object_several_trailing_commas.json │ │ ├── n_object_single_quote.json │ │ ├── n_object_trailing_comma.json │ │ ├── n_object_trailing_comment.json │ │ ├── n_object_trailing_comment_open.json │ │ ├── n_object_trailing_comment_slash_open.json │ │ ├── n_object_trailing_comment_slash_open_incomplete.json │ │ ├── n_object_two_commas_in_a_row.json │ │ ├── n_object_unquoted_key.json │ │ ├── n_object_unterminated-value.json │ │ ├── n_object_with_single_string.json │ │ ├── n_object_with_trailing_garbage.json │ │ ├── n_single_space.json │ │ ├── n_string_1_surrogate_then_escape.json │ │ ├── n_string_1_surrogate_then_escape_u.json │ │ ├── n_string_1_surrogate_then_escape_u1.json │ │ ├── n_string_1_surrogate_then_escape_u1x.json │ │ ├── n_string_accentuated_char_no_quotes.json │ │ ├── n_string_backslash_00.json │ │ ├── n_string_escape_x.json │ │ ├── n_string_escaped_backslash_bad.json │ │ ├── n_string_escaped_ctrl_char_tab.json │ │ ├── n_string_escaped_emoji.json │ │ ├── n_string_incomplete_escape.json │ │ ├── n_string_incomplete_escaped_character.json │ │ ├── n_string_incomplete_surrogate.json │ │ ├── n_string_incomplete_surrogate_escape_invalid.json │ │ ├── n_string_invalid-utf-8-in-escape.json │ │ ├── n_string_invalid_backslash_esc.json │ │ ├── n_string_invalid_unicode_escape.json │ │ ├── n_string_invalid_utf8_after_escape.json │ │ ├── n_string_leading_uescaped_thinspace.json │ │ ├── n_string_no_quotes_with_bad_escape.json │ │ ├── n_string_single_doublequote.json │ │ ├── n_string_single_quote.json │ │ ├── n_string_single_string_no_double_quotes.json │ │ ├── n_string_start_escape_unclosed.json │ │ ├── n_string_unescaped_crtl_char.json │ │ ├── n_string_unescaped_newline.json │ │ ├── n_string_unescaped_tab.json │ │ ├── n_string_unicode_CapitalU.json │ │ ├── n_string_with_trailing_garbage.json │ │ ├── n_structure_100000_opening_arrays.json │ │ ├── n_structure_U+2060_word_joined.json │ │ ├── n_structure_UTF8_BOM_no_data.json │ │ ├── n_structure_angle_bracket_..json │ │ ├── n_structure_angle_bracket_null.json │ │ ├── n_structure_array_trailing_garbage.json │ │ ├── n_structure_array_with_extra_array_close.json │ │ ├── n_structure_array_with_unclosed_string.json │ │ ├── n_structure_ascii-unicode-identifier.json │ │ ├── n_structure_capitalized_True.json │ │ ├── n_structure_close_unopened_array.json │ │ ├── n_structure_comma_instead_of_closing_brace.json │ │ ├── n_structure_double_array.json │ │ ├── n_structure_end_array.json │ │ ├── n_structure_incomplete_UTF8_BOM.json │ │ ├── n_structure_lone-invalid-utf-8.json │ │ ├── n_structure_lone-open-bracket.json │ │ ├── n_structure_no_data.json │ │ ├── n_structure_null-byte-outside-string.json │ │ ├── n_structure_number_with_trailing_garbage.json │ │ ├── n_structure_object_followed_by_closing_object.json │ │ ├── n_structure_object_unclosed_no_value.json │ │ ├── n_structure_object_with_comment.json │ │ ├── n_structure_object_with_trailing_garbage.json │ │ ├── n_structure_open_array_apostrophe.json │ │ ├── n_structure_open_array_comma.json │ │ ├── n_structure_open_array_object.json │ │ ├── n_structure_open_array_open_object.json │ │ ├── n_structure_open_array_open_string.json │ │ ├── n_structure_open_array_string.json │ │ ├── n_structure_open_object.json │ │ ├── n_structure_open_object_close_array.json │ │ ├── n_structure_open_object_comma.json │ │ ├── n_structure_open_object_open_array.json │ │ ├── n_structure_open_object_open_string.json │ │ ├── n_structure_open_object_string_with_apostrophes.json │ │ ├── n_structure_open_open.json │ │ ├── n_structure_single_eacute.json │ │ ├── n_structure_single_star.json │ │ ├── n_structure_trailing_#.json │ │ ├── n_structure_uescaped_LF_before_string.json │ │ ├── n_structure_unclosed_array.json │ │ ├── n_structure_unclosed_array_partial_null.json │ │ ├── n_structure_unclosed_array_unfinished_false.json │ │ ├── n_structure_unclosed_array_unfinished_true.json │ │ ├── n_structure_unclosed_object.json │ │ ├── n_structure_unicode-identifier.json │ │ ├── n_structure_whitespace_U+2060_word_joiner.json │ │ ├── n_structure_whitespace_formfeed.json │ │ ├── y_array_arraysWithSpaces.json │ │ ├── y_array_empty-string.json │ │ ├── y_array_empty.json │ │ ├── y_array_ending_with_newline.json │ │ ├── y_array_false.json │ │ ├── y_array_heterogeneous.json │ │ ├── y_array_null.json │ │ ├── y_array_with_1_and_newline.json │ │ ├── y_array_with_leading_space.json │ │ ├── y_array_with_several_null.json │ │ ├── y_array_with_trailing_space.json │ │ ├── y_number.json │ │ ├── y_number_0e+1.json │ │ ├── y_number_0e1.json │ │ ├── y_number_after_space.json │ │ ├── y_number_double_close_to_zero.json │ │ ├── y_number_int_with_exp.json │ │ ├── y_number_minus_zero.json │ │ ├── y_number_negative_int.json │ │ ├── y_number_negative_one.json │ │ ├── y_number_negative_zero.json │ │ ├── y_number_real_capital_e.json │ │ ├── y_number_real_capital_e_neg_exp.json │ │ ├── y_number_real_capital_e_pos_exp.json │ │ ├── y_number_real_exponent.json │ │ ├── y_number_real_fraction_exponent.json │ │ ├── y_number_real_neg_exp.json │ │ ├── y_number_real_pos_exponent.json │ │ ├── y_number_simple_int.json │ │ ├── y_number_simple_real.json │ │ ├── y_object.json │ │ ├── y_object_basic.json │ │ ├── y_object_duplicated_key.json │ │ ├── y_object_duplicated_key_and_value.json │ │ ├── y_object_empty.json │ │ ├── y_object_empty_key.json │ │ ├── y_object_escaped_null_in_key.json │ │ ├── y_object_extreme_numbers.json │ │ ├── y_object_long_strings.json │ │ ├── y_object_simple.json │ │ ├── y_object_string_unicode.json │ │ ├── y_object_with_newlines.json │ │ ├── y_string_1_2_3_bytes_UTF-8_sequences.json │ │ ├── y_string_accepted_surrogate_pair.json │ │ ├── y_string_accepted_surrogate_pairs.json │ │ ├── y_string_allowed_escapes.json │ │ ├── y_string_backslash_and_u_escaped_zero.json │ │ ├── y_string_backslash_doublequotes.json │ │ ├── y_string_comments.json │ │ ├── y_string_double_escape_a.json │ │ ├── y_string_double_escape_n.json │ │ ├── y_string_escaped_control_character.json │ │ ├── y_string_escaped_noncharacter.json │ │ ├── y_string_in_array.json │ │ ├── y_string_in_array_with_leading_space.json │ │ ├── y_string_last_surrogates_1_and_2.json │ │ ├── y_string_nbsp_uescaped.json │ │ ├── y_string_nonCharacterInUTF-8_U+10FFFF.json │ │ ├── y_string_nonCharacterInUTF-8_U+FFFF.json │ │ ├── y_string_null_escape.json │ │ ├── y_string_one-byte-utf-8.json │ │ ├── y_string_pi.json │ │ ├── y_string_reservedCharacterInUTF-8_U+1BFFF.json │ │ ├── y_string_simple_ascii.json │ │ ├── y_string_space.json │ │ ├── y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json │ │ ├── y_string_three-byte-utf-8.json │ │ ├── y_string_two-byte-utf-8.json │ │ ├── y_string_u+2028_line_sep.json │ │ ├── y_string_u+2029_par_sep.json │ │ ├── y_string_uEscape.json │ │ ├── y_string_uescaped_newline.json │ │ ├── y_string_unescaped_char_delete.json │ │ ├── y_string_unicode.json │ │ ├── y_string_unicodeEscapedBackslash.json │ │ ├── y_string_unicode_2.json │ │ ├── y_string_unicode_U+10FFFE_nonchar.json │ │ ├── y_string_unicode_U+1FFFE_nonchar.json │ │ ├── y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json │ │ ├── y_string_unicode_U+2064_invisible_plus.json │ │ ├── y_string_unicode_U+FDD0_nonchar.json │ │ ├── y_string_unicode_U+FFFE_nonchar.json │ │ ├── y_string_unicode_escaped_double_quote.json │ │ ├── y_string_utf8.json │ │ ├── y_string_with_del_character.json │ │ ├── y_structure_lonely_false.json │ │ ├── y_structure_lonely_int.json │ │ ├── y_structure_lonely_negative_real.json │ │ ├── y_structure_lonely_null.json │ │ ├── y_structure_lonely_string.json │ │ ├── y_structure_lonely_true.json │ │ ├── y_structure_string_empty.json │ │ ├── y_structure_trailing_newline.json │ │ ├── y_structure_true_in_array.json │ │ └── y_structure_whitespace_array.json │ │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ ├── json │ │ ├── ParseJsonBulkSpec.scala │ │ └── ParseJsonSpec.scala │ │ └── jsonschema │ │ └── ParseJsonSchemaSpec.scala ├── play-json │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── AsPlay.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ ├── AsPlaySpec.scala │ │ ├── Person.scala │ │ └── UserProfileJson.scala ├── refined │ ├── README.md │ ├── TODO.md │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ ├── RefinedSupport.scala │ │ │ └── refined │ │ │ ├── AST.scala │ │ │ ├── Extractors.scala │ │ │ ├── HasContext.scala │ │ │ ├── HasLog.scala │ │ │ ├── Logic.scala │ │ │ ├── Math.scala │ │ │ └── RefinedMacro.scala │ │ └── test │ │ └── scala │ │ └── com.github.andyglow.jsonschema.refined │ │ ├── FsExample.scala │ │ ├── RefinedAliasedSpec.scala │ │ ├── RefinedBooleanSpec.scala │ │ ├── RefinedCollectionsSpec.scala │ │ ├── RefinedEscapedSpec.scala │ │ ├── RefinedNumbersSpec.scala │ │ └── RefinedStringsSpec.scala ├── spray-json │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── github │ │ │ └── andyglow │ │ │ └── jsonschema │ │ │ └── AsSpray.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ ├── AsSpraySpec.scala │ │ └── UserProfileJson.scala └── u-json │ └── src │ ├── main │ └── scala │ │ └── com │ │ └── github │ │ └── andyglow │ │ └── jsonschema │ │ └── AsU.scala │ └── test │ └── scala │ └── com │ └── github │ └── andyglow │ └── jsonschema │ ├── AsUSpec.scala │ └── UserProfileJson.scala ├── project ├── CompilerOptions.scala ├── CustomGithubActions.scala ├── ScalaVer.scala ├── build.properties └── plugins.sbt ├── src └── main │ └── paradox │ ├── builtin.md │ ├── getting-started.md │ ├── index.md │ ├── modules │ ├── cats.md │ ├── index.md │ ├── joda-time.md │ ├── json.md │ └── refined.md │ └── overview.md └── version.sbt /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Scala Steward: Reformat with scalafmt 3.7.11 2 | be846dbd36ec41d3257ad0c09d5b604d17424fa8 3 | -------------------------------------------------------------------------------- /.githooks/pre-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | /usr/local/bin/scalafmt-native --test -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: andyglow # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: andyglow 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Code describing your situation 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Actual results** 20 | Inconsistently resulted schema or compilation errors, or other type of exceptions. 21 | 22 | **Versions:** 23 | - jsonschema version 24 | - scala version 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature--new-type-support.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Feature: New Type Support' 3 | about: Request for a new type support 4 | title: '' 5 | labels: enhancement 6 | assignees: andyglow 7 | 8 | --- 9 | 10 | **Describe a type or a family of types** 11 | - name: joda-money 12 | - url: https://www.joda.org/joda-money/ 13 | 14 | **Is your feature request related to a problem? Please describe.** 15 | A clear and concise description of what the problem is. Ex. We are working on [..] and we need this for [..] 16 | 17 | **Describe the solution you'd like** 18 | A clear and concise description of what you want to happen. 19 | 20 | **Describe alternatives you've considered** 21 | A clear and concise description of any alternative solutions or features you've considered. 22 | 23 | **Additional context** 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Continuous Integration 9 | 10 | on: 11 | pull_request: 12 | branches: ['**'] 13 | push: 14 | branches: ['**'] 15 | 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | jobs: 20 | build: 21 | name: Build and Test 22 | strategy: 23 | matrix: 24 | os: [ubuntu-latest] 25 | scala: [2.13.14, 2.12.20, 2.11.12] 26 | java: [temurin@11] 27 | runs-on: ${{ matrix.os }} 28 | steps: 29 | - name: Checkout current branch (full) 30 | uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 33 | 34 | - name: Setup Java (temurin@11) 35 | if: matrix.java == 'temurin@11' 36 | uses: actions/setup-java@v4 37 | with: 38 | distribution: temurin 39 | java-version: 11 40 | cache: sbt 41 | 42 | - name: Setup sbt 43 | uses: sbt/setup-sbt@v1 44 | 45 | - name: Check that workflows are up to date 46 | run: sbt '++ ${{ matrix.scala }}' githubWorkflowCheck 47 | 48 | - name: Build project 49 | run: sbt '++ ${{ matrix.scala }}' test 50 | 51 | - name: Generate Code Coverage Reports 52 | if: matrix.scala == '2.13.14' 53 | run: sbt '++ ${{ matrix.scala }}' clean coverage test 54 | 55 | - name: Aggregate Code Coverage Report 56 | if: matrix.scala == '2.13.14' 57 | run: sbt '++ ${{ matrix.scala }}' coverageAggregate 58 | 59 | - name: Upload Code Coverage Report 60 | if: matrix.scala == '2.13.14' 61 | uses: codecov/codecov-action@v3 62 | with: 63 | token: ${{ secrets.CODECOV_TOKEN }} 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bsp 2 | .DS_Store 3 | .idea/ 4 | target/ 5 | project/target 6 | 7 | /TODO.md 8 | *.afdesign 9 | *.afphoto -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: Automatic merge PRs from scala-steward 3 | conditions: 4 | - author=scala-steward 5 | actions: 6 | merge: 7 | method: squash 8 | -------------------------------------------------------------------------------- /.sbtopts: -------------------------------------------------------------------------------- 1 | -J-Xmx4G 2 | -J-Xss15M -------------------------------------------------------------------------------- /.scala-steward.conf: -------------------------------------------------------------------------------- 1 | updates.ignore = [ 2 | { groupId = "org.scala-sbt", artifactId = "sbt" } 3 | ] -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "3.8.3" 2 | runner.dialect = scala213 3 | maxColumn = 200 # For my wide 30" display. 4 | project.git = true 5 | project.excludeFilters = ["target/"] 6 | align.preset = more 7 | align.stripMargin = true 8 | align.arrowEnumeratorGenerator = true 9 | assumeStandardLibraryStripMargin = true 10 | includeCurlyBraceInSelectChains = false 11 | newlines.penalizeSingleSelectMultiArgList = false 12 | continuationIndent.defnSite = 2 13 | docstrings.style = SpaceAsterisk 14 | 15 | fileOverride { 16 | "glob:**/scala-3*/**" { 17 | runner.dialect = scala3 18 | } 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | - **0.7.10** 2 | - draft 2020-12 implemented 3 | - dependencies updated 4 | - **0.7.1** 5 | - draft 2019-09 implemented 6 | - initial json-diff functionality added 7 | ... 8 | - **0.6.3** 9 | - fixed scala.None handling for the default values of case class parameters (scala 2.11 is not supported this kind of functionality) 10 | - **0.6.2** 11 | - added annotations for 12 | - definition enforcement 13 | - discriminator/discriminatorKey 14 | - **0.6.1** 15 | - added support for scala.Enumeration 16 | - default values won't be inferred for scala.None 17 | - **0.6.0** 18 | ... 19 | - **0.2.7-M1** 20 | - Add initial code for supporting #18. Use scaladoc for descriptions. 21 | Added support for case classes and fields. 22 | - todo: 23 | - add scaladoc support for sum types 24 | - here is some problem. comments live in source files and get erased after compilation, so resulted bytecode 25 | doesn't have this info and thus schemas derived from such models won't have descriptions. 26 | this might probably be solved by packing scaladocs into annotations by macro or compiler plugin and 27 | have those reused my SchemaMacro. Here is an example of something like this: 28 | https://medium.com/@takezoe/a-compiler-plugin-makes-scaladoc-readable-at-runtime-aecbebccb794 29 | - **0.2.6** 30 | - hot-fix release. disable mistakingly left enabled debug mode for Refined macro 31 | - **0.2.5** 32 | - Improve Schema matching. 33 | Since now Ref and Validations are taken into an account during schema comparision. 34 | - Cats version upgraded to 2.1.1 for scala 2.12 and greater 35 | - Support Refined types (0.9.13 for scala 2.12, 2.13, 0.9.12 for scala 2.11) 36 | - **0.2.4** 37 | - Support Cats data types (non-empty-*) 38 | - Support generic product types 39 | - Module `scala-jsonschema-api` renamed into `scala-jsonschema` 40 | - **0.2.3** 41 | - Support generic sum types (aka sealed trait hierarchies) 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Andriy Onyshchuk 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /api/src/main/scala/json/Json.scala: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import com.github.andyglow.jsonschema._ 4 | import json.Schema.`object` 5 | import json.schema.{Predef, Version} 6 | 7 | import scala.language.experimental.macros 8 | 9 | object Json { 10 | 11 | def schema[T]: Schema[T] = macro Macroses.deriveSchema[T] 12 | 13 | def objectSchema[T](decorations: (String, String)*): `object`[T] = 14 | macro Macroses.deriveObjectSchema[T] 15 | 16 | def sig[T]: TypeSignature[T] = macro Macroses.deriveSignature[T] 17 | 18 | object auto { 19 | 20 | implicit def derived[T]: Predef[T] = macro Macroses.derivePredef[T] 21 | } 22 | 23 | def stringify[T, V <: Version: AsValueBuilder](schema: Schema[T], version: V): String = 24 | schema.stringify(version) 25 | } 26 | -------------------------------------------------------------------------------- /api/src/test/scala-2.11/com.github.andyglow.jsonschema.model/UserProfile.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | case class UserProfile( 4 | firstName: String, 5 | middleName: Option[String], 6 | lastName: String, 7 | age: Int, 8 | enabledFeatures: Set[BetaFeature] = Set(F0, F1), 9 | active: Active = On, 10 | credentials: Credentials = Credentials("anonymous", "-"), 11 | role: Role = Role.User, 12 | lastLoginMs: Option[Long], // = None; not supported currently for scala 2.11 13 | notes: Option[Notes] = Some(Notes("initial note", Nil)) 14 | ) 15 | -------------------------------------------------------------------------------- /api/src/test/scala-2.12/com/github/andyglow/jsonschema/NoneForDefaultValueSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json._ 4 | import json.Schema._ 5 | import json.Schema.`object`._ 6 | import org.scalatest.matchers.should.Matchers._ 7 | import org.scalatest.funsuite.AnyFunSuite 8 | import com.github.andyglow.jsonschema.{model => case0} 9 | 10 | object NoneForDefaultValueCases { 11 | 12 | object case1 { 13 | case class Foo(value: Option[String] = None) 14 | } 15 | } 16 | 17 | object case2 { 18 | case class Foo(value: Option[String] = None) 19 | } 20 | 21 | class NoneForDefaultValueSpec extends AnyFunSuite { 22 | import NoneForDefaultValueCases._ 23 | 24 | test("scala.None for default value") { 25 | 26 | // type id specified in external file 27 | Json.schema[case0.Foo] shouldBe `object`(Field("value", `string`, required = false)) 28 | 29 | // type is specified in same file 30 | Json.schema[case2.Foo] shouldBe `object`(Field("value", `string`, required = false)) 31 | 32 | // type is specified in same file, companion object 33 | Json.schema[case1.Foo] shouldBe `object`(Field("value", `string`, required = false)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /api/src/test/scala-2.12/com/github/andyglow/jsonschema/SchemaMacro212Spec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json.Json 4 | import json.Schema.{`number`, `object`, `oneof`} 5 | import org.scalatest.matchers.should.Matchers._ 6 | import org.scalatest.wordspec.AnyWordSpec 7 | 8 | class SchemaMacro212Spec extends AnyWordSpec { 9 | import SchemaMacro212Spec._ 10 | 11 | "generate schema for Sealed Trait subclasses defined inside of another object" in { 12 | import `object`.Field 13 | 14 | Json.schema[NestedFooBar] shouldEqual `oneof`( 15 | Set(`object`(Field("foo", `number`[Double])), `object`(Field("bar", `number`[Double]))) 16 | ) 17 | } 18 | } 19 | 20 | object SchemaMacro212Spec { 21 | 22 | sealed trait NestedFooBar 23 | object NestedFooBar { 24 | case class NestedFooBar1(foo: Double) extends NestedFooBar 25 | case class NestedFooBar2(bar: Double) extends NestedFooBar 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/src/test/scala-2.12/com/github/andyglow/jsonschema/model/UserProfile.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | case class UserProfile( 4 | firstName: String, 5 | middleName: Option[String], 6 | lastName: String, 7 | age: Int, 8 | enabledFeatures: Set[BetaFeature] = Set(F0, F1), 9 | active: Active = On, 10 | credentials: Credentials = Credentials("anonymous", "-"), 11 | role: Role = Role.User, 12 | lastLoginMs: Option[Long] = None, 13 | notes: Option[Notes] = Some(Notes("initial note", Nil)) 14 | ) 15 | -------------------------------------------------------------------------------- /api/src/test/scala-2.13/com/github/andyglow/jsonschema/NoneForDefaultValueSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json._ 4 | import json.Schema._ 5 | import json.Schema.`object`._ 6 | import org.scalatest.matchers.should.Matchers._ 7 | import org.scalatest.funsuite.AnyFunSuite 8 | import com.github.andyglow.jsonschema.{model => case0} 9 | 10 | object NoneForDefaultValueCases { 11 | 12 | object case1 { 13 | case class Foo(value: Option[String] = None) 14 | } 15 | } 16 | 17 | object case2 { 18 | case class Foo(value: Option[String] = None) 19 | } 20 | 21 | class NoneForDefaultValueSpec extends AnyFunSuite { 22 | import NoneForDefaultValueCases._ 23 | 24 | test("scala.None for default value") { 25 | 26 | // type id specified in external file 27 | Json.schema[case0.Foo] shouldBe `object`(Field("value", `string`, required = false)) 28 | 29 | // type is specified in same file 30 | Json.schema[case2.Foo] shouldBe `object`(Field("value", `string`, required = false)) 31 | 32 | // type is specified in same file, companion object 33 | Json.schema[case1.Foo] shouldBe `object`(Field("value", `string`, required = false)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /api/src/test/scala-2.13/com/github/andyglow/jsonschema/SchemaMacro213Spec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json.Json 4 | import json.Schema.{`number`, `object`, `oneof`} 5 | import org.scalatest.matchers.should.Matchers._ 6 | import org.scalatest.wordspec.AnyWordSpec 7 | 8 | class SchemaMacro213Spec extends AnyWordSpec { 9 | import SchemaMacro213Spec._ 10 | 11 | "generate schema for Sealed Trait subclasses defined inside of another object" in { 12 | import `object`.Field 13 | 14 | Json.schema[NestedFooBar] shouldEqual `oneof`(Set(`object`(Field("foo", `number`[Double])), `object`(Field("bar", `number`[Double])))) 15 | } 16 | } 17 | 18 | object SchemaMacro213Spec { 19 | 20 | sealed trait NestedFooBar 21 | object NestedFooBar { 22 | case class NestedFooBar1(foo: Double) extends NestedFooBar 23 | case class NestedFooBar2(bar: Double) extends NestedFooBar 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api/src/test/scala-2.13/com/github/andyglow/jsonschema/model/UserProfile.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | case class UserProfile( 4 | firstName: String, 5 | middleName: Option[String], 6 | lastName: String, 7 | age: Int, 8 | enabledFeatures: Set[BetaFeature] = Set(F0, F1), 9 | active: Active = On, 10 | credentials: Credentials = Credentials("anonymous", "-"), 11 | role: Role = Role.User, 12 | lastLoginMs: Option[Long] = None, 13 | notes: Option[Notes] = Some(Notes("initial note", Nil)) 14 | ) 15 | -------------------------------------------------------------------------------- /api/src/test/scala/com/example/unrelated/CommentsMain.scala: -------------------------------------------------------------------------------- 1 | package com.example.unrelated 2 | 3 | import json.{Json, Schema} 4 | import json.schema.Version.Draft07 5 | 6 | object CommentsMain { 7 | 8 | private def printSchema[T](s: Schema[T]): Unit = { 9 | val str = Json.stringify(s, Draft07("foo-id")) 10 | println(str) 11 | } 12 | 13 | def main(args: Array[String]): Unit = { 14 | val s0 = json.Json.objectSchema[models.Foo]("c" -> "C, C, ccc", "b" -> "bcd") 15 | 16 | printSchema(s0) 17 | 18 | val s1 = json.Json.objectSchema[models.Bar]() 19 | 20 | printSchema(s1) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /api/src/test/scala/com/example/unrelated/SchemaMacroNamesSpec.scala: -------------------------------------------------------------------------------- 1 | package com.example.unrelated 2 | 3 | import org.scalatest.matchers.should.Matchers._ 4 | import org.scalatest.wordspec.AnyWordSpec 5 | 6 | /** This spec checks that the macro works even when names from `com.github.andyglow.jsonschema` are not in scope. 7 | */ 8 | class SchemaMacroNamesSpec extends AnyWordSpec { 9 | 10 | "Schema" should { 11 | 12 | "generate a schema with a reference to another schema" in { 13 | """import json._ 14 | |import SchemaNamesSpec._ 15 | | 16 | |implicit val fooSchema: Schema[Foo] = Json.schema[Foo] 17 | |implicit val barSchema: Schema[Bar] = Json.schema[Bar] 18 | """.stripMargin should compile 19 | } 20 | } 21 | } 22 | 23 | object SchemaNamesSpec { 24 | final case class Foo(s: String) 25 | final case class Bar(foo: Foo) 26 | } 27 | -------------------------------------------------------------------------------- /api/src/test/scala/com/example/unrelated/models.scala: -------------------------------------------------------------------------------- 1 | package com.example.unrelated 2 | 3 | import json.schema._ 4 | 5 | object models { 6 | 7 | /** My perfect class 8 | * 9 | * @param a 10 | * A Param 11 | * @param b 12 | * B Param 13 | */ 14 | case class Foo( 15 | a: String, 16 | b: Int, 17 | c: Boolean, 18 | @description("some description") d: Map[String, String] 19 | ) 20 | 21 | @title("A Bar Title") 22 | @description("this is Bar. description") 23 | case class Bar( 24 | @description("aaa") a: String, 25 | @description("bbb") b: Int, 26 | @description("ccc") c: Boolean, 27 | @description("ddd") d: Map[String, String] 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/AsDraft06Spec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import json.Schema._ 5 | import json.schema.Version.{Draft06, Draft09} 6 | import JsonMatchers._ 7 | import org.scalatest.matchers.should.Matchers._ 8 | import org.scalatest.wordspec.AnyWordSpec 9 | 10 | class AsDraft06Spec extends AnyWordSpec { 11 | 12 | "AsValue.schema" should { 13 | 14 | "emit const" in { 15 | 16 | val a = `oneof`.of( 17 | `const`("foo").withTitle("Foo").withDescription("f-o-o"), 18 | `const`("bar").withTitle("Bar").withDescription("b-a-r") 19 | ) 20 | 21 | val e = obj( 22 | f"$$schema" -> "https://json-schema.org/draft/2019-09/schema", 23 | f"$$id" -> "http://example.com/foobarbaz.json", 24 | "oneOf" -> arr( 25 | obj("const" -> "foo", "title" -> "Foo", "description" -> "f-o-o"), 26 | obj("const" -> "bar", "title" -> "Bar", "description" -> "b-a-r") 27 | ) 28 | ) 29 | 30 | AsValue.schema( 31 | a, 32 | Draft09(id = "http://example.com/foobarbaz.json") 33 | ) should beStructurallyEqualTo(e) 34 | } 35 | 36 | "emit Object" in { 37 | import `object`.Field 38 | 39 | val o = `object`( 40 | Field("foo", `string`[String]), 41 | Field("bar", `integer`, required = false), 42 | Field("baz", `def`[Boolean]("my-bool", `boolean`)) 43 | ) 44 | 45 | AsValue.schema( 46 | o, 47 | Draft06(id = "http://example.com/foobarbaz.json") 48 | ) should beStructurallyEqualTo( 49 | obj( 50 | f"$$schema" -> "http://json-schema.org/draft-06/schema#", 51 | f"$$id" -> "http://example.com/foobarbaz.json", 52 | "type" -> "object", 53 | "additionalProperties" -> false, 54 | "required" -> arr("foo", "baz"), 55 | "properties" -> obj( 56 | "foo" -> obj("type" -> "string"), 57 | "bar" -> obj("type" -> "integer"), 58 | "baz" -> obj(f"$$ref" -> "#my-bool") 59 | ), 60 | "definitions" -> obj("my-bool" -> obj(f"$$id" -> "#my-bool", "type" -> "boolean")) 61 | ) 62 | ) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/AsDraft09Spec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import com.github.andyglow.jsonschema.JsonMatchers._ 5 | import json.Schema._ 6 | import json.schema.Version._ 7 | import org.scalatest.matchers.should.Matchers._ 8 | import org.scalatest.wordspec.AnyWordSpec 9 | 10 | class AsDraft09Spec extends AnyWordSpec { 11 | 12 | "AsValue.schema" should { 13 | 14 | "emit const" in { 15 | 16 | val a = `oneof`.of( 17 | `const`("foo").withTitle("Foo").withDescription("f-o-o"), 18 | `const`("bar").withTitle("Bar").withDescription("b-a-r") 19 | ) 20 | 21 | val e = obj( 22 | f"$$schema" -> "https://json-schema.org/draft/2019-09/schema", 23 | f"$$id" -> "http://example.com/foobarbaz.json", 24 | "oneOf" -> arr( 25 | obj("const" -> "foo", "title" -> "Foo", "description" -> "f-o-o"), 26 | obj("const" -> "bar", "title" -> "Bar", "description" -> "b-a-r") 27 | ) 28 | ) 29 | 30 | AsValue.schema( 31 | a, 32 | Draft09(id = "http://example.com/foobarbaz.json") 33 | ) should beStructurallyEqualTo(e) 34 | } 35 | 36 | "emit Object" in { 37 | import `object`.Field 38 | 39 | val a = `object`( 40 | Field("foo", `string`[String]), 41 | Field("uuid", `string`(`string`.Format.`uuid`)), 42 | Field("bar", `integer`, required = false), 43 | Field("baz", `def`[Boolean]("my-bool", `boolean`)) 44 | ) 45 | 46 | val e = obj( 47 | f"$$schema" -> "https://json-schema.org/draft/2019-09/schema", 48 | f"$$id" -> "http://example.com/foobarbaz.json", 49 | "type" -> "object", 50 | "additionalProperties" -> false, 51 | "required" -> arr("foo", "baz", "uuid"), 52 | "properties" -> obj( 53 | "foo" -> obj("type" -> "string"), 54 | "uuid" -> obj("type" -> "string", "format" -> "uuid"), 55 | "bar" -> obj("type" -> "integer"), 56 | "baz" -> obj(f"$$ref" -> "#my-bool") 57 | ), 58 | "$defs" -> obj("my-bool" -> obj(f"$$anchor" -> "my-bool", "type" -> "boolean")) 59 | ) 60 | 61 | AsValue.schema( 62 | a, 63 | Draft09(id = "http://example.com/foobarbaz.json") 64 | ) should beStructurallyEqualTo(e) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/AsDraft12Spec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import com.github.andyglow.jsonschema.JsonMatchers._ 5 | import json.Schema._ 6 | import json.schema.Version._ 7 | import org.scalatest.matchers.should.Matchers._ 8 | import org.scalatest.wordspec.AnyWordSpec 9 | 10 | class AsDraft12Spec extends AnyWordSpec { 11 | 12 | "AsValue.schema" should { 13 | 14 | "emit const" in { 15 | 16 | val a = `oneof`.of( 17 | `const`("foo").withTitle("Foo").withDescription("f-o-o"), 18 | `const`("bar").withTitle("Bar").withDescription("b-a-r") 19 | ) 20 | 21 | val e = obj( 22 | f"$$schema" -> "https://json-schema.org/draft/2020-12/schema", 23 | f"$$id" -> "http://example.com/foobarbaz.json", 24 | "oneOf" -> arr( 25 | obj("const" -> "foo", "title" -> "Foo", "description" -> "f-o-o"), 26 | obj("const" -> "bar", "title" -> "Bar", "description" -> "b-a-r") 27 | ) 28 | ) 29 | 30 | AsValue.schema( 31 | a, 32 | Draft12(id = "http://example.com/foobarbaz.json") 33 | ) should beStructurallyEqualTo(e) 34 | } 35 | 36 | "emit Object" in { 37 | import `object`.Field 38 | 39 | val a = `object`( 40 | Field("foo", `string`[String]), 41 | Field("uuid", `string`(`string`.Format.`uuid`)), 42 | Field("bar", `integer`, required = false), 43 | Field("baz", `def`[Boolean]("my-bool", `boolean`)) 44 | ) 45 | 46 | val e = obj( 47 | f"$$schema" -> "https://json-schema.org/draft/2020-12/schema", 48 | f"$$id" -> "http://example.com/foobarbaz.json", 49 | "type" -> "object", 50 | "additionalProperties" -> false, 51 | "required" -> arr("foo", "baz", "uuid"), 52 | "properties" -> obj( 53 | "foo" -> obj("type" -> "string"), 54 | "uuid" -> obj("type" -> "string", "format" -> "uuid"), 55 | "bar" -> obj("type" -> "integer"), 56 | "baz" -> obj(f"$$ref" -> "#my-bool") 57 | ), 58 | "$defs" -> obj("my-bool" -> obj(f"$$anchor" -> "my-bool", "type" -> "boolean")) 59 | ) 60 | 61 | AsValue.schema( 62 | a, 63 | Draft12(id = "http://example.com/foobarbaz.json") 64 | ) should beStructurallyEqualTo(e) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/CRLFSourceSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json._ 4 | import json.schema.validation.Instance._ 5 | import json.Schema._ 6 | import json.Schema.`object`.Field 7 | 8 | import org.scalatest.matchers.should.Matchers._ 9 | import org.scalatest.funsuite._ 10 | 11 | class CRLFSourceSpec extends AnyFunSuite { 12 | import CRLFSourceSpec._ 13 | 14 | test("support windows CRLF") { 15 | CC.ccSchema shouldEqual `object`( 16 | Field("set", `array`(`integer`, unique = true), required = false, default = Set(1, 5, 9)), 17 | Field("list", `array`(`boolean`), required = false, default = List(true, false)), 18 | Field("vector", `array`(`number`[Long]), required = false, default = Vector(9, 7)), 19 | Field( 20 | "strMap", 21 | `dictionary`(`number`[Double]), 22 | required = false, 23 | default = Map("foo" -> .12) 24 | ), 25 | Field( 26 | "intMap", 27 | `dictionary`[Int, String, Map](`string`[String]) 28 | .withValidation(`patternProperties` := "^[0-9]+$"), 29 | required = false, 30 | default = Map(1 -> "1", 2 -> "2") 31 | ) 32 | ) 33 | } 34 | 35 | } 36 | 37 | object CRLFSourceSpec { 38 | 39 | case class CC( 40 | set: Set[Int] = Set(1, 5, 9), 41 | list: List[Boolean] = List(true, false), 42 | vector: Vector[Long] = Vector(9L, 7L), 43 | strMap: Map[String, Double] = Map("foo" -> .12), 44 | intMap: Map[Int, String] = Map(1 -> "1", 2 -> "2") 45 | ) 46 | 47 | object CC { 48 | implicit val ccSchema: Schema[CC] = Json.schema[CC] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/Example.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import java.time.LocalDateTime 4 | import com.github.andyglow.json.JsonFormatter 5 | import json.Json 6 | import json.schema._ 7 | 8 | object Example { 9 | import ExampleJsonSchema._ 10 | 11 | def main(args: Array[String]): Unit = { 12 | println(JsonFormatter.format(AsValue.schema(personJsonType, Version.Draft04()))) 13 | } 14 | } 15 | 16 | object ExampleMsg { 17 | 18 | sealed trait Gender 19 | 20 | object Gender { 21 | 22 | @typeHint[String] @title("The Male") case object Male extends Gender 23 | 24 | /** The Female 25 | */ 26 | @typeHint[String] 27 | case object Female extends Gender 28 | } 29 | 30 | case class Company(name: String) 31 | 32 | case class Car(name: String, manufacturer: Company) 33 | 34 | /** Super Person 35 | * 36 | * @param firstName 37 | * First 38 | * @param middleName 39 | * Middle 40 | * @param lastName 41 | * Last 42 | * @param gender 43 | * Gender 44 | * @param birthDay 45 | * Birth dat 46 | * @param company 47 | * Company 48 | * @param cars 49 | * Cars owned 50 | * @param active 51 | * Is Active or not 52 | */ 53 | case class Person( 54 | firstName: String, 55 | middleName: Option[String], 56 | lastName: String, 57 | gender: Gender, 58 | birthDay: LocalDateTime, 59 | company: Company, 60 | cars: Seq[Car], 61 | active: Boolean = true 62 | ) 63 | 64 | } 65 | 66 | object ExampleJsonSchema { 67 | import ExampleMsg._ 68 | 69 | implicit val jsonSchemaFlags: Flag with Flag.EnumsAsOneOf = null 70 | 71 | implicit val genderJsonType: json.Schema[Gender] = Json.schema[Gender] 72 | 73 | implicit val companyJsonType: json.Schema[Company] = Json.schema[Company] 74 | 75 | implicit val carJsonType: json.Schema[Car] = Json.schema[Car] 76 | 77 | val personJsonType: json.Schema[Person] = Json.schema[Person] 78 | } 79 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/GenericSchemaMacroSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import java.time.LocalDateTime 4 | 5 | import json.Json 6 | import json.Schema._ 7 | import json.Schema.`object`.Field 8 | import org.scalatest.matchers.should.Matchers._ 9 | import org.scalatest.funsuite.AnyFunSuite 10 | 11 | class GenericSchemaMacroSpec extends AnyFunSuite { 12 | 13 | test("generic case class") { 14 | Json.schema[GenericCC[Int]] shouldEqual `object`( 15 | Field("head", `integer`), 16 | Field("tail", `array`(`integer`)) 17 | ) 18 | } 19 | 20 | test("generic case class with generic nesting") { 21 | import `string`.Format._ 22 | 23 | Json.schema[DoubleGenericCC[LocalDateTime]] shouldEqual `object`( 24 | Field("str", `string`), 25 | Field( 26 | "gen", 27 | `object`( 28 | Field("head", `string`(`date-time`)), 29 | Field("tail", `array`(`string`(`date-time`))) 30 | ) 31 | ) 32 | ) 33 | } 34 | 35 | test("generic case class with generic nesting and defaults") { 36 | Json.schema[DoubleGenericCCWithDefaults[Long]] shouldEqual `object`( 37 | Field("str", `string`, required = false, "abc"), 38 | Field("gen", `object`(Field("head", `number`[Long]), Field("tail", `array`(`number`[Long])))) 39 | ) 40 | } 41 | 42 | test("generic case class with generic nesting and defaults defined externally") { 43 | Json.schema[DoubleGenericCCWithPredefinedDefaults[Long]] shouldEqual `object`( 44 | Field("str", `string`, required = false, "some-str"), 45 | Field("gen", `object`(Field("head", `number`[Long]), Field("tail", `array`(`number`[Long])))) 46 | ) 47 | } 48 | } 49 | 50 | case class GenericCC[T](head: T, tail: List[T]) 51 | 52 | case class DoubleGenericCC[T](str: String, gen: GenericCC[T]) 53 | 54 | case class DoubleGenericCCWithDefaults[T](str: String = "abc", gen: GenericCC[T]) 55 | 56 | case class DoubleGenericCCWithPredefinedDefaults[T]( 57 | str: String = GenericSchemaMacroSpec.defaultStr1 + "-" + GenericSchemaMacroSpec.defaultStr2, 58 | gen: GenericCC[T] 59 | ) 60 | 61 | object GenericSchemaMacroSpec { 62 | 63 | val defaultStr1 = "some" 64 | val defaultStr2 = "str" 65 | } 66 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/JsonMatchers.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.comparison.Result 4 | import com.github.andyglow.json.{JsonFormatter, Value} 5 | import org.scalatest.matchers.{MatchResult, Matcher} 6 | 7 | trait JsonMatchers { 8 | 9 | class Contain(right: Value.obj) extends Matcher[Value.obj] { 10 | 11 | override def apply(left: Value.obj): MatchResult = { 12 | MatchResult( 13 | left.contains(right), 14 | s"${JsonFormatter format left} doesn't contain ${JsonFormatter format right}", 15 | s"${JsonFormatter format left} contains ${JsonFormatter format right}" 16 | ) 17 | } 18 | } 19 | 20 | class BeStructurallyEqual(right: Value) extends Matcher[Value] { 21 | 22 | override def apply(left: Value): MatchResult = { 23 | left.diff(right) match { 24 | case Result.Equal => 25 | MatchResult( 26 | matches = true, 27 | "", 28 | s"```\n${JsonFormatter format left}\n```\nis structurally equal to\n```\n${JsonFormatter format right}\n```" 29 | ) 30 | case Result.Different(diffs) => 31 | MatchResult( 32 | matches = false, 33 | s"```\n${JsonFormatter format left}\n```\nis not structurally equal to\n```\n${JsonFormatter format right}\n```\nDifferences:\n${diffs 34 | .map(_.str) 35 | .mkString("- ", "\n- ", "")}", 36 | "" 37 | ) 38 | } 39 | } 40 | } 41 | 42 | def containJson(right: Value.obj) = new Contain(right) 43 | 44 | def beStructurallyEqualTo(right: Value) = new BeStructurallyEqual(right) 45 | } 46 | 47 | object JsonMatchers extends JsonMatchers 48 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/RWSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import com.github.andyglow.jsonschema.JsonMatchers._ 5 | import json.Schema._ 6 | import json.Schema.`object`.{Field => F} 7 | import json.schema.{Version => v, _} 8 | import json.Json 9 | import org.scalatest.matchers.should.Matchers._ 10 | import org.scalatest.wordspec._ 11 | 12 | // moved on top because of 13 | // - knownDirectSubclasses of Pet observed before subclass Cat registered 14 | object RWSpec { 15 | 16 | case class Foo(@readOnly id: String, name: String, @writeOnly password: String) 17 | 18 | } 19 | 20 | class RWSpec extends AnyWordSpec { 21 | import RWSpec._ 22 | 23 | "readOnly and writeOnly" should { 24 | 25 | "be exposed" in { 26 | 27 | val s = Json.schema[Foo] 28 | s shouldBe `object`( 29 | F("id", `string`, required = true).setReadOnly, 30 | F("name", `string`, required = true), 31 | F("password", `string`, required = true).setWriteOnly 32 | ) 33 | 34 | AsValue.schema(s, v.Raw) should containJson { 35 | obj( 36 | "type" -> "object", 37 | "additionalProperties" -> false, 38 | "required" -> arr("id", "name", "password"), 39 | "properties" -> obj( 40 | "id" -> obj("type" -> "string", "readOnly" -> true), 41 | "name" -> obj("type" -> "string"), 42 | "password" -> obj("type" -> "string", "writeOnly" -> true) 43 | ) 44 | ) 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/SchemaMatchers.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.jsonschema.SchemaEquality.{Equal, UnEqual} 4 | import json.Schema 5 | import org.scalatest.enablers.Sequencing 6 | import org.scalatest.matchers.{MatchResult, Matcher} 7 | 8 | class SchemaMatchers(right: Schema[_], checkOrder: Boolean) extends Matcher[Schema[_]] { 9 | 10 | override def apply(left: Schema[_]): MatchResult = { 11 | import Schema._, `object`.Field 12 | 13 | val eq = SchemaEquality(checkOrder).compute(left, right) 14 | val explanation = eq match { 15 | case Equal => "" 16 | case UnEqual(diff) => " due to " + diff.toDebugString 17 | } 18 | 19 | MatchResult( 20 | eq == Equal, 21 | left.toDebugString + " was not equal to " + right.toDebugString + explanation, 22 | left.toDebugString + " was equal to " + right.toDebugString 23 | ) 24 | } 25 | } 26 | 27 | object SchemaMatchers { 28 | 29 | def matchSchema(x: Schema[_]) = new SchemaMatchers(x, checkOrder = true) 30 | 31 | def unorderedMatchSchema(x: Schema[_]) = new SchemaMatchers(x, checkOrder = false) 32 | } 33 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/SumTypeModels.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json.schema.{description, title} 4 | 5 | object SumTypeModels { 6 | 7 | sealed trait FooBarInsideCompanionWithAnnotations extends Any 8 | object FooBarInsideCompanionWithAnnotations { 9 | @title("t1") 10 | @description("d1") 11 | case class M1(val1: Double) extends FooBarInsideCompanionWithAnnotations 12 | 13 | @title("t2") 14 | @description("d2") 15 | case class M2(val2: Double) extends FooBarInsideCompanionWithAnnotations 16 | 17 | @title("t3") 18 | @description("d3") 19 | case class M3(val3: String) extends AnyVal with FooBarInsideCompanionWithAnnotations 20 | 21 | @title("t4") 22 | @description("d4") 23 | case object M4 extends FooBarInsideCompanionWithAnnotations 24 | 25 | @title("t5") 26 | @description("d5") 27 | case object M5 extends FooBarInsideCompanionWithAnnotations 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/SumTypeWithConstantsSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json._ 4 | import json.Schema._ 5 | import json.schema._ 6 | import org.scalatest.matchers.should.Matchers._ 7 | import org.scalatest.wordspec._ 8 | 9 | class SumTypeWithConstantsSpec extends AnyWordSpec { 10 | import SumTypeModels._ 11 | 12 | "Macro" should { 13 | "generate schema for Sealed Trait subclasses defined inside of it's companion object and include sub-types annotations, enum-as-oneof" in { 14 | import `object`.Field 15 | 16 | implicit val flag: Flag with Flag.EnumsAsOneOf = null 17 | 18 | Json.schema[FooBarInsideCompanionWithAnnotations] shouldEqual `oneof`( 19 | Set( 20 | `object`(Field("val1", `number`[Double])).withTitle("t1").withDescription("d1"), 21 | `object`(Field("val2", `number`[Double])).withTitle("t2").withDescription("d2"), 22 | `value-class`(`string`).withTitle("t3").withDescription("d3"), 23 | `const`("M4").withTitle("t4").withDescription("d4"), 24 | `const`("M5").withTitle("t5").withDescription("d5") 25 | ) 26 | ) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/SumTypeWithEnumsSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json._ 4 | import json.Schema._ 5 | //import json.schema._ 6 | import org.scalatest.matchers.should.Matchers._ 7 | import org.scalatest.wordspec._ 8 | 9 | class SumTypeWithEnumsSpec extends AnyWordSpec { 10 | import SumTypeModels._ 11 | 12 | "Macro" should { 13 | "generate schema for Sealed Trait subclasses defined inside of it's companion object and include sub-types annotations" in { 14 | import `object`.Field 15 | 16 | Json.schema[FooBarInsideCompanionWithAnnotations] shouldEqual `oneof`( 17 | Set( 18 | `object`(Field("val1", `number`[Double])).withTitle("t1").withDescription("d1"), 19 | `object`(Field("val2", `number`[Double])).withTitle("t2").withDescription("d2"), 20 | `value-class`(`string`).withTitle("t3").withDescription("d3"), 21 | `enum`.of("M4", "M5") 22 | ) 23 | ) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/TypeSignatureSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json.Json 4 | import org.scalatest.matchers.should.Matchers._ 5 | import org.scalatest.wordspec.AnyWordSpec 6 | 7 | class TypeSignatureSpec extends AnyWordSpec { 8 | import TypeSignatureSpec._ 9 | 10 | "Json.sig" should { 11 | "return proper type signatures" in { 12 | 13 | Json.sig[String].signature shouldEqual "scala.Predef.String" 14 | 15 | Json.sig[Int].signature shouldEqual "scala.Int" 16 | 17 | Json.sig[Int].signature shouldEqual "scala.Int" 18 | 19 | Json.sig[Z].signature shouldEqual "com.github.andyglow.jsonschema.TypeSignatureSpec.Z" 20 | 21 | Json.sig[Y.type].signature shouldEqual "com.github.andyglow.jsonschema.TypeSignatureSpec.Y" 22 | 23 | Json 24 | .sig[Map[String, String]] 25 | .signature shouldEqual "scala.Predef.Map[scala.Predef.String,scala.Predef.String]" 26 | 27 | Json 28 | .sig[X[BigDecimal]] 29 | .signature shouldEqual "com.github.andyglow.jsonschema.TypeSignatureSpec.X[scala.BigDecimal]" 30 | 31 | Json.sig[Value].signature shouldEqual "com.github.andyglow.jsonschema.TypeSignatureSpec.Value" 32 | } 33 | } 34 | } 35 | 36 | object TypeSignatureSpec { 37 | 38 | case class Z() 39 | 40 | case object Y 41 | 42 | case class X[T](foo: T) 43 | 44 | case class Value(x: String) extends AnyVal 45 | } 46 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/model/Active.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | import com.github.andyglow.json.{ToValue, Value} 4 | import json.schema.typeHint 5 | 6 | // members goes without companion 7 | 8 | @typeHint[String] 9 | sealed trait Active 10 | 11 | case object On extends Active 12 | case object Off extends Active 13 | case object Suspended extends Active 14 | 15 | object Active { 16 | 17 | // ToValue is explicitly specified 18 | implicit val ActiveV: ToValue[Active] = ToValue mk { 19 | case On => Value.str("On") 20 | case Off => Value.str("Off") 21 | case Suspended => Value.str("Suspended") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/model/BetaFeature.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | import json.schema.typeHint 4 | 5 | @typeHint[String] 6 | sealed trait BetaFeature 7 | 8 | case object F0 extends BetaFeature 9 | case object F1 extends BetaFeature 10 | case object F2 extends BetaFeature 11 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/model/Credentials.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | case class Credentials(login: String, password: String) 4 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/model/Foo.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | case class Foo(value: Option[String] = None) 4 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/model/Notes.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | case class Notes(head: String, tail: List[String]) 4 | -------------------------------------------------------------------------------- /api/src/test/scala/com/github/andyglow/jsonschema/model/Role.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.model 2 | 3 | import json.schema.typeHint 4 | 5 | // members goes inside companion 6 | 7 | @typeHint[String] 8 | sealed trait Role 9 | 10 | object Role { 11 | 12 | case object User extends Role 13 | 14 | case object Admin extends Role 15 | 16 | case object Manager extends Role 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/scala-2.11/com/github/andyglow/json/LowPriorityArrayImplicits.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | trait LowPriorityArrayImplicits { this: LowPriorityPrimitiveImplicits => 4 | import Value._ 5 | import ToValue._ 6 | 7 | implicit def ArrV[T, C[_] <: Traversable[_]](implicit to: ToValue[T]): ToValue[C[T]] = { 8 | 9 | mk { items => 10 | val v = items map { v: Any => to(v.asInstanceOf[T]) } 11 | arr(v.toSeq) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala-2.12/com/github/andyglow/json/LowPriorityArrayImplicits.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | trait LowPriorityArrayImplicits { this: LowPriorityPrimitiveImplicits => 4 | import Value._ 5 | import ToValue._ 6 | 7 | implicit def ArrV[T, C[_] <: Traversable[_]](implicit to: ToValue[T]): ToValue[C[T]] = { 8 | 9 | mk { items => 10 | val v = items map { v: Any => to(v.asInstanceOf[T]) } 11 | arr(v.toSeq) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala-2.12/com/github/andyglow/scalamigration.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow 2 | 3 | import scala.collection.immutable 4 | import scala.collection.mutable.ListBuffer 5 | import scala.util.{Failure, Success, Try} 6 | import scala.util.control.NonFatal 7 | 8 | object scalamigration { 9 | 10 | implicit class collectionMapOps[K, V](private val kv: scala.collection.Map[K, V]) extends AnyVal { 11 | @inline def immutableKeys: immutable.Set[K] = kv.keySet.toSet 12 | } 13 | 14 | implicit class SpecificStringOps(private val x: String) extends AnyVal { 15 | 16 | // in order to not clash with different versions of scala as well as jdk 17 | @inline def asLines: Iterator[String] = x.linesWithSeparators map { _.stripLineEnd } 18 | } 19 | 20 | implicit class EitherOps[L, R](private val x: Either[L, R]) extends AnyVal { 21 | 22 | @inline def opt: Option[R] = x.toOption 23 | } 24 | 25 | implicit class MapMigOps[K, V](private val x: Map[K, V]) extends AnyVal { 26 | 27 | @inline def mapV[V2](f: V => V2): Map[K, V2] = 28 | x mapValues f 29 | 30 | def updatedWith(k: K)(op: Option[V] => Option[V]): Map[K, V] = { 31 | op(x get k) match { 32 | case Some(v) => x.updated(k, v) 33 | case None => x - k 34 | } 35 | } 36 | } 37 | 38 | implicit class TryMigOps[+T](private val e: Try[T]) extends AnyVal { 39 | 40 | def fold[U](fa: Throwable => U, fb: T => U): U = e match { 41 | case Success(b) => fb(b) 42 | case Failure(a) => fa(a) 43 | } 44 | 45 | def collect[U](pf: PartialFunction[T, U]): Try[U] = e match { 46 | 47 | case Success(b) => 48 | try { 49 | if (pf isDefinedAt b) Success(pf(b)) 50 | else Failure(new NoSuchElementException("Predicate does not hold for " + b)) 51 | } catch { 52 | case NonFatal(err) => Failure(err) 53 | } 54 | 55 | case Failure(_) => 56 | e.asInstanceOf[Try[U]] 57 | } 58 | 59 | def find[U](pf: PartialFunction[T, U]): Try[U] = e collect pf 60 | } 61 | 62 | implicit class ListBufferCompanionMigOps(private val lb: ListBuffer.type) extends AnyVal { 63 | 64 | @inline def from[T](elements: TraversableOnce[T]): ListBuffer[T] = 65 | lb.apply[T](elements.toSeq: _*) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core/src/main/scala-2.13/com/github/andyglow/json/LowPriorityArrayImplicits.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | trait LowPriorityArrayImplicits { this: LowPriorityPrimitiveImplicits => 4 | import Value._ 5 | import ToValue._ 6 | 7 | implicit def ArrV[T, C[_] <: Iterable[_]](implicit to: ToValue[T]): ToValue[C[T]] = { 8 | 9 | mk { items => 10 | val v = items map { v: Any => to(v.asInstanceOf[T]) } 11 | arr(v.toSeq) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala-2.13/com/github/andyglow/scalamigration.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow 2 | 3 | import scala.collection.immutable 4 | import scala.util.Try 5 | 6 | object scalamigration { 7 | 8 | implicit class collectionMapOps[K, V](private val kv: scala.collection.Map[K, V]) extends AnyVal { 9 | @inline def immutableKeys: immutable.Set[K] = kv.keySet.to(immutable.Set) 10 | } 11 | 12 | implicit class SpecificStringOps(private val x: String) extends AnyVal { 13 | 14 | // in order to not clash with different versions of scala as well as jdk 15 | @inline def asLines: Iterator[String] = x.linesWithSeparators map { _.stripLineEnd } 16 | } 17 | 18 | implicit class EitherOps[L, R](private val x: Either[L, R]) extends AnyVal { 19 | 20 | @inline def opt: Option[R] = x.toOption 21 | } 22 | 23 | implicit class MapMigOps[K, V](private val x: Map[K, V]) extends AnyVal { 24 | 25 | @inline def mapV[V2](f: V => V2): Map[K, V2] = 26 | x.view.mapValues(f).toMap 27 | } 28 | 29 | implicit class TryMigOps[+T](private val e: Try[T]) extends AnyVal { 30 | 31 | def find[U](pf: PartialFunction[T, U]): Try[U] = e collect pf 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/scala-3/com/github/andyglow/json/LowPriorityArrayImplicits.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | trait LowPriorityArrayImplicits { this: LowPriorityPrimitiveImplicits => 4 | import Value._ 5 | import ToValue._ 6 | 7 | implicit def ArrV[T, C[_] <: Iterable[_]](implicit to: ToValue[T]): ToValue[C[T]] = { 8 | 9 | mk { items => 10 | val v = items map { (v: Any) => to(v.asInstanceOf[T]) } 11 | arr(v.toSeq) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala-3/com/github/andyglow/scalamigration.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow 2 | 3 | import scala.util.Try 4 | 5 | object scalamigration { 6 | 7 | implicit class SpecificStringOps(private val x: String) extends AnyVal { 8 | 9 | // in order to not clash with different versions of scala as well as jdk 10 | @inline def asLines: Iterator[String] = x.linesWithSeparators map { _.stripLineEnd } 11 | } 12 | 13 | implicit class EitherOps[L, R](private val x: Either[L, R]) extends AnyVal { 14 | 15 | @inline def opt: Option[R] = x.toOption 16 | } 17 | 18 | implicit class MapMigOps[K, V](private val x: Map[K, V]) extends AnyVal { 19 | 20 | @inline def mapV[V2](f: V => V2): Map[K, V2] = 21 | x.view.mapValues(f).toMap 22 | } 23 | 24 | implicit class TryMigOps[+T](private val e: Try[T]) extends AnyVal { 25 | 26 | def find[U](pf: PartialFunction[T, U]): Try[U] = e collect pf 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/json/Escaped.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | object Escaped { 4 | 5 | def apply(x: String): String = { 6 | val sb = new StringBuilder 7 | for { ch <- x } { 8 | val chx = ch.toInt 9 | require(chx != 0) 10 | ch match { 11 | case '\n' => sb append "\\n" 12 | case '\t' => sb append "\\t" 13 | case '\r' => sb append "\\r" 14 | case '\b' => sb append "\\b" 15 | case '\f' => sb append "\\f" 16 | case '\\' => sb append "\\\\" 17 | case '"' => sb append "\\\"" 18 | case _ => 19 | if (chx >= 0x10000) throw new IllegalArgumentException() 20 | val c = if (chx > 127) "\\u%04x".format(chx) else ch.toString 21 | sb append c 22 | } 23 | } 24 | sb.toString() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/json/JsonFormatter.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | import com.github.andyglow.json.Value._ 4 | 5 | class JsonFormatter(step: Int = 2, sorted: Boolean = false) { 6 | import JsonFormatter.NL 7 | 8 | def format(value: Value): String = { 9 | val sb = new StringBuilder 10 | _format(value, sb) 11 | 12 | sb.toString() 13 | } 14 | 15 | private def fields(x: obj): scala.collection.Seq[(String, Value)] = 16 | if (sorted) x.fields.sortBy(_._1) else x.fields 17 | 18 | private def elements(x: arr): scala.collection.Seq[Value] = 19 | if (sorted) x.value.sorted else x.value 20 | 21 | private def _format(value: Value, sb: StringBuilder, indent: Int = 0): Unit = { 22 | val i0 = " " * indent 23 | val i1 = " " * (indent + step) 24 | 25 | value match { 26 | case o: obj => 27 | sb append "{" 28 | sb append NL 29 | var first = true 30 | 31 | for { (name, value) <- fields(o) } { 32 | if (!first) { 33 | sb append "," 34 | sb append NL 35 | } else { 36 | first = false 37 | } 38 | 39 | sb append i1 40 | sb append s""""${Escaped(name)}": """ 41 | _format(value, sb, indent + step) 42 | } 43 | 44 | sb append NL 45 | sb append i0 46 | sb append "}" 47 | 48 | case a: arr => 49 | sb append "[" 50 | sb append NL 51 | var first = true 52 | 53 | for { value <- elements(a) } { 54 | if (!first) { 55 | sb append "," 56 | sb append NL 57 | } else { 58 | first = false 59 | } 60 | 61 | sb append i1 62 | _format(value, sb, indent + step) 63 | } 64 | 65 | sb append NL 66 | sb append i0 67 | sb append "]" 68 | 69 | case str(x) => sb append s""""${Escaped(x)}"""" 70 | case _ => sb append value.toString 71 | } 72 | 73 | () 74 | } 75 | } 76 | 77 | object JsonFormatter { 78 | val NL: String = System.lineSeparator() 79 | 80 | private lazy val plainFmt = new JsonFormatter() 81 | private lazy val sortedFmt = new JsonFormatter(sorted = true) 82 | 83 | def format(value: Value, sorted: Boolean = false): String = { 84 | def fmt = if (sorted) sortedFmt else plainFmt 85 | fmt.format(value) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/json/ToValue.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | trait ToValue[T] { 4 | 5 | def apply(x: T): Value 6 | } 7 | 8 | trait LowPriorityPrimitiveImplicits { 9 | import Value._ 10 | import ToValue._ 11 | 12 | implicit val BoolV: ToValue[Boolean] = mk(bool.apply) 13 | implicit val IntV: ToValue[Int] = mk(num.apply) 14 | implicit val LongV: ToValue[Long] = mk(num.apply) 15 | implicit val ShortV: ToValue[Short] = mk(num.apply) 16 | implicit val FloatV: ToValue[Float] = mk(num.apply) 17 | implicit val DoubleV: ToValue[Double] = mk(num.apply) 18 | implicit val BigDecimalV: ToValue[BigDecimal] = mk(num.apply) 19 | implicit val BigIntV: ToValue[BigInt] = mk(num.apply) 20 | implicit val NumberV: ToValue[Number] = mk(num.apply) 21 | implicit val StringV: ToValue[String] = mk(str.apply) 22 | implicit val NullV: ToValue[Null] = mk(_ => `null`) 23 | 24 | // NOTICE: there are no way to work around objects (case classes) as long as different libraries 25 | // may generate different json representations out of the single case class instance 26 | } 27 | 28 | trait LowPriorityMapImplicits { this: LowPriorityPrimitiveImplicits => 29 | import Value._ 30 | import ToValue._ 31 | 32 | implicit def StrMapV[T](implicit to: ToValue[T]): ToValue[Map[String, T]] = { 33 | 34 | mk { items => 35 | val v = items map { case (k, v) => (k, to(v)) } 36 | obj(v.toMap) 37 | } 38 | } 39 | 40 | implicit def IntMapV[T](implicit to: ToValue[T]): ToValue[Map[Int, T]] = { 41 | 42 | mk { items => 43 | val v = items map { case (k, v) => (k.toString, to(v)) } 44 | obj(v.toMap) 45 | } 46 | } 47 | } 48 | 49 | trait LowPriorityProductImplicits { 50 | import Value._ 51 | import ToValue._ 52 | 53 | implicit def ProductV[T <: Product]: ToValue[T] = mk(p => str(p.productPrefix)) 54 | } 55 | trait LowPriorityImplicits extends LowPriorityPrimitiveImplicits with LowPriorityArrayImplicits with LowPriorityMapImplicits 56 | 57 | object ToValue extends LowPriorityImplicits { 58 | 59 | def mk[T](f: T => Value): ToValue[T] = new ToValue[T] { def apply(x: T): Value = f(x) } 60 | 61 | def apply[T](x: T)(implicit to: ToValue[T]): Value = to(x) 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/json/ValueSchema.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | import json.Schema 4 | import json.Schema._ 5 | 6 | sealed trait ValueSchema[T] { 7 | type S 8 | def schema: json.Schema[S] 9 | } 10 | 11 | object ValueSchema { 12 | 13 | implicit val schemaForStr: ValueSchema[Value.str] = new ValueSchema[Value.str] { 14 | override type S = String 15 | override def schema: Schema[S] = `string` 16 | } 17 | 18 | implicit val schemaForNum: ValueSchema[Value.num] = new ValueSchema[Value.num] { 19 | override type S = Long 20 | override def schema: Schema[S] = `number`[S] 21 | } 22 | 23 | implicit val schemaForBool: ValueSchema[Value.bool] = new ValueSchema[Value.bool] { 24 | override type S = Boolean 25 | override def schema: Schema[S] = `boolean` 26 | } 27 | 28 | implicit val schemaForString: ValueSchema[String] = new ValueSchema[String] { 29 | override type S = String 30 | override def schema: Schema[S] = `string` 31 | } 32 | 33 | implicit val schemaForInteger: ValueSchema[Int] = new ValueSchema[Int] { 34 | override type S = Int 35 | override def schema: Schema[S] = `integer` 36 | } 37 | 38 | implicit val schemaForLong: ValueSchema[Long] = new ValueSchema[Long] { 39 | override type S = Int 40 | override def schema: Schema[S] = `integer` 41 | } 42 | 43 | implicit val schemaForByte: ValueSchema[Byte] = new ValueSchema[Byte] { 44 | override type S = Int 45 | override def schema: Schema[S] = `integer` 46 | } 47 | 48 | implicit val schemaForShort: ValueSchema[Short] = new ValueSchema[Short] { 49 | override type S = Int 50 | override def schema: Schema[S] = `integer` 51 | } 52 | 53 | implicit def schemaForNumeric[T: Numeric]: ValueSchema[T] = new ValueSchema[T] { 54 | override type S = T 55 | override def schema: Schema[S] = `number`[S] 56 | } 57 | 58 | implicit val schemaForBoolean: ValueSchema[Boolean] = new ValueSchema[Boolean] { 59 | override type S = Boolean 60 | override def schema: Schema[S] = `boolean` 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsDraft04.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import json.schema.Version.Draft04 5 | 6 | class AsDraft04(val v: Draft04) extends AsValue with AsDraftSupport with Pre09 { 7 | 8 | def schema(x: json.Schema[_]): obj = { 9 | val base = obj(f"$$schema" -> v.uri) 10 | 11 | val definitions = inferDefinitions(x) 12 | 13 | base ++ apply(x) ++ { 14 | if (definitions.fields.nonEmpty) obj("definitions" -> definitions) else obj() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsDraft06.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import json.Schema._ 5 | import json.schema.Version._ 6 | 7 | class AsDraft06(val v: Draft06) extends AsValue with AsDraftSupport with Pre09 { 8 | 9 | def schema(x: json.Schema[_]): obj = { 10 | val base = obj(f"$$schema" -> v.uri, f"$$id" -> v.id) 11 | 12 | val definitions = inferDefinitions(x) 13 | 14 | base ++ apply(x) ++ { 15 | if (definitions.fields.nonEmpty) obj("definitions" -> definitions) else obj() 16 | } 17 | } 18 | 19 | override def buildRef(ref: String): String = s"#$ref" 20 | 21 | override def inferDefinition(x: `def`[_], par: ParentSchema): (String, obj) = { 22 | val ref = x.sig 23 | ref -> (obj(f"$$id" -> s"#$ref") ++ apply( 24 | x.tpe, 25 | par orElse Some(x), 26 | includeType = true, 27 | isRoot = false 28 | )) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsDraft07.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import json.Schema._ 5 | import json.schema.Version._ 6 | 7 | class AsDraft07(val v: Draft07) extends AsValue with AsDraftSupport with Pre09 { 8 | 9 | def schema(x: json.Schema[_]): obj = { 10 | val base = obj(f"$$schema" -> v.uri, f"$$id" -> v.id) 11 | 12 | val definitions = inferDefinitions(x) 13 | 14 | base ++ apply(x) ++ { 15 | if (definitions.fields.nonEmpty) obj("definitions" -> definitions) else obj() 16 | } 17 | } 18 | 19 | override def buildRef(ref: String): String = s"#$ref" 20 | 21 | override def inferDefinition(x: `def`[_], par: ParentSchema): (String, obj) = { 22 | val ref = x.sig 23 | ref -> (obj(f"$$id" -> s"#$ref") ++ apply( 24 | x.tpe, 25 | par orElse Some(x), 26 | includeType = true, 27 | isRoot = false 28 | )) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsDraft09.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import json.Schema._ 5 | import json.schema.Version._ 6 | 7 | class AsDraft09(val v: Draft09) extends AsValue with AsDraftSupport with Post09 { 8 | 9 | def schema(x: json.Schema[_]): obj = { 10 | val base = obj(f"$$schema" -> v.uri, f"$$id" -> v.id) 11 | 12 | val definitions = inferDefinitions(x) 13 | 14 | base ++ { 15 | if (definitions.fields.nonEmpty) obj("$defs" -> definitions) else obj() 16 | } ++ apply(x) 17 | } 18 | 19 | override def buildRef(ref: String): String = s"#$ref" 20 | 21 | override def inferDefinition(x: `def`[_], par: ParentSchema): (String, obj) = { 22 | val ref = x.sig 23 | ref -> (obj(f"$$anchor" -> ref) ++ apply( 24 | x.tpe, 25 | par orElse Some(x), 26 | includeType = true, 27 | isRoot = false 28 | )) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsDraft12.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import json.Schema._ 5 | import json.schema.Version._ 6 | 7 | class AsDraft12(val v: Draft12) extends AsValue with AsDraftSupport with Post09 { 8 | 9 | def schema(x: json.Schema[_]): obj = { 10 | val base = obj(f"$$schema" -> v.uri, f"$$id" -> v.id) 11 | 12 | val definitions = inferDefinitions(x) 13 | 14 | base ++ { 15 | if (definitions.fields.nonEmpty) obj("$defs" -> definitions) else obj() 16 | } ++ apply(x) 17 | } 18 | 19 | override def buildRef(ref: String): String = s"#$ref" 20 | 21 | override def inferDefinition(x: `def`[_], par: ParentSchema): (String, obj) = { 22 | val ref = x.sig 23 | ref -> (obj(f"$$anchor" -> ref) ++ apply( 24 | x.tpe, 25 | par orElse Some(x), 26 | includeType = true, 27 | isRoot = false 28 | )) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsRaw.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | 5 | object AsRaw extends AsValue with AsDraftSupport with Post09 { 6 | 7 | def schema(x: json.Schema[_]): obj = { 8 | val base = obj("description" -> x.description, "title" -> x.title) 9 | 10 | val definitions = inferDefinitions(x) 11 | 12 | base ++ apply(x) ++ { 13 | if (definitions.fields.nonEmpty) obj("definitions" -> definitions) else obj() 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsTree.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.tree.Tree 4 | import json.Schema 5 | import Schema._ 6 | import Tree._ 7 | import com.github.andyglow.json.Value 8 | 9 | object AsTree { 10 | 11 | def apply(schema: Schema[_]): Tree = schema match { 12 | case `boolean` => "boolean" 13 | case `integer` => "integer" 14 | case _: `number`[_] => "number" 15 | case `string`(fmt) => fmt.fold[Tree]("number")(fmt => Apply("string", Iterator("format" -> fmt.productPrefix))) 16 | case `array`(elementTpe, unique) => Apply("array", Map("component" -> AsTree(elementTpe), "unique" -> Lit(unique.toString))) 17 | case `dictionary`(valueTpe) => Apply("dictionary", Iterator("value" -> AsTree(valueTpe))) 18 | case `object`(fields) => 19 | Apply( 20 | "object", 21 | fields.map { f => 22 | Infix(f.name, "=", AsTree(f.tpe)) 23 | }.toIterator 24 | ) 25 | case `enum`(tpe, vals) => 26 | Apply( 27 | "enum", 28 | Map( 29 | "type" -> AsTree(tpe), 30 | "values" -> Lit(Value.arr(vals.toSeq).toString) 31 | ) 32 | ) 33 | case `oneof`(subTypes, discriminationField) => 34 | Apply( 35 | "oneof", 36 | Iterator( 37 | Apply("variants", subTypes.map(AsTree(_)).toIterator), 38 | "discriminatorField" -> discriminationField.getOrElse("empty") 39 | ) 40 | ) 41 | case `allof`(subTypes) => 42 | Apply( 43 | "ollof", 44 | Iterator( 45 | Apply("variants", subTypes.map(AsTree(_)).toIterator) 46 | ) 47 | ) 48 | case `not`(tpe) => Apply("not", Iterator(AsTree(tpe))) 49 | case `def`(sig, tpe) => Apply("def", Iterator("sig" -> sig, AsTree(tpe))) 50 | case `ref`(sig) => Apply("ref", Iterator(sig)) 51 | case `value-class`(tpe) => Apply("value-class", Iterator(AsTree(tpe))) 52 | case `const`(v) => Apply("const", Iterator(v.toString)) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsValue.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value._ 4 | import json.schema.Version 5 | 6 | trait AsValue { 7 | 8 | def schema(tpe: json.Schema[_]): obj 9 | 10 | def apply(schema: json.Schema[_]): obj 11 | } 12 | 13 | object AsValue { 14 | 15 | def schema[V <: Version](x: json.Schema[_], v: V)(implicit asValue: AsValueBuilder[V]): obj = 16 | asValue(v).schema(x) 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/AsValueBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json.schema.Version 4 | import json.schema.Version._ 5 | 6 | trait AsValueBuilder[V <: Version] { 7 | 8 | def apply(v: V): AsValue 9 | } 10 | 11 | trait LowPriorityAsValueBuilder { 12 | 13 | implicit val pseudo: AsValueBuilder[Raw.type] = new AsValueBuilder[Raw.type] { 14 | override def apply(v: Raw.type): AsValue = AsRaw 15 | } 16 | 17 | implicit val draft04: AsValueBuilder[Draft04] = new AsValueBuilder[Draft04] { 18 | override def apply(v: Draft04): AsValue = new AsDraft04(v) 19 | } 20 | 21 | implicit val draft06: AsValueBuilder[Draft06] = new AsValueBuilder[Draft06] { 22 | override def apply(v: Draft06): AsValue = new AsDraft06(v) 23 | } 24 | 25 | implicit val draft07: AsValueBuilder[Draft07] = new AsValueBuilder[Draft07] { 26 | override def apply(v: Draft07): AsValue = new AsDraft07(v) 27 | } 28 | 29 | implicit val draft09: AsValueBuilder[Draft09] = new AsValueBuilder[Draft09] { 30 | override def apply(v: Draft09): AsValue = new AsDraft09(v) 31 | } 32 | 33 | implicit val draft12: AsValueBuilder[Draft12] = new AsValueBuilder[Draft12] { 34 | override def apply(v: Draft12): AsValue = new AsDraft12(v) 35 | } 36 | } 37 | 38 | object AsValueBuilder extends LowPriorityAsValueBuilder 39 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/Constants.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | private[jsonschema] object Constants { 4 | 5 | object RegEx { 6 | 7 | val uuid = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/Post09.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json.Value.obj 4 | import json.Schema.`string` 5 | 6 | trait Post09 { this: AsDraftSupport => 7 | 8 | def mkStr(vl: ValidationList, x: `string`[_], par: ParentSchema): obj = 9 | obj("format" -> x.format.map(_.productPrefix)) 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/Pre09.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json.schema.{validation => V} 4 | import com.github.andyglow.json.Value.obj 5 | import json.Schema.`string` 6 | 7 | trait Pre09 { this: AsDraftSupport => 8 | 9 | /** Adds `format` attribute if Format is defined and it defines formats known prior to 2012-09. In case it's `uuid` and `pattern` validation is not defined manually, it will add pattern attribute. 10 | */ 11 | def mkStr(vl: ValidationList, x: `string`[_], par: ParentSchema): obj = { 12 | import `string`.Format._ 13 | 14 | val format = x.format.fold(obj.empty) { 15 | case `duration` => obj.empty 16 | case `uuid` => obj.empty 17 | case `idn-hostname` => obj.empty 18 | case f => obj("format" -> f.productPrefix) 19 | } 20 | 21 | val pattern = vl.find(_.validation == V.Instance.`pattern`) 22 | val extraVV = x.format flatMap { 23 | case `uuid` if pattern.isEmpty => Some(V.Instance.`pattern` := Constants.RegEx.uuid) 24 | case _ => None 25 | } 26 | 27 | format ++ extraVV.fold(obj.empty) { extraVV => 28 | obj(extraVV.validation.name -> extraVV.json) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/TypeSignature.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | case class TypeSignature[+T](signature: String) 4 | 5 | object TypeSignature { 6 | 7 | def unsafe(name: String): TypeSignature[_] = TypeSignature[Any](name) 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/jsonschema/package.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow 2 | 3 | import _root_.json.{Schema => S, schema => s} 4 | import s.{Version => v} 5 | 6 | package object jsonschema { 7 | 8 | implicit class SchemaOps[T](private val x: S[T]) extends AnyVal { 9 | 10 | def stringify: String = json.JsonFormatter.format(AsValue.schema(x, v.Raw)) 11 | 12 | def stringify[V <: s.Version: AsValueBuilder](v: V): String = 13 | json.JsonFormatter.format(AsValue.schema(x, v)) 14 | 15 | def draft04: String = stringify(v.Draft04()) 16 | 17 | def draft06(id: String): String = stringify(v.Draft06(id)) 18 | 19 | def draft07(id: String): String = stringify(v.Draft07(id)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/tree/Result.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.tree 2 | 3 | import scala.annotation.tailrec 4 | 5 | /** The intermediate return type of the pretty-print system: provides an iterator which produces the actual string output, as well as metadata around that output that is only available after the 6 | * iterator is exhausted 7 | */ 8 | class Result(val iter: Iterator[String], completedLineCount0: => Int, lastLineLength0: => Int) { 9 | lazy val completedLineCount = { 10 | require(iter.isEmpty) 11 | completedLineCount0 12 | } 13 | lazy val lastLineLength = { 14 | require(iter.isEmpty) 15 | lastLineLength0 16 | } 17 | def flatMap(f: (Int, Int) => Result): Result = { 18 | var newCompletedLineCount = 0 19 | var newLastLineLength = 0 20 | 21 | val mergedIterator = Util.concat( 22 | () => iter, 23 | () => { 24 | require(!iter.hasNext) 25 | val newResult = f(completedLineCount, lastLineLength0) 26 | newResult.iter.map { x => 27 | if (!newResult.iter.hasNext) { 28 | newCompletedLineCount = newResult.completedLineCount 29 | newLastLineLength = newResult.lastLineLength 30 | } 31 | x 32 | } 33 | } 34 | ) 35 | new Result( 36 | mergedIterator, 37 | newCompletedLineCount + completedLineCount, 38 | if (newCompletedLineCount > 0) newLastLineLength 39 | else newLastLineLength + lastLineLength 40 | ) 41 | 42 | } 43 | } 44 | 45 | object Result { 46 | def fromString(s: => String) = { 47 | lazy val lines = s.linesIterator.toArray 48 | new Result(Iterator(s), lines.length - 1, lines.last.length) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/scala/com/github/andyglow/tree/Tree.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.tree 2 | 3 | import scala.language.implicitConversions 4 | 5 | // this is borrowed from https://github.com/com-lihaoyi/PPrint 6 | sealed trait Tree { 7 | 8 | def rendered: String = { 9 | new Truncated( 10 | new Renderer(120, 2).rec(this, 0, 0).iter, 11 | 120, 12 | 500 13 | ).mkString 14 | } 15 | } 16 | 17 | object Tree { 18 | 19 | case class Apply(prefix: String, body: Iterator[Tree]) extends Tree 20 | case class Infix(lhs: Tree, op: String, rhs: Tree) extends Tree 21 | case class Lit(body: String) extends Tree { 22 | val hasNewLine = body.exists(c => c == '\n' || c == '\r') 23 | } 24 | case class KV(key: String, value: Tree) extends Tree 25 | case class Lazy(body0: Ctx => Iterator[String]) extends Tree 26 | case class Ctx(width: Int, leftOffset: Int, indentCount: Int, indentStep: Int) 27 | 28 | implicit def stringAsLit(x: String): Lit = Lit(x) 29 | implicit def pairAsKV[T](pair: (String, T))(implicit conv: T => Tree): KV = KV(pair._1, pair._2) 30 | implicit def mapAsKVIter(pairs: Map[String, Tree]): Iterator[KV] = pairs.map { case (k, v) => KV(k, v) }.toIterator 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/Flag.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | sealed trait Flag 4 | object Flag { 5 | 6 | trait EnumsAsOneOf extends Flag 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/Version.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | sealed trait Version { 4 | 5 | def uri: String 6 | } 7 | 8 | object Version { 9 | 10 | case object Raw extends Version { 11 | def uri = ??? 12 | } 13 | 14 | final case class Draft04() extends Version { 15 | 16 | val uri: String = "http://json-schema.org/draft-04/schema#" 17 | } 18 | 19 | final case class Draft06(id: String) extends Version { 20 | 21 | val uri: String = "http://json-schema.org/draft-06/schema#" 22 | } 23 | 24 | final case class Draft07(id: String) extends Version { 25 | 26 | val uri: String = "http://json-schema.org/draft-07/schema#" 27 | } 28 | 29 | final case class Draft09(id: String) extends Version { 30 | 31 | val uri: String = "https://json-schema.org/draft/2019-09/schema" 32 | } 33 | 34 | final case class Draft12(id: String) extends Version { 35 | 36 | val uri: String = "https://json-schema.org/draft/2020-12/schema" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/definition.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | final case class definition(label: String = "__default__") extends StaticAnnotation 6 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/description.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | final case class description(text: String) extends StaticAnnotation 6 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/discriminator.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | final case class discriminator(field: String = "type", phantom: Boolean = true) extends StaticAnnotation 6 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/discriminatorKey.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | final case class discriminatorKey(label: String) extends StaticAnnotation 6 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/readOnly.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | final class readOnly extends StaticAnnotation 6 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/title.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | final case class title(text: String) extends StaticAnnotation 6 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/typeHint.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | final class typeHint[T] extends StaticAnnotation with Serializable 6 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/validation/Def.scala: -------------------------------------------------------------------------------- 1 | package json.schema.validation 2 | 3 | import com.github.andyglow.json.Value 4 | 5 | final case class Def[S, V](validation: Instance[S, V], value: V, json: Value) { 6 | 7 | override def canEqual(that: Any): Boolean = that.isInstanceOf[Def[_, _]] 8 | 9 | override def equals(that: Any): Boolean = that match { 10 | case that: Def[_, _] => 11 | getClass == that.getClass && validation == that.validation && json == that.json 12 | case _ => false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/validation/Instance.scala: -------------------------------------------------------------------------------- 1 | package json.schema.validation 2 | 3 | import com.github.andyglow.json.Value._ 4 | import com.github.andyglow.json.Value 5 | import com.github.andyglow.jsonschema.AsValue 6 | import json.schema.Version.Draft04 7 | 8 | sealed abstract class Instance[S, V]()(implicit conv: V => Value) extends Product { 9 | 10 | def name: String = productPrefix 11 | 12 | def :=(x: V): Def[S, V] = Def(this, x, conv(x)) 13 | } 14 | 15 | object Instance { 16 | 17 | case object `multipleOf` extends Instance[Number, Number]()(num.apply) 18 | 19 | case object `maximum` extends Instance[Number, Number]()(num.apply) 20 | 21 | case object `minimum` extends Instance[Number, Number]()(num.apply) 22 | 23 | case object `exclusiveMaximum` extends Instance[Number, Number]()(num.apply) 24 | 25 | case object `exclusiveMinimum` extends Instance[Number, Number]()(num.apply) 26 | 27 | case object `contentEncoding` extends Instance[String, String]()(str.apply) 28 | 29 | case object `contentMediaType` extends Instance[String, String]()(str.apply) 30 | 31 | case object `contentSchema` 32 | extends Instance[String, json.Schema[_]]()( 33 | AsValue.schema(_, Draft04()) 34 | ) // FIXME: shouldn't specify a Version at this level 35 | 36 | case object `maxLength` extends Instance[String, Int]()(num.apply) 37 | 38 | case object `minLength` extends Instance[String, Int]()(num.apply) 39 | 40 | case object `pattern` extends Instance[String, String]()(str.apply) 41 | 42 | case object `maxItems` extends Instance[Iterable[_], Int]()(num.apply) 43 | 44 | case object `minItems` extends Instance[Iterable[_], Int]()(num.apply) 45 | 46 | case object `maxContains` extends Instance[Iterable[_], Int]()(num.apply) 47 | 48 | case object `minContains` extends Instance[Iterable[_], Int]()(num.apply) 49 | 50 | case object `uniqueItems` extends Instance[Iterable[_], Boolean]()(bool.apply) 51 | 52 | case object `maxProperties` extends Instance[Map[_, _], Int]()(num.apply) 53 | 54 | case object `minProperties` extends Instance[Map[_, _], Int]()(num.apply) 55 | 56 | case object `patternProperties` extends Instance[Map[_, _], String]()(str.apply) 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/validation/Magnet.scala: -------------------------------------------------------------------------------- 1 | package json.schema.validation 2 | 3 | import scala.annotation.implicitNotFound 4 | 5 | @implicitNotFound( 6 | "Implicit not found: validation.Magnet[${F}, ${T}]. Some of validations doesn't match schema type" 7 | ) 8 | trait Magnet[F, T] { 9 | 10 | def append(seq: collection.Seq[Def[_, _]], item: Def[T, _]): collection.Seq[Def[_, _]] = 11 | seq :+ item 12 | } 13 | 14 | object Magnet { 15 | def mk[A, B]: Magnet[A, B] = new Magnet[A, B] {} 16 | 17 | implicit def identityMagnet[X]: Magnet[X, X] = mk[X, X] 18 | 19 | implicit def numericMagnet[X: Numeric]: Magnet[X, Number] = mk[X, Number] 20 | 21 | implicit def intMapMagnet[X]: Magnet[Map[Int, X], Map[Int, _]] = mk[Map[Int, X], Map[Int, _]] 22 | implicit def stringMapMagnet[X]: Magnet[Map[String, X], Map[String, _]] = 23 | mk[Map[String, X], Map[String, _]] 24 | implicit def mapMagnet[K, V]: Magnet[Map[K, V], Map[_, _]] = mk[Map[K, V], Map[_, _]] 25 | 26 | implicit def arrayMagnet[X]: Magnet[Array[X], Iterable[_]] = mk[Array[X], Iterable[_]] 27 | implicit def iterableMagnet[X]: Magnet[Iterable[X], Iterable[_]] = mk[Iterable[X], Iterable[_]] 28 | implicit def listMagnet[X]: Magnet[List[X], Iterable[_]] = mk[List[X], Iterable[_]] 29 | implicit def vectorMagnet[X]: Magnet[Vector[X], Iterable[_]] = mk[Vector[X], Iterable[_]] 30 | implicit def setMagnet[X]: Magnet[Set[X], Iterable[_]] = mk[Set[X], Iterable[_]] 31 | 32 | implicit def chrMagnet: Magnet[Char, String] = mk[Char, String] 33 | implicit def characterMagnet: Magnet[Character, String] = mk[Character, String] 34 | implicit def uuidMagnet: Magnet[java.util.UUID, String] = mk[java.util.UUID, String] 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/scala/json/schema/writeOnly.scala: -------------------------------------------------------------------------------- 1 | package json.schema 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | final class writeOnly extends StaticAnnotation 6 | -------------------------------------------------------------------------------- /core/src/test/scala-2.11/com/github/andyglow/testsupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow 2 | 3 | import com.github.andyglow.scalamigration._ 4 | import scala.util.Try 5 | import org.scalatest.matchers.should.Matchers.fail 6 | 7 | object testsupport { 8 | 9 | implicit class TestTryOps[T](private val t: Try[T]) extends AnyVal { 10 | 11 | def value: T = t.fold(err => fail("", err), identity) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/test/scala-2.12/com/github/andyglow/testsupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow 2 | 3 | import org.scalatest.matchers.should.Matchers.fail 4 | import scala.util.Try 5 | 6 | object testsupport { 7 | 8 | implicit class TestTryOps[T](private val t: Try[T]) extends AnyVal { 9 | 10 | def value: T = t.fold(err => fail("", err), identity) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/src/test/scala-2.13/com/github/andyglow/testsupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow 2 | 3 | import org.scalatest.matchers.should.Matchers.fail 4 | 5 | import scala.util.Try 6 | 7 | object testsupport { 8 | 9 | implicit class TestTryOps[T](private val t: Try[T]) extends AnyVal { 10 | 11 | def value: T = t.fold(err => fail("", err), identity) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/test/scala/com/github/andyglow/json/DiffSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | import org.scalatest.matchers.should.Matchers._ 4 | import org.scalatest.wordspec.AnyWordSpec 5 | import comparison._ 6 | import comparison.Result._ 7 | import comparison.Diff._ 8 | 9 | class DiffSpec extends AnyWordSpec { 10 | import Value._ 11 | 12 | "diff" should { 13 | 14 | "be computed" when { 15 | 16 | "primitives are equal" in { 17 | str("1").diff("1") shouldBe Equal 18 | `null`.diff(`null`) shouldBe Equal 19 | `true`.diff(`true`) shouldBe Equal 20 | `false`.diff(`false`) shouldBe Equal 21 | num(5).diff(5) shouldBe Equal 22 | } 23 | 24 | "primitives has same types but different values" in { 25 | str("1").diff("2") shouldBe Different(ValueMismatch(Path.Empty, "1", "2")) 26 | `true`.diff(`false`) shouldBe Different(ValueMismatch(Path.Empty, true, false)) 27 | num(5).diff(15) shouldBe Different(ValueMismatch(Path.Empty, 5, 15)) 28 | `null`.diff(15) shouldBe Different(ValueMismatch(Path.Empty, `null`, num(15))) 29 | } 30 | 31 | "primitives has different types" in { 32 | str("1").diff(`null`) shouldBe Different(TypeMismatch(Path.Empty, "string", "null")) 33 | str("1").diff(2) shouldBe Different(TypeMismatch(Path.Empty, "string", "number")) 34 | `true`.diff("abc") shouldBe Different(TypeMismatch(Path.Empty, "boolean", "string")) 35 | num(5).diff("abc") shouldBe Different(TypeMismatch(Path.Empty, "number", "string")) 36 | } 37 | 38 | "structural types" when { 39 | 40 | "objects are equal" in { 41 | obj("a" -> "b").diff(obj("a" -> "b")) shouldBe Equal 42 | obj("a" -> `true`).diff(obj("a" -> `true`)) shouldBe Equal 43 | obj("a" -> `false`).diff(obj("a" -> `false`)) shouldBe Equal 44 | obj("a" -> 15).diff(obj("a" -> 15)) shouldBe Equal 45 | } 46 | 47 | "objects are different" in { 48 | obj("a" -> "b", "c" -> "d").diff( 49 | obj( 50 | "b" -> "c" 51 | ) 52 | ) shouldBe Different( 53 | MissingProperty("b", "c"), 54 | MissingProperty("a", "b"), 55 | MissingProperty("c", "d") 56 | ) 57 | } 58 | 59 | "arrays are equal" in { 60 | arr(1, 2, 3).diff(arr(1, 2, 3)) shouldBe Equal 61 | arr("1", "2", "3").diff(arr("1", "2", "3")) shouldBe Equal 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /core/src/test/scala/com/github/andyglow/json/ValueDslSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | import org.scalatest.prop.TableDrivenPropertyChecks._ 4 | import com.github.andyglow.json.Value._ 5 | import org.scalatest.matchers.should.Matchers._ 6 | import org.scalatest.propspec.AnyPropSpec 7 | 8 | class ValueDslSpec extends AnyPropSpec { 9 | 10 | val some: Option[String] = Some("15") 11 | 12 | val none: Option[String] = None 13 | 14 | val examples = Table( 15 | ("DSL", "Expected"), 16 | (obj("foo" -> true), obj("foo" -> `true`)), 17 | (obj("foo" -> false), obj("foo" -> `false`)), 18 | (obj("foo" -> "bar"), obj("foo" -> str("bar"))), 19 | (obj("foo" -> 15), obj("foo" -> num(15))), 20 | (obj("foo" -> some, "bar" -> none), obj("foo" -> str("15"))), 21 | (arr(1, 2), arr(num(1), num(2))), 22 | (arr("str", 2), arr(str("str"), num(2))) 23 | ) 24 | 25 | property("check is DSL works well") { 26 | forAll(examples) { (js1, js2) => js1 shouldEqual js2 } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/test/scala/json/SchemaSpec.scala: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import org.scalatest.matchers.should.Matchers._ 4 | import org.scalatest.OptionValues._ 5 | import org.scalatest.wordspec.AnyWordSpec 6 | import Schema._ 7 | import `object`._ 8 | import json.schema.validation.Instance._ 9 | import org.scalactic.source.Position 10 | 11 | class SchemaSpec extends AnyWordSpec { 12 | import SchemaSpec._ 13 | 14 | "Schema" should { 15 | // check all places where `copy` method call is observed 16 | "copy extra" when { 17 | "def.withValidation" in { checkExtraGotCopied(_def) { _.withValidation(`maxLength` := 200) } } 18 | "def.toDefinition" in { checkExtraGotCopied(_def) { _.toDefinition("new-ref") } } 19 | "object.withField" in { checkExtraGotCopied(_obj) { _.withField("bar", `integer`) } } 20 | "object.dropField" in { checkExtraGotCopied(_obj) { _ dropField { _.name == "foo" } } } 21 | "object.withFieldsUpdated" in { 22 | checkExtraGotCopied(_obj) { 23 | _.withFieldsUpdated { 24 | case f if f.name == "foo" => f.copy(tpe = `boolean`) 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | 32 | object SchemaSpec { 33 | import `string`.Format._ 34 | 35 | private val _obj = `object`[Any]( 36 | Field("foo", `string`) 37 | ) 38 | 39 | private val _def = `def`[String]( 40 | "some-ref", 41 | `string`(`date-time`) 42 | ) 43 | 44 | private def checkExtraGotCopied[T, S[_] <: Schema[_]](schema: S[T])(mod: S[T] => S[T])(implicit pos: Position): Unit = { 45 | val schemaWithExtra = schema 46 | .withTitle("some title") 47 | .withDescription("some description") 48 | .withDiscriminationKey("_key") 49 | 50 | val resultingSchema = mod(schemaWithExtra.asInstanceOf[S[T]]) 51 | 52 | resultingSchema.title.value shouldBe "some title" 53 | resultingSchema.description.value shouldBe "some description" 54 | resultingSchema.discriminationKey.value shouldBe "_key" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/ScalaParts.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | object ScalaParts { 4 | 5 | case class ParsedParameter(name: String, tpe: String, default: Option[String]) 6 | 7 | object ParsedParameter { 8 | 9 | def fromString(x: String): ParsedParameter = { 10 | val colonIdx = x.indexOf(':') 11 | val eqIdx = x.indexOf('=') 12 | require(eqIdx < 0 || colonIdx < eqIdx) 13 | 14 | val name = x.substring(0, colonIdx).trim 15 | val (tpe, default) = if (eqIdx >= 0) { 16 | (x.substring(colonIdx + 1, eqIdx).trim, Some(x.substring(eqIdx + 1).trim)) 17 | } else { 18 | (x.substring(colonIdx + 1).trim, None) 19 | } 20 | 21 | ParsedParameter(name, tpe, default) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/UArrays.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | private[jsonschema] trait UArrays { this: UContext with UCommons => 4 | import c.universe._ 5 | 6 | class ArrExtractor { 7 | 8 | def unapply(tpe: Type)(implicit ctx: ResolutionContext): Option[U.Arr] = { 9 | Some.when(tpe <:< T.array || tpe <:< T.iterable) { 10 | val containerTpe = tpe.typeConstructor 11 | val elementTpe = tpe.typeArgs.head 12 | val elementSchema = resolve(elementTpe, ctx :+ tpe) 13 | 14 | U.Arr(elementTpe, containerTpe, elementSchema, unique = tpe <:< T.set) 15 | } 16 | } 17 | } 18 | 19 | val Arr = new ArrExtractor 20 | } 21 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/UContext.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import scala.reflect.macros.blackbox 4 | 5 | private[jsonschema] trait UContext { val c: blackbox.Context } 6 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/UFlags.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | private[jsonschema] trait UFlags { this: UContext => 4 | import c.universe._ 5 | 6 | case class Flags( 7 | enumsAsOneOf: Boolean = false 8 | ) 9 | 10 | lazy val flags: Flags = { 11 | val ff = c.inferImplicitValue(typeOf[json.schema.Flag]) 12 | if (ff.isEmpty) Flags() 13 | else { 14 | val enumsAsOneOf = ff.tpe <:< typeOf[json.schema.Flag.EnumsAsOneOf] 15 | Flags(enumsAsOneOf = enumsAsOneOf) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/ULogging.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | private[jsonschema] trait ULogging { this: UContext => 4 | 5 | val debugEnabled = false 6 | 7 | val dbg: String => Unit = 8 | if (debugEnabled) c.info(c.enclosingPosition, _, force = true) else _ => () 9 | 10 | val info: String => Unit = c.info(c.enclosingPosition, _, force = true) 11 | 12 | val warn: String => Unit = c.warning(c.enclosingPosition, _) 13 | 14 | val err: String => Unit = c.error(c.enclosingPosition, _) 15 | 16 | val abort: String => Nothing = c.abort(c.enclosingPosition, _) 17 | } 18 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/URecursiveTypes.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | private[jsonschema] trait URecursiveTypes { this: UContext with SchemaTypes with USignatures => 4 | import c.universe._ 5 | 6 | class RecursiveTypes { 7 | 8 | private var types: List[Type] = Nil 9 | 10 | def append(x: Type): Unit = { 11 | if (types.exists(_ =:= x)) () 12 | else { 13 | types = types :+ x 14 | } 15 | } 16 | 17 | /** Traverses through the schema tree and replaces top recursive types with Ref, so that it would go to definition when rendered 18 | * 19 | * @param in 20 | * @return 21 | */ 22 | def substitute(in: SchemaType): SchemaType = { 23 | import SchemaType._ 24 | 25 | transformSchema(in) { 26 | case st if types.exists(_ =:= st.tpe) => 27 | st match { 28 | case _: Def | _: Ref => st 29 | case st => Def(st.tpe, q"${signature(st.tpe)}", st) 30 | } 31 | case st => st 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/UScalaParsers.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | private[jsonschema] trait UScalaParsers { this: UContext => 4 | import c.universe._ 5 | import ScalaParts._ 6 | 7 | def parseParameter(sym: TermSymbol): ParsedParameter = { 8 | utils.ScalaParser.parseField(sym.pos.source.content, sym.pos.start) match { 9 | case Right(x) => x 10 | case Left(err) => c.abort(c.enclosingPosition, err) 11 | } 12 | } 13 | 14 | def parseFCQN(x: String): Tree = { 15 | val path = x.split('.') 16 | if (path.length == 1) 17 | Ident(TermName(path.head)) 18 | else 19 | path.tail.foldLeft[Tree](Ident(TermName(path.head))) { case (acc, x) => 20 | Select(acc, TermName(x)) 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/UScaladocs.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import scaladoc._ 4 | import scaladoc.macros.ExtractScaladoc 5 | 6 | private[jsonschema] trait UScaladocs extends ExtractScaladoc { this: UContext with UCommons => 7 | import c.universe._ 8 | 9 | def getTypeScaladoc(tpe: Type): Option[Scaladoc] = { 10 | fromAttachment orElse fromAnnotatedType(tpe) orElse fromSourceCode(tpe.typeSymbol.pos) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/USignatures.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | private[jsonschema] trait USignatures { this: UContext => 4 | import c.universe._ 5 | 6 | def signature(tpe: Type): String = { 7 | 8 | def compute(tpe: Type): String = { 9 | 10 | tpe match { 11 | case tpe: SingleType => tpe.typeSymbol.fullName 12 | case TypeRef(_, name, Nil) => name.fullName 13 | case ExistentialType(_, TypeRef(_, name, Nil)) => name.fullName 14 | case TypeRef(_, name, typeargs) => name.fullName + s"[${typeargs map compute mkString ","}]" 15 | case ExistentialType(_, TypeRef(_, name, typeargs)) => 16 | name.fullName + s"[${typeargs map compute mkString ","}]" 17 | case ConstantType(x) => x.value.toString 18 | } 19 | } 20 | 21 | compute(tpe) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /macros/src/main/scala/com/github/andyglow/jsonschema/UValueTypes.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | private[jsonschema] trait UValueTypes { this: UContext with UCommons => 4 | import c.universe._ 5 | 6 | class ValueClassExtractor { 7 | 8 | def unapply(tpe: Type)(implicit ctx: ResolutionContext): Option[U.ValueClass] = { 9 | val symbol = tpe.typeSymbol 10 | 11 | Option.whenever(symbol.isClass) { 12 | val clazz = symbol.asClass 13 | Some.when(clazz.isCaseClass && clazz.isDerivedValueClass) { 14 | val innerType = clazz.primaryConstructor.asMethod.paramLists.head.head.typeSignature 15 | U.ValueClass(tpe, innerType, resolve(innerType, ctx :+ tpe)) 16 | } 17 | } 18 | } 19 | } 20 | 21 | val ValueClass = new ValueClassExtractor 22 | } 23 | -------------------------------------------------------------------------------- /modules/cats/src/main/scala-2.12-/com/github/andyglow/jsonschema/ScalaVersionSpecificCatsSupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import cats.data._ 4 | import json.Schema 5 | import json.schema.Predef 6 | 7 | private[jsonschema] trait ScalaVersionSpecificCatsSupport { 8 | this: LowPriorityCatsSupport with ScalaVersionSpecificLowPriorityCatsSupport => 9 | 10 | implicit def nestSchema[T](implicit ss: Schema[T]): Predef[NonEmptyStream[T]] = 11 | mkNEx[T, NonEmptyStream](ss) 12 | } 13 | -------------------------------------------------------------------------------- /modules/cats/src/main/scala-2.12-/com/github/andyglow/jsonschema/ScalaVersionSpecificLowPriorityCatsSupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import cats.data._ 4 | import json.schema.validation.Magnet 5 | import json.schema.Predef 6 | 7 | private[jsonschema] trait ScalaVersionSpecificLowPriorityCatsSupport { 8 | this: LowPriorityCatsSupport => 9 | import Magnet.mk 10 | 11 | implicit def nestVB[X]: Magnet[NonEmptyStream[X], Iterable[_]] = 12 | mk[NonEmptyStream[X], Iterable[_]] 13 | 14 | implicit def nestSchemaFromPredef[T](implicit p: Predef[T]): Predef[NonEmptyStream[T]] = 15 | mkNEx[T, NonEmptyStream](p.schema) 16 | } 17 | -------------------------------------------------------------------------------- /modules/cats/src/main/scala-2.13+/com/github/andyglow/jsonschema/ScalaVersionSpecificCatsSupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import cats.data.NonEmptyLazyList 4 | import json.Schema 5 | import json.schema.Predef 6 | 7 | private[jsonschema] trait ScalaVersionSpecificCatsSupport { this: LowPriorityCatsSupport with ScalaVersionSpecificLowPriorityCatsSupport => 8 | 9 | implicit def nellSchema[T](implicit ss: Schema[T]): Predef[NonEmptyLazyList[T]] = mkNEx[T, NonEmptyLazyList](ss) 10 | } 11 | -------------------------------------------------------------------------------- /modules/cats/src/main/scala-2.13+/com/github/andyglow/jsonschema/ScalaVersionSpecificLowPriorityCatsSupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import cats.data.NonEmptyLazyList 4 | import json.schema.validation.Magnet 5 | import json.schema.Predef 6 | 7 | private[jsonschema] trait ScalaVersionSpecificLowPriorityCatsSupport { this: LowPriorityCatsSupport => 8 | import Magnet.mk 9 | 10 | implicit def nellVB[X]: Magnet[NonEmptyLazyList[X], Iterable[_]] = mk[NonEmptyLazyList[X], Iterable[_]] 11 | 12 | implicit def nellSchemaFromPredef[T](implicit p: Predef[T]): Predef[NonEmptyLazyList[T]] = mkNEx[T, NonEmptyLazyList](p.schema) 13 | } 14 | -------------------------------------------------------------------------------- /modules/cats/src/test/scala-2.12-/com/github/andyglow/jsonschema/ScalaVersionSpecificCatsSupportSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import cats.data._ 4 | import json.Schema 5 | import json.Schema.`object`._ 6 | import json.Schema._ 7 | import json.schema.validation.Instance._ 8 | import CatsSupport._ 9 | import org.scalatest.matchers.should.Matchers._ 10 | import org.scalatest.wordspec.AnyWordSpec 11 | 12 | class ScalaVersionSpecificCatsSupportSpec extends AnyWordSpec { 13 | import ScalaVersionSpecificCatsSupportSpec._ 14 | 15 | "CatsSupportSpec" when { 16 | 17 | "NonEmptyStream" should { 18 | 19 | "be exposed as object" in { 20 | nestEventSchema shouldBe `object`( 21 | Field("id", `string`), 22 | Field("arr", `array`[String, NonEmptyStream](`string`).withValidation(`minItems` := 1)) 23 | ) 24 | } 25 | } 26 | } 27 | } 28 | 29 | object ScalaVersionSpecificCatsSupportSpec { 30 | 31 | case class NonEmptyStreamEvent(id: String, arr: NonEmptyStream[String]) 32 | lazy val nestEventSchema: Schema[NonEmptyStreamEvent] = json.Json.schema[NonEmptyStreamEvent] 33 | } 34 | -------------------------------------------------------------------------------- /modules/cats/src/test/scala-2.13+/com/github/andyglow/jsonschema/ScalaVersionSpecificCatsSupportSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import cats.data._ 4 | import json.Schema 5 | import json.Schema.`object`._ 6 | import json.Schema._ 7 | import json.schema.validation.Instance._ 8 | import org.scalatest.matchers.should.Matchers._ 9 | import org.scalatest.wordspec.AnyWordSpec 10 | import CatsSupport._ 11 | 12 | class ScalaVersionSpecificCatsSupportSpec extends AnyWordSpec { 13 | import ScalaVersionSpecificCatsSupportSpec._ 14 | 15 | "CatsSupportSpec" when { 16 | 17 | "NonEmptyLazyList" should { 18 | 19 | "be exposed as object" in { 20 | nellEventSchema shouldBe `object`( 21 | Field("id", `string`), 22 | Field("arr", `array`[String, NonEmptyLazyList](`string`).withValidation(`minItems` := 1)) 23 | ) 24 | } 25 | } 26 | } 27 | } 28 | 29 | object ScalaVersionSpecificCatsSupportSpec { 30 | 31 | case class NonEmptyLazyListEvent(id: String, arr: NonEmptyLazyList[String]) 32 | lazy val nellEventSchema: Schema[NonEmptyLazyListEvent] = json.Json.schema[NonEmptyLazyListEvent] 33 | } 34 | -------------------------------------------------------------------------------- /modules/circe-json/src/main/scala/com/github/andyglow/jsonschema/AsCirce.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.json._ 4 | import com.github.andyglow.json.Value._ 5 | import json.Schema 6 | import io.circe._ 7 | import com.github.andyglow.scalamigration._ 8 | import json.schema.Version 9 | 10 | object AsCirce { 11 | 12 | final def apply(value: Value): Json = value match { 13 | case `null` => Json.Null 14 | case `true` => Json.True 15 | case `false` => Json.False 16 | case num(x) => Json.fromBigDecimal(x) 17 | case str(x) => Json.fromString(x) 18 | case arr(x) => val arr = x map AsCirce.apply; Json.arr(arr.toSeq: _*) 19 | case obj(x) => val map = x.toMap mapV AsCirce.apply; Json.obj(map.toSeq: _*) 20 | } 21 | 22 | implicit class CirceSchemaOps[T](val x: Schema[T]) extends AnyVal { 23 | 24 | def asCirce[V <: Version](v: V)(implicit asValue: AsValueBuilder[V]): Json = AsCirce( 25 | AsValue.schema(x, v) 26 | ) 27 | } 28 | 29 | implicit def toValue[T](implicit w: Encoder[T]): ToValue[T] = new ToValue[T] { 30 | override def apply(x: T): Value = { 31 | val js = w.apply(x) 32 | def translate(js: Json): Value = js match { 33 | case Json.Null => `null` 34 | case Json.True => `true` 35 | case Json.False => `false` 36 | case x if x.isNumber => num(x.asNumber.get.toDouble) 37 | case x if x.isString => str(x.asString.get) 38 | case x if x.isArray => val a = x.asArray.get map translate; arr(a) 39 | case x if x.isObject => val map = x.asObject.get.toMap mapV translate; obj(map) 40 | case _ => throw new IllegalArgumentException() // compiler warns of non-exhaustive matching otherwise 41 | } 42 | 43 | translate(js) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/circe-json/src/test/scala/com/github/andyglow/jsonschema/UserProfileJson.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.jsonschema.model._ 4 | import io.circe._ 5 | import io.circe.generic.semiauto._ 6 | 7 | object UserProfileJson { 8 | 9 | implicit val CredentialsW: Encoder[Credentials] = deriveEncoder[Credentials] 10 | 11 | implicit val NotesW: Encoder[Notes] = deriveEncoder[Notes] 12 | 13 | implicit val BetaFeatureW: Encoder[BetaFeature] = new Encoder[BetaFeature] { 14 | override def apply(o: BetaFeature): Json = o match { 15 | case F0 => Json.fromString("feature-0-name") 16 | case F1 => Json.fromString("feature-1-name") 17 | case F2 => Json.fromString("feature-2-name") 18 | } 19 | } 20 | 21 | implicit val RoleW: Encoder[Role] = new Encoder[Role] { 22 | import Role._ 23 | 24 | override def apply(o: Role): Json = o match { 25 | case User => Json.fromString("e-user") 26 | case Manager => Json.fromString("e-manager") 27 | case Admin => Json.fromString("e-admin") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /modules/derived/src/main/scala/json/schema/derived/DerivedSchema.scala: -------------------------------------------------------------------------------- 1 | package json.schema.derived 2 | 3 | import com.github.andyglow.jsonschema.MacroCake 4 | import json.Schema 5 | 6 | import scala.language.experimental.macros 7 | import scala.reflect.macros.whitebox 8 | 9 | final case class DerivedSchema[T](schema: Schema[T]) 10 | 11 | object DerivedSchema { 12 | 13 | implicit def deriveDerivedSchema[T]: DerivedSchema[T] = 14 | macro DerivedSchemaMacros.deriveDerivedSchema[T] 15 | } 16 | 17 | class DerivedSchemaMacros(val c: whitebox.Context) extends MacroCake { 18 | import c.universe._ 19 | 20 | def deriveDerivedSchema[T](implicit 21 | T: c.WeakTypeTag[T] 22 | ): c.Expr[DerivedSchema[T]] = { 23 | val schemaTree = deriveInternal[T, json.Schema](noImplicitSearch = true).tree 24 | val derivedSchemaTree = q"_root_.json.schema.derived.DerivedSchema($schemaTree)" 25 | c.Expr[DerivedSchema[T]](derivedSchemaTree) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/derived/src/test/scala/json/schema/derived/DerivedSchemaSpec.scala: -------------------------------------------------------------------------------- 1 | package json.schema.derived 2 | 3 | import json.{Json, Schema} 4 | import org.scalatest.matchers.should.Matchers._ 5 | import org.scalatest.funsuite._ 6 | 7 | class DerivedSchemaSpec extends AnyFunSuite { 8 | import DerivedSchemaSpec._ 9 | 10 | test("compiles") { 11 | """case class ABC(a: Int, b: String, c: Boolean) 12 | |val schema = implicitly[DerivedSchema[ABC]] 13 | |""".stripMargin 14 | } 15 | 16 | test("equal") { 17 | implicitly[DerivedSchema[ABC]].schema shouldBe abcSchema 18 | } 19 | 20 | test("derives") { 21 | HasSchema[ABC](ABC(1, "2", false)).schema shouldBe abcSchema 22 | } 23 | } 24 | 25 | object DerivedSchemaSpec { 26 | 27 | case class ABC(a: Int, b: String, c: Boolean) 28 | 29 | val abcSchema = Json.schema[ABC] 30 | 31 | case class HasSchema[T](value: T)(implicit val D: DerivedSchema[T]) { 32 | def schema: Schema[T] = D.schema 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /modules/joda-time/src/main/scala/com/github/andyglow/jsonschema/JodaTimeSupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import json.Schema 4 | import json.Schema._ 5 | import json.schema.Predef 6 | import org.joda.time._ 7 | 8 | object JodaTimeSupport { 9 | 10 | implicit val jtDateTimeSchema: Schema[DateTime] = `string`[DateTime](`string`.Format.`date-time`) 11 | 12 | implicit val jtInstantSchema: Schema[Instant] = `string`[Instant](`string`.Format.`date-time`) 13 | 14 | implicit val jtLocalDateTimeSchema: Schema[LocalDateTime] = 15 | `string`[LocalDateTime](`string`.Format.`date-time`) 16 | 17 | implicit val jtLocalDateSchema: Schema[LocalDate] = `string`[LocalDate](`string`.Format.`date`) 18 | 19 | implicit val jtLocalTimeSchema: Schema[LocalTime] = `string`[LocalTime](`string`.Format.`time`) 20 | 21 | implicit val jtDurationSchema: Schema[Duration] = `string`[Duration](`string`.Format.`duration`) 22 | 23 | object predef { 24 | 25 | implicit val jtDateTimePredef: Predef[DateTime] = Predef(jtDateTimeSchema) 26 | 27 | implicit val jtInstantPredef: Predef[Instant] = Predef(jtInstantSchema) 28 | 29 | implicit val jtLocalDateTimePredef: Predef[LocalDateTime] = Predef(jtLocalDateTimeSchema) 30 | 31 | implicit val jtLocalDatePredef: Predef[LocalDate] = Predef(jtLocalDateSchema) 32 | 33 | implicit val jtLocalTimePredef: Predef[LocalTime] = Predef(jtLocalTimeSchema) 34 | 35 | implicit val jtDurationPredef: Predef[Duration] = Predef(jtDurationSchema) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /modules/json4s-json/src/test/scala/com/github/andyglow/jsonschema/UserProfileJson.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.jsonschema.model._ 4 | import org.json4s.Writer 5 | import org.json4s.JsonAST._ 6 | 7 | object UserProfileJson { 8 | 9 | implicit val CredentialsW: Writer[Credentials] = new Writer[Credentials] { 10 | override def write(o: Credentials): JValue = 11 | JObject("login" -> JString(o.login), "password" -> JString(o.password)) 12 | } 13 | 14 | implicit val NotesW: Writer[Notes] = new Writer[Notes] { 15 | override def write(o: Notes): JValue = 16 | JObject("head" -> JString(o.head), "tail" -> JArray(o.tail map JString.apply)) 17 | } 18 | 19 | implicit val BetaFeatureW: Writer[BetaFeature] = new Writer[BetaFeature] { 20 | override def write(o: BetaFeature): JValue = o match { 21 | case F0 => JString("feature-0-name") 22 | case F1 => JString("feature-1-name") 23 | case F2 => JString("feature-2-name") 24 | } 25 | } 26 | 27 | implicit val RoleW: Writer[Role] = new Writer[Role] { 28 | import Role._ 29 | 30 | override def write(o: Role): JValue = o match { 31 | case User => JString("e-user") 32 | case Manager => JString("e-manager") 33 | case Admin => JString("e-admin") 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /modules/parser/src/main/java/com/github/andyglow/json/JsonHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json; 2 | 3 | 4 | import java.math.BigDecimal; 5 | import java.math.BigInteger; 6 | 7 | interface JsonHandler { 8 | 9 | void start(); 10 | 11 | void end(); 12 | 13 | void objectStart(); 14 | 15 | void objectEnd(); 16 | 17 | void arrayStart(); 18 | 19 | void arrayEnd(); 20 | 21 | void name(String name); 22 | 23 | void value(String value); 24 | 25 | void value(int value); 26 | 27 | void value(long value); 28 | 29 | void value(BigInteger value); 30 | 31 | void value(BigDecimal value); 32 | 33 | void value(boolean value); 34 | 35 | void nullValue(); 36 | } 37 | -------------------------------------------------------------------------------- /modules/parser/src/main/java/com/github/andyglow/json/JsonParseException.java: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json; 2 | 3 | class JsonParseException extends Exception { 4 | 5 | JsonParseException() { 6 | super(); 7 | } 8 | 9 | JsonParseException(String msg) { 10 | super(msg); 11 | } 12 | } -------------------------------------------------------------------------------- /modules/parser/src/main/scala/com/github/andyglow/json/MutableValue.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | import scala.collection.mutable 4 | import com.github.andyglow.scalamigration._ 5 | 6 | sealed trait MutableValue { 7 | 8 | def toValue: Value 9 | } 10 | 11 | object MutableValue { 12 | 13 | case object `null` extends MutableValue { override def toValue = Value.`null` } 14 | 15 | case class bool(value: Boolean) extends MutableValue { 16 | 17 | override def toValue = if (value) Value.`true` else Value.`false` 18 | } 19 | 20 | case class int(value: BigInt) extends MutableValue { override def toValue = Value.num(value) } 21 | 22 | case class dec(value: BigDecimal) extends MutableValue { override def toValue = Value.num(value) } 23 | 24 | case class str(value: String) extends MutableValue { override def toValue = Value.str(value) } 25 | 26 | case class arr(value: mutable.ArrayBuffer[MutableValue] = mutable.ArrayBuffer.empty) extends MutableValue { 27 | 28 | def +=(el: MutableValue): arr = { value += el; this } 29 | 30 | override def toValue = Value.arr(value map { _.toValue }) 31 | } 32 | 33 | case class obj(value: mutable.Map[String, MutableValue] = mutable.Map.empty) extends MutableValue { 34 | 35 | def update(k: String, v: MutableValue): obj = { value.update(k, v); this } 36 | 37 | override def toValue = Value.obj(value.toMap mapV { _.toValue }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_double_huge_neg_exp.json: -------------------------------------------------------------------------------- 1 | [123.456e-789] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_huge_exp.json: -------------------------------------------------------------------------------- 1 | [0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_neg_int_huge_exp.json: -------------------------------------------------------------------------------- 1 | [-1e+9999] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_pos_double_huge_exp.json: -------------------------------------------------------------------------------- 1 | [1.5e+9999] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_real_neg_overflow.json: -------------------------------------------------------------------------------- 1 | [-123123e100000] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_real_pos_overflow.json: -------------------------------------------------------------------------------- 1 | [123123e100000] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_real_underflow.json: -------------------------------------------------------------------------------- 1 | [123e-10000000] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_too_big_neg_int.json: -------------------------------------------------------------------------------- 1 | [-123123123123123123123123123123] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_too_big_pos_int.json: -------------------------------------------------------------------------------- 1 | [100000000000000000000] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_number_very_big_negative_int.json: -------------------------------------------------------------------------------- 1 | [-237462374673276894279832749832423479823246327846] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_object_key_lone_2nd_surrogate.json: -------------------------------------------------------------------------------- 1 | {"\uDFAA":0} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_1st_surrogate_but_2nd_missing.json: -------------------------------------------------------------------------------- 1 | ["\uDADA"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_1st_valid_surrogate_2nd_invalid.json: -------------------------------------------------------------------------------- 1 | ["\uD888\u1234"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_UTF-16LE_with_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_UTF-16LE_with_BOM.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_UTF-8_invalid_sequence.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_UTF-8_invalid_sequence.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_UTF8_surrogate_U+D800.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_UTF8_surrogate_U+D800.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_incomplete_surrogate_and_escape_valid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\n"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_incomplete_surrogate_pair.json: -------------------------------------------------------------------------------- 1 | ["\uDd1ea"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_incomplete_surrogates_escape_valid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\n"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_invalid_lonely_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\ud800"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_invalid_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\ud800abc"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_invalid_utf-8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_invalid_utf-8.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_inverted_surrogates_U+1D11E.json: -------------------------------------------------------------------------------- 1 | ["\uDd1e\uD834"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_iso_latin_1.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_iso_latin_1.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_lone_second_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\uDFAA"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_lone_utf8_continuation_byte.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_lone_utf8_continuation_byte.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_not_in_unicode_range.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_not_in_unicode_range.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_overlong_sequence_2_bytes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_overlong_sequence_2_bytes.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_overlong_sequence_6_bytes.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_overlong_sequence_6_bytes.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_overlong_sequence_6_bytes_null.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_overlong_sequence_6_bytes_null.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_truncated-utf-8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_truncated-utf-8.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_utf16BE_no_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_utf16BE_no_BOM.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_string_utf16LE_no_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/i_string_utf16LE_no_BOM.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_structure_500_nested_arrays.json: -------------------------------------------------------------------------------- 1 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/i_structure_UTF-8_BOM_empty_object.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_1_true_without_comma.json: -------------------------------------------------------------------------------- 1 | [1 true] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_a_invalid_utf8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_array_a_invalid_utf8.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_colon_instead_of_comma.json: -------------------------------------------------------------------------------- 1 | ["": 1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_comma_after_close.json: -------------------------------------------------------------------------------- 1 | [""], -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_comma_and_number.json: -------------------------------------------------------------------------------- 1 | [,1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_double_comma.json: -------------------------------------------------------------------------------- 1 | [1,,2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_double_extra_comma.json: -------------------------------------------------------------------------------- 1 | ["x",,] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_extra_close.json: -------------------------------------------------------------------------------- 1 | ["x"]] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_extra_comma.json: -------------------------------------------------------------------------------- 1 | ["",] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_incomplete.json: -------------------------------------------------------------------------------- 1 | ["x" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_incomplete_invalid_value.json: -------------------------------------------------------------------------------- 1 | [x -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_inner_array_no_comma.json: -------------------------------------------------------------------------------- 1 | [3[4]] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_invalid_utf8.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_array_invalid_utf8.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_items_separated_by_semicolon.json: -------------------------------------------------------------------------------- 1 | [1:2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_just_comma.json: -------------------------------------------------------------------------------- 1 | [,] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_just_minus.json: -------------------------------------------------------------------------------- 1 | [-] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_missing_value.json: -------------------------------------------------------------------------------- 1 | [ , ""] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_newlines_unclosed.json: -------------------------------------------------------------------------------- 1 | ["a", 2 | 4 3 | ,1, -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_number_and_comma.json: -------------------------------------------------------------------------------- 1 | [1,] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_number_and_several_commas.json: -------------------------------------------------------------------------------- 1 | [1,,] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_spaces_vertical_tab_formfeed.json: -------------------------------------------------------------------------------- 1 | [" a"\f] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_star_inside.json: -------------------------------------------------------------------------------- 1 | [*] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_unclosed.json: -------------------------------------------------------------------------------- 1 | ["" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_unclosed_trailing_comma.json: -------------------------------------------------------------------------------- 1 | [1, -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_unclosed_with_new_lines.json: -------------------------------------------------------------------------------- 1 | [1, 2 | 1 3 | ,1 -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_array_unclosed_with_object_inside.json: -------------------------------------------------------------------------------- 1 | [{} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_incomplete_false.json: -------------------------------------------------------------------------------- 1 | [fals] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_incomplete_null.json: -------------------------------------------------------------------------------- 1 | [nul] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_incomplete_true.json: -------------------------------------------------------------------------------- 1 | [tru] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_multidigit_number_then_00.json: -------------------------------------------------------------------------------- 1 | 123 -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_++.json: -------------------------------------------------------------------------------- 1 | [++1234] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_+1.json: -------------------------------------------------------------------------------- 1 | [+1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_+Inf.json: -------------------------------------------------------------------------------- 1 | [+Inf] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_-01.json: -------------------------------------------------------------------------------- 1 | [-01] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_-1.0..json: -------------------------------------------------------------------------------- 1 | [-1.0.] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_-2..json: -------------------------------------------------------------------------------- 1 | [-2.] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_-NaN.json: -------------------------------------------------------------------------------- 1 | [-NaN] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_.-1.json: -------------------------------------------------------------------------------- 1 | [.-1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_.2e-3.json: -------------------------------------------------------------------------------- 1 | [.2e-3] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_0.1.2.json: -------------------------------------------------------------------------------- 1 | [0.1.2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_0.3e+.json: -------------------------------------------------------------------------------- 1 | [0.3e+] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_0.3e.json: -------------------------------------------------------------------------------- 1 | [0.3e] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_0.e1.json: -------------------------------------------------------------------------------- 1 | [0.e1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_0_capital_E+.json: -------------------------------------------------------------------------------- 1 | [0E+] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_0_capital_E.json: -------------------------------------------------------------------------------- 1 | [0E] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_0e+.json: -------------------------------------------------------------------------------- 1 | [0e+] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_0e.json: -------------------------------------------------------------------------------- 1 | [0e] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_1.0e+.json: -------------------------------------------------------------------------------- 1 | [1.0e+] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_1.0e-.json: -------------------------------------------------------------------------------- 1 | [1.0e-] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_1.0e.json: -------------------------------------------------------------------------------- 1 | [1.0e] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_1_000.json: -------------------------------------------------------------------------------- 1 | [1 000.0] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_1eE2.json: -------------------------------------------------------------------------------- 1 | [1eE2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_2.e+3.json: -------------------------------------------------------------------------------- 1 | [2.e+3] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_2.e-3.json: -------------------------------------------------------------------------------- 1 | [2.e-3] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_2.e3.json: -------------------------------------------------------------------------------- 1 | [2.e3] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_9.e+.json: -------------------------------------------------------------------------------- 1 | [9.e+] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_Inf.json: -------------------------------------------------------------------------------- 1 | [Inf] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_NaN.json: -------------------------------------------------------------------------------- 1 | [NaN] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_U+FF11_fullwidth_digit_one.json: -------------------------------------------------------------------------------- 1 | [1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_expression.json: -------------------------------------------------------------------------------- 1 | [1+2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_hex_1_digit.json: -------------------------------------------------------------------------------- 1 | [0x1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_hex_2_digits.json: -------------------------------------------------------------------------------- 1 | [0x42] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_infinity.json: -------------------------------------------------------------------------------- 1 | [Infinity] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_invalid+-.json: -------------------------------------------------------------------------------- 1 | [0e+-1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_invalid-negative-real.json: -------------------------------------------------------------------------------- 1 | [-123.123foo] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_invalid-utf-8-in-bigger-int.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_number_invalid-utf-8-in-bigger-int.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_invalid-utf-8-in-exponent.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_number_invalid-utf-8-in-exponent.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_invalid-utf-8-in-int.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_number_invalid-utf-8-in-int.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_minus_infinity.json: -------------------------------------------------------------------------------- 1 | [-Infinity] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_minus_sign_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | [-foo] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_minus_space_1.json: -------------------------------------------------------------------------------- 1 | [- 1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_neg_int_starting_with_zero.json: -------------------------------------------------------------------------------- 1 | [-012] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_neg_real_without_int_part.json: -------------------------------------------------------------------------------- 1 | [-.123] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_neg_with_garbage_at_end.json: -------------------------------------------------------------------------------- 1 | [-1x] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_real_garbage_after_e.json: -------------------------------------------------------------------------------- 1 | [1ea] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_real_with_invalid_utf8_after_e.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_number_real_with_invalid_utf8_after_e.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_real_without_fractional_part.json: -------------------------------------------------------------------------------- 1 | [1.] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_starting_with_dot.json: -------------------------------------------------------------------------------- 1 | [.123] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_with_alpha.json: -------------------------------------------------------------------------------- 1 | [1.2a-3] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_with_alpha_char.json: -------------------------------------------------------------------------------- 1 | [1.8011670033376514H-308] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_number_with_leading_zero.json: -------------------------------------------------------------------------------- 1 | [012] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_bad_value.json: -------------------------------------------------------------------------------- 1 | ["x", truth] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_bracket_key.json: -------------------------------------------------------------------------------- 1 | {[: "x"} 2 | -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_comma_instead_of_colon.json: -------------------------------------------------------------------------------- 1 | {"x", null} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_double_colon.json: -------------------------------------------------------------------------------- 1 | {"x"::"b"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_emoji.json: -------------------------------------------------------------------------------- 1 | {🇨🇭} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_garbage_at_end.json: -------------------------------------------------------------------------------- 1 | {"a":"a" 123} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_key_with_single_quotes.json: -------------------------------------------------------------------------------- 1 | {key: 'value'} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_lone_continuation_byte_in_key_and_trailing_comma.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_object_lone_continuation_byte_in_key_and_trailing_comma.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_missing_colon.json: -------------------------------------------------------------------------------- 1 | {"a" b} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_missing_key.json: -------------------------------------------------------------------------------- 1 | {:"b"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_missing_semicolon.json: -------------------------------------------------------------------------------- 1 | {"a" "b"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_missing_value.json: -------------------------------------------------------------------------------- 1 | {"a": -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_no-colon.json: -------------------------------------------------------------------------------- 1 | {"a" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_non_string_key.json: -------------------------------------------------------------------------------- 1 | {1:1} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_non_string_key_but_huge_number_instead.json: -------------------------------------------------------------------------------- 1 | {9999E9999:1} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_repeated_null_null.json: -------------------------------------------------------------------------------- 1 | {null:null,null:null} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_several_trailing_commas.json: -------------------------------------------------------------------------------- 1 | {"id":0,,,,,} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_single_quote.json: -------------------------------------------------------------------------------- 1 | {'a':0} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_trailing_comma.json: -------------------------------------------------------------------------------- 1 | {"id":0,} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_trailing_comment.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/**/ -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_trailing_comment_open.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/**// -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_trailing_comment_slash_open.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}// -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_trailing_comment_slash_open_incomplete.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}/ -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_two_commas_in_a_row.json: -------------------------------------------------------------------------------- 1 | {"a":"b",,"c":"d"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_unquoted_key.json: -------------------------------------------------------------------------------- 1 | {a: "b"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_unterminated-value.json: -------------------------------------------------------------------------------- 1 | {"a":"a -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_with_single_string.json: -------------------------------------------------------------------------------- 1 | { "foo" : "bar", "a" } -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_object_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}# -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_single_space.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_1_surrogate_then_escape.json: -------------------------------------------------------------------------------- 1 | ["\uD800\"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_1_surrogate_then_escape_u.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_1_surrogate_then_escape_u1.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u1"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_1_surrogate_then_escape_u1x.json: -------------------------------------------------------------------------------- 1 | ["\uD800\u1x"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_accentuated_char_no_quotes.json: -------------------------------------------------------------------------------- 1 | [é] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_backslash_00.json: -------------------------------------------------------------------------------- 1 | ["\"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_escape_x.json: -------------------------------------------------------------------------------- 1 | ["\x00"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_escaped_backslash_bad.json: -------------------------------------------------------------------------------- 1 | ["\\\"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_escaped_ctrl_char_tab.json: -------------------------------------------------------------------------------- 1 | ["\ "] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_escaped_emoji.json: -------------------------------------------------------------------------------- 1 | ["\🌀"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_incomplete_escape.json: -------------------------------------------------------------------------------- 1 | ["\"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_incomplete_escaped_character.json: -------------------------------------------------------------------------------- 1 | ["\u00A"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_incomplete_surrogate.json: -------------------------------------------------------------------------------- 1 | ["\uD834\uDd"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_incomplete_surrogate_escape_invalid.json: -------------------------------------------------------------------------------- 1 | ["\uD800\uD800\x"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_invalid-utf-8-in-escape.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_string_invalid-utf-8-in-escape.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_invalid_backslash_esc.json: -------------------------------------------------------------------------------- 1 | ["\a"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_invalid_unicode_escape.json: -------------------------------------------------------------------------------- 1 | ["\uqqqq"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_invalid_utf8_after_escape.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_string_invalid_utf8_after_escape.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_leading_uescaped_thinspace.json: -------------------------------------------------------------------------------- 1 | [\u0020"asd"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_no_quotes_with_bad_escape.json: -------------------------------------------------------------------------------- 1 | [\n] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_single_doublequote.json: -------------------------------------------------------------------------------- 1 | " -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_single_quote.json: -------------------------------------------------------------------------------- 1 | ['single quote'] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_single_string_no_double_quotes.json: -------------------------------------------------------------------------------- 1 | abc -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_start_escape_unclosed.json: -------------------------------------------------------------------------------- 1 | ["\ -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_unescaped_crtl_char.json: -------------------------------------------------------------------------------- 1 | ["aa"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_unescaped_newline.json: -------------------------------------------------------------------------------- 1 | ["new 2 | line"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_unescaped_tab.json: -------------------------------------------------------------------------------- 1 | [" "] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_unicode_CapitalU.json: -------------------------------------------------------------------------------- 1 | "\UA66D" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_string_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | ""x -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_U+2060_word_joined.json: -------------------------------------------------------------------------------- 1 | [⁠] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_UTF8_BOM_no_data.json: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_angle_bracket_..json: -------------------------------------------------------------------------------- 1 | <.> -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_angle_bracket_null.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_array_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | [1]x -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_array_with_extra_array_close.json: -------------------------------------------------------------------------------- 1 | [1]] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_array_with_unclosed_string.json: -------------------------------------------------------------------------------- 1 | ["asd] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_ascii-unicode-identifier.json: -------------------------------------------------------------------------------- 1 | aå -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_capitalized_True.json: -------------------------------------------------------------------------------- 1 | [True] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_close_unopened_array.json: -------------------------------------------------------------------------------- 1 | 1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_comma_instead_of_closing_brace.json: -------------------------------------------------------------------------------- 1 | {"x": true, -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_double_array.json: -------------------------------------------------------------------------------- 1 | [][] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_end_array.json: -------------------------------------------------------------------------------- 1 | ] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_incomplete_UTF8_BOM.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_structure_incomplete_UTF8_BOM.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_lone-invalid-utf-8.json: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_lone-open-bracket.json: -------------------------------------------------------------------------------- 1 | [ -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_no_data.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/parser/src/test/resources/n_structure_no_data.json -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_null-byte-outside-string.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_number_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | 2@ -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_object_followed_by_closing_object.json: -------------------------------------------------------------------------------- 1 | {}} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_object_unclosed_no_value.json: -------------------------------------------------------------------------------- 1 | {"": -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_object_with_comment.json: -------------------------------------------------------------------------------- 1 | {"a":/*comment*/"b"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_object_with_trailing_garbage.json: -------------------------------------------------------------------------------- 1 | {"a": true} "x" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_array_apostrophe.json: -------------------------------------------------------------------------------- 1 | [' -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_array_comma.json: -------------------------------------------------------------------------------- 1 | [, -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_array_open_object.json: -------------------------------------------------------------------------------- 1 | [{ -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_array_open_string.json: -------------------------------------------------------------------------------- 1 | ["a -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_array_string.json: -------------------------------------------------------------------------------- 1 | ["a" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_object.json: -------------------------------------------------------------------------------- 1 | { -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_object_close_array.json: -------------------------------------------------------------------------------- 1 | {] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_object_comma.json: -------------------------------------------------------------------------------- 1 | {, -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_object_open_array.json: -------------------------------------------------------------------------------- 1 | {[ -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_object_open_string.json: -------------------------------------------------------------------------------- 1 | {"a -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_object_string_with_apostrophes.json: -------------------------------------------------------------------------------- 1 | {'a' -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_open_open.json: -------------------------------------------------------------------------------- 1 | ["\{["\{["\{["\{ -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_single_eacute.json: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_single_star.json: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_trailing_#.json: -------------------------------------------------------------------------------- 1 | {"a":"b"}#{} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_uescaped_LF_before_string.json: -------------------------------------------------------------------------------- 1 | [\u000A""] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_unclosed_array.json: -------------------------------------------------------------------------------- 1 | [1 -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_unclosed_array_partial_null.json: -------------------------------------------------------------------------------- 1 | [ false, nul -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_unclosed_array_unfinished_false.json: -------------------------------------------------------------------------------- 1 | [ true, fals -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_unclosed_array_unfinished_true.json: -------------------------------------------------------------------------------- 1 | [ false, tru -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_unclosed_object.json: -------------------------------------------------------------------------------- 1 | {"asd":"asd" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_unicode-identifier.json: -------------------------------------------------------------------------------- 1 | å -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_whitespace_U+2060_word_joiner.json: -------------------------------------------------------------------------------- 1 | [⁠] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/n_structure_whitespace_formfeed.json: -------------------------------------------------------------------------------- 1 | [ ] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_arraysWithSpaces.json: -------------------------------------------------------------------------------- 1 | [[] ] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_empty-string.json: -------------------------------------------------------------------------------- 1 | [""] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_empty.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_ending_with_newline.json: -------------------------------------------------------------------------------- 1 | ["a"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_false.json: -------------------------------------------------------------------------------- 1 | [false] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_heterogeneous.json: -------------------------------------------------------------------------------- 1 | [null, 1, "1", {}] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_null.json: -------------------------------------------------------------------------------- 1 | [null] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_with_1_and_newline.json: -------------------------------------------------------------------------------- 1 | [1 2 | ] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_with_leading_space.json: -------------------------------------------------------------------------------- 1 | [1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_with_several_null.json: -------------------------------------------------------------------------------- 1 | [1,null,null,null,2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_array_with_trailing_space.json: -------------------------------------------------------------------------------- 1 | [2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number.json: -------------------------------------------------------------------------------- 1 | [123e65] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_0e+1.json: -------------------------------------------------------------------------------- 1 | [0e+1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_0e1.json: -------------------------------------------------------------------------------- 1 | [0e1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_after_space.json: -------------------------------------------------------------------------------- 1 | [ 4] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_double_close_to_zero.json: -------------------------------------------------------------------------------- 1 | [-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] 2 | -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_int_with_exp.json: -------------------------------------------------------------------------------- 1 | [20e1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_minus_zero.json: -------------------------------------------------------------------------------- 1 | [-0] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_negative_int.json: -------------------------------------------------------------------------------- 1 | [-123] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_negative_one.json: -------------------------------------------------------------------------------- 1 | [-1] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_negative_zero.json: -------------------------------------------------------------------------------- 1 | [-0] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_real_capital_e.json: -------------------------------------------------------------------------------- 1 | [1E22] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_real_capital_e_neg_exp.json: -------------------------------------------------------------------------------- 1 | [1E-2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_real_capital_e_pos_exp.json: -------------------------------------------------------------------------------- 1 | [1E+2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_real_exponent.json: -------------------------------------------------------------------------------- 1 | [123e45] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_real_fraction_exponent.json: -------------------------------------------------------------------------------- 1 | [123.456e78] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_real_neg_exp.json: -------------------------------------------------------------------------------- 1 | [1e-2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_real_pos_exponent.json: -------------------------------------------------------------------------------- 1 | [1e+2] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_simple_int.json: -------------------------------------------------------------------------------- 1 | [123] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_number_simple_real.json: -------------------------------------------------------------------------------- 1 | [123.456789] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object.json: -------------------------------------------------------------------------------- 1 | {"asd":"sdf", "dfg":"fgh"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_basic.json: -------------------------------------------------------------------------------- 1 | {"asd":"sdf"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_duplicated_key.json: -------------------------------------------------------------------------------- 1 | {"a":"b","a":"c"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_duplicated_key_and_value.json: -------------------------------------------------------------------------------- 1 | {"a":"b","a":"b"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_empty.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_empty_key.json: -------------------------------------------------------------------------------- 1 | {"":0} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_escaped_null_in_key.json: -------------------------------------------------------------------------------- 1 | {"foo\u0000bar": 42} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_extreme_numbers.json: -------------------------------------------------------------------------------- 1 | { "min": -1.0e+28, "max": 1.0e+28 } -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_long_strings.json: -------------------------------------------------------------------------------- 1 | {"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_simple.json: -------------------------------------------------------------------------------- 1 | {"a":[]} -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_string_unicode.json: -------------------------------------------------------------------------------- 1 | {"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_object_with_newlines.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": "b" 3 | } -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_1_2_3_bytes_UTF-8_sequences.json: -------------------------------------------------------------------------------- 1 | ["\u0060\u012a\u12AB"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_accepted_surrogate_pair.json: -------------------------------------------------------------------------------- 1 | ["\uD801\udc37"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_accepted_surrogate_pairs.json: -------------------------------------------------------------------------------- 1 | ["\ud83d\ude39\ud83d\udc8d"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_allowed_escapes.json: -------------------------------------------------------------------------------- 1 | ["\"\\\/\b\f\n\r\t"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_backslash_and_u_escaped_zero.json: -------------------------------------------------------------------------------- 1 | ["\\u0000"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_backslash_doublequotes.json: -------------------------------------------------------------------------------- 1 | ["\""] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_comments.json: -------------------------------------------------------------------------------- 1 | ["a/*b*/c/*d//e"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_double_escape_a.json: -------------------------------------------------------------------------------- 1 | ["\\a"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_double_escape_n.json: -------------------------------------------------------------------------------- 1 | ["\\n"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_escaped_control_character.json: -------------------------------------------------------------------------------- 1 | ["\u0012"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_escaped_noncharacter.json: -------------------------------------------------------------------------------- 1 | ["\uFFFF"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_in_array.json: -------------------------------------------------------------------------------- 1 | ["asd"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_in_array_with_leading_space.json: -------------------------------------------------------------------------------- 1 | [ "asd"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_last_surrogates_1_and_2.json: -------------------------------------------------------------------------------- 1 | ["\uDBFF\uDFFF"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_nbsp_uescaped.json: -------------------------------------------------------------------------------- 1 | ["new\u00A0line"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_nonCharacterInUTF-8_U+10FFFF.json: -------------------------------------------------------------------------------- 1 | ["􏿿"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_nonCharacterInUTF-8_U+FFFF.json: -------------------------------------------------------------------------------- 1 | ["￿"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_null_escape.json: -------------------------------------------------------------------------------- 1 | ["\u0000"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_one-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u002c"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_pi.json: -------------------------------------------------------------------------------- 1 | ["π"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_reservedCharacterInUTF-8_U+1BFFF.json: -------------------------------------------------------------------------------- 1 | ["𛿿"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_simple_ascii.json: -------------------------------------------------------------------------------- 1 | ["asd "] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_space.json: -------------------------------------------------------------------------------- 1 | " " -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json: -------------------------------------------------------------------------------- 1 | ["\uD834\uDd1e"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_three-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u0821"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_two-byte-utf-8.json: -------------------------------------------------------------------------------- 1 | ["\u0123"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_u+2028_line_sep.json: -------------------------------------------------------------------------------- 1 | ["
"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_u+2029_par_sep.json: -------------------------------------------------------------------------------- 1 | ["
"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_uEscape.json: -------------------------------------------------------------------------------- 1 | ["\u0061\u30af\u30EA\u30b9"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_uescaped_newline.json: -------------------------------------------------------------------------------- 1 | ["new\u000Aline"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unescaped_char_delete.json: -------------------------------------------------------------------------------- 1 | [""] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode.json: -------------------------------------------------------------------------------- 1 | ["\uA66D"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicodeEscapedBackslash.json: -------------------------------------------------------------------------------- 1 | ["\u005C"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode_2.json: -------------------------------------------------------------------------------- 1 | ["⍂㈴⍂"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode_U+10FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uDBFF\uDFFE"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode_U+1FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uD83F\uDFFE"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json: -------------------------------------------------------------------------------- 1 | ["\u200B"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode_U+2064_invisible_plus.json: -------------------------------------------------------------------------------- 1 | ["\u2064"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode_U+FDD0_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uFDD0"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode_U+FFFE_nonchar.json: -------------------------------------------------------------------------------- 1 | ["\uFFFE"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_unicode_escaped_double_quote.json: -------------------------------------------------------------------------------- 1 | ["\u0022"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_utf8.json: -------------------------------------------------------------------------------- 1 | ["€𝄞"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_string_with_del_character.json: -------------------------------------------------------------------------------- 1 | ["aa"] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_lonely_false.json: -------------------------------------------------------------------------------- 1 | false -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_lonely_int.json: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_lonely_negative_real.json: -------------------------------------------------------------------------------- 1 | -0.1 -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_lonely_null.json: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_lonely_string.json: -------------------------------------------------------------------------------- 1 | "asd" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_lonely_true.json: -------------------------------------------------------------------------------- 1 | true -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_string_empty.json: -------------------------------------------------------------------------------- 1 | "" -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_trailing_newline.json: -------------------------------------------------------------------------------- 1 | ["a"] 2 | -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_true_in_array.json: -------------------------------------------------------------------------------- 1 | [true] -------------------------------------------------------------------------------- /modules/parser/src/test/resources/y_structure_whitespace_array.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /modules/parser/src/test/scala/com/github/andyglow/json/ParseJsonBulkSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.json 2 | 3 | import scala.io.Source 4 | import scala.util.{Failure, Success} 5 | import org.scalatest.funsuite.AnyFunSuite 6 | 7 | class ParseJsonBulkSpec extends AnyFunSuite { 8 | 9 | private lazy val clazz = classOf[ParseJsonBulkSpec] 10 | 11 | private lazy val CI_WS_PATH = { 12 | def env[T](x: String)(fn: String => T): Option[T] = { 13 | sys.env 14 | .get(x) 15 | .flatMap { x => 16 | try Some(fn(x)) 17 | catch { case _: Throwable => None } 18 | } 19 | } 20 | def CI = env("CI")(_.toBoolean) getOrElse false 21 | def DRONE = env("DRONE")(_.toBoolean) getOrElse false 22 | def DRONE_WORKSPACE_PATH = env("DRONE_WORKSPACE_PATH")( 23 | identity 24 | ) // custom env var according to https://docs.drone.io/pipeline/environment/reference/ 25 | 26 | if (CI & DRONE) { 27 | DRONE_WORKSPACE_PATH orElse Some("/drone/src") 28 | } else None 29 | } 30 | 31 | // for running in parallel in drone 32 | private def open(resourcePath: String, attempt: Int = 0): Source = { 33 | Thread.sleep(attempt * 1000L) 34 | try { 35 | CI_WS_PATH 36 | .map(_ + "/modules/parser/src/test/resources" + resourcePath) 37 | .map(Source.fromFile) 38 | .getOrElse(Source.fromInputStream(clazz.getResourceAsStream(resourcePath))) 39 | } catch { 40 | case _: Throwable if attempt < 5 => 41 | open(resourcePath, attempt + 1) 42 | } 43 | } 44 | 45 | lazy val examples = open("/examples") 46 | 47 | examples.getLines() foreach { name => 48 | def run(expect: Boolean) = { 49 | val json = open("/" + name).mkString 50 | ParseJson(json) match { 51 | case Success(x) if !expect => fail(json + " should have fail, but: " + x) 52 | case Failure(x) if expect => fail(json + " should have succeed", x) 53 | case Success(_) => 54 | case Failure(_) => 55 | } 56 | } 57 | 58 | if (name.startsWith("i_") || name.startsWith("y_")) 59 | ignore(name + " should succeed") { run(true) } 60 | else 61 | ignore(name + " should fail") { run(false) } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /modules/play-json/src/test/scala/com/github/andyglow/jsonschema/Person.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import play.api.libs.json.JsObject 4 | 5 | case class Person(id: String, name: String, metadata: JsObject) 6 | -------------------------------------------------------------------------------- /modules/play-json/src/test/scala/com/github/andyglow/jsonschema/UserProfileJson.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.jsonschema.model._ 4 | import play.api.libs.json._ 5 | 6 | object UserProfileJson { 7 | 8 | implicit val CredentialsW: Writes[Credentials] = Json.writes[Credentials] 9 | 10 | implicit val NotesW: Writes[Notes] = Json.writes[Notes] 11 | 12 | implicit val BetaFeatureW: Writes[BetaFeature] = new Writes[BetaFeature] { 13 | override def writes(o: BetaFeature): JsValue = o match { 14 | case F0 => JsString("feature-0-name") 15 | case F1 => JsString("feature-1-name") 16 | case F2 => JsString("feature-2-name") 17 | } 18 | } 19 | 20 | implicit val RoleW: Writes[Role] = new Writes[Role] { 21 | import Role._ 22 | 23 | override def writes(o: Role): JsValue = o match { 24 | case User => JsString("e-user") 25 | case Manager => JsString("e-manager") 26 | case Admin => JsString("e-admin") 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /modules/refined/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyglow/scala-jsonschema/91c9c09f4ecf8cf4921062384310b333a832a0ee/modules/refined/README.md -------------------------------------------------------------------------------- /modules/refined/TODO.md: -------------------------------------------------------------------------------- 1 | - const type => https://json-schema.org/understanding-json-schema/reference/generic.html#constant-values -------------------------------------------------------------------------------- /modules/refined/src/main/scala/com/github/andyglow/jsonschema/RefinedSupport.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.jsonschema.refined.RefinedMacro 4 | import eu.timepit.refined.api.Refined 5 | import scala.language.experimental.macros 6 | 7 | object RefinedSupport { 8 | 9 | implicit def refinedJsonSchema[A, B]: json.schema.Predef[Refined[A, B]] = 10 | macro RefinedMacro.forTypeParams[A, B] 11 | 12 | def refined[T <: Refined[_, _]]: json.Schema[T] = macro RefinedMacro.forRefinedType[T] 13 | } 14 | -------------------------------------------------------------------------------- /modules/refined/src/main/scala/com/github/andyglow/jsonschema/refined/HasContext.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | import scala.reflect.macros.blackbox 4 | 5 | private[jsonschema] trait HasContext { 6 | 7 | val c: blackbox.Context 8 | 9 | } 10 | -------------------------------------------------------------------------------- /modules/refined/src/main/scala/com/github/andyglow/jsonschema/refined/HasLog.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | private[jsonschema] trait HasLog { this: HasContext => 4 | val debugEnabled = false 5 | 6 | val dbg: String => Unit = 7 | if (debugEnabled) c.info(c.enclosingPosition, _, force = true) else _ => () 8 | 9 | val warn: String => Unit = c.warning(c.enclosingPosition, _) 10 | 11 | val err: String => Nothing = c.abort(c.enclosingPosition, _) 12 | } 13 | -------------------------------------------------------------------------------- /modules/refined/src/main/scala/com/github/andyglow/jsonschema/refined/Logic.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | import scala.reflect.macros.blackbox 4 | 5 | private[jsonschema] trait Logic extends Extractors with HasLog with AST with Math with HasContext { 6 | import refined._ 7 | 8 | val c: blackbox.Context 9 | import c.universe._ 10 | 11 | def gen(t: Type): Tree = { 12 | 13 | t match { 14 | case R(t, p) => 15 | (t, p) match { 16 | case P(pp) => pp.norm.tree 17 | case _ => warn(s"Can't infer Predicate out of ${showRaw(p)}"); EmptyTree 18 | } 19 | case _ => warn(s"Can't infer Refined out of ${showRaw(t)}"); EmptyTree 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /modules/refined/src/main/scala/com/github/andyglow/jsonschema/refined/Math.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | private[jsonschema] trait Math { this: HasLog => 4 | 5 | object math { 6 | 7 | def min(l: Any, r: Any): Any = (l, r) match { 8 | case (l: Byte, r: Byte) => l min r 9 | case (l: Short, r: Short) => l min r 10 | case (l: Int, r: Int) => l min r 11 | case (l: Long, r: Long) => l min r 12 | case (l: Double, r: Double) => l min r 13 | case (l: Float, r: Float) => l min r 14 | case (l: BigDecimal, r: BigDecimal) => l min r 15 | case (l: BigInt, r: BigInt) => l min r 16 | case (l: Number, r: Number) => if (l.doubleValue() <= r.doubleValue()) l else r 17 | case _ => err(s"Can't find minimal out of $l and $r") 18 | } 19 | 20 | def max(l: Any, r: Any): Any = (l, r) match { 21 | case (l: Byte, r: Byte) => l max r 22 | case (l: Short, r: Short) => l max r 23 | case (l: Int, r: Int) => l max r 24 | case (l: Long, r: Long) => l max r 25 | case (l: Double, r: Double) => l max r 26 | case (l: Float, r: Float) => l max r 27 | case (l: BigDecimal, r: BigDecimal) => l max r 28 | case (l: BigInt, r: BigInt) => l max r 29 | case (l: Number, r: Number) => if (l.doubleValue() >= r.doubleValue()) l else r 30 | case _ => err(s"Can't find maximal out of $l and $r") 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /modules/refined/src/main/scala/com/github/andyglow/jsonschema/refined/RefinedMacro.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | import eu.timepit.refined.api.Refined 4 | import scala.reflect.macros.blackbox 5 | 6 | class RefinedMacro(val c: blackbox.Context) extends Logic { 7 | import c.universe._ 8 | 9 | def forTypeParams[A, B](implicit 10 | a: c.WeakTypeTag[A], 11 | b: c.WeakTypeTag[B] 12 | ): c.Expr[json.schema.Predef[Refined[A, B]]] = { 13 | val t = typeOf[eu.timepit.refined.api.Refined[_, _]] 14 | val tt = appliedType(t.typeConstructor, List(a.tpe, b.tpe)) 15 | val tree = forRefinedType(c.WeakTypeTag(tt)).tree 16 | 17 | c.Expr[json.schema.Predef[Refined[A, B]]](q"json.schema.Predef($tree)") 18 | } 19 | 20 | def forRefinedType[T](implicit t: c.WeakTypeTag[T]): c.Expr[json.Schema[T]] = { 21 | import c.universe._ 22 | val tt = t.tpe.dealias 23 | 24 | dbg("\n---------------\n" + showRaw(tt) + "\n---------------") 25 | 26 | val tree = gen(tt) 27 | 28 | dbg(showCode(tree)) 29 | 30 | c.Expr[json.Schema[T]](q""" 31 | import json.Schema._ 32 | import `string`._ 33 | import Format._ 34 | import json.schema.validation.Instance._ 35 | 36 | $tree.asInstanceOf[json.Schema[$tt]] 37 | """) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/refined/src/test/scala/com.github.andyglow.jsonschema.refined/FsExample.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | import com.github.andyglow.json.JsonFormatter 4 | import com.github.andyglow.jsonschema.AsValue 5 | 6 | import json.Json 7 | import json.schema._ 8 | 9 | import com.github.andyglow.jsonschema.RefinedSupport._ 10 | 11 | import eu.timepit.refined._ 12 | import eu.timepit.refined.api._ 13 | import eu.timepit.refined.boolean._ 14 | import eu.timepit.refined.collection._ 15 | import eu.timepit.refined.numeric._ 16 | import eu.timepit.refined.string._ 17 | 18 | object FsExample { 19 | import FsExampleJsonSchema._ 20 | 21 | def main(args: Array[String]): Unit = println { 22 | fsSchema.stringify(Version.Draft07(id = "http://models.org/example.json")) 23 | } 24 | } 25 | 26 | object FsExampleMsg { 27 | 28 | sealed trait FsType 29 | object FsType { 30 | case object ext3 extends FsType 31 | case object ext4 extends FsType 32 | case object btrfs extends FsType 33 | } 34 | 35 | sealed trait DiskType 36 | object DiskType { 37 | case object disk extends DiskType 38 | case object nfs extends DiskType 39 | case object tmpfs extends DiskType 40 | } 41 | 42 | @discriminator sealed trait Storage 43 | object Storage { 44 | // format: off 45 | @definition("diskDevice") @discriminatorKey("disk") final case class DiskDevice(device: String Refined MatchesRegex[W.`"^/dev/[^/]+(/[^/]+)*$"`.T]) extends Storage 46 | @definition("diskUUID") @discriminatorKey("disk") final case class DiskUUID(label: java.util.UUID) extends Storage 47 | @definition("nfs") @discriminatorKey("nfs") final case class NFS(remotePath: String Refined MatchesRegex[W.`"^(/[^/]+)+$"`.T], server: String) extends Storage 48 | @definition("tmpfs") @discriminatorKey("tmpfs") final case class TmpFS(sizeInMB: Refined[Int, GreaterEqual[W.`16`.T] And LessEqual[W.`512`.T]]) extends Storage 49 | // format: on 50 | } 51 | 52 | case class FS( 53 | storage: Storage, 54 | fstype: Option[FsType], 55 | readonly: Option[Boolean], 56 | options: Option[Set[String] Refined MinSize[W.`1`.T]] 57 | ) 58 | 59 | } 60 | 61 | object FsExampleJsonSchema { 62 | import FsExampleMsg._ 63 | 64 | val fsSchema: json.Schema[FS] = Json.schema[FS] 65 | } 66 | -------------------------------------------------------------------------------- /modules/refined/src/test/scala/com.github.andyglow.jsonschema.refined/RefinedAliasedSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | import eu.timepit.refined.api.Refined 4 | import eu.timepit.refined.boolean.Or 5 | import eu.timepit.refined.{string => S} 6 | import json.Json.schema 7 | import com.github.andyglow.jsonschema.RefinedSupport._ 8 | import org.scalatest.matchers.should.Matchers._ 9 | import org.scalatest.funsuite._ 10 | import json.Schema._ 11 | import json.Schema.`string`.Format 12 | 13 | class RefinedAliasedSpec extends AnyFunSuite { 14 | 15 | test("partially aliased") { 16 | type IP = S.IPv4 Or S.IPv6 17 | 18 | schema[Refined[String, IP]] shouldBe `oneof`.of( 19 | `string`(Format.`ipv4`), 20 | `string`(Format.`ipv6`) 21 | ) 22 | } 23 | 24 | test("fully aliased") { 25 | type IPFormat = S.IPv4 Or S.IPv6 26 | type IP = String Refined IPFormat 27 | 28 | schema[IP] shouldBe `oneof`.of(`string`(Format.`ipv4`), `string`(Format.`ipv6`)) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /modules/refined/src/test/scala/com.github.andyglow.jsonschema.refined/RefinedCollectionsSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | import eu.timepit.refined.api._ 4 | import eu.timepit.refined.boolean._ 5 | import eu.timepit.refined.collection._ 6 | import eu.timepit.refined._ 7 | import json.Json.schema 8 | import json.Schema._ 9 | import json.schema.validation.Instance._ 10 | import org.scalatest.matchers.should.Matchers._ 11 | import org.scalatest.funsuite._ 12 | 13 | import com.github.andyglow.jsonschema.RefinedSupport._ 14 | 15 | class RefinedCollectionsSpec extends AnyFunSuite { 16 | 17 | test("size") { 18 | schema[List[Int] Refined Size[W.`12`.T]] shouldBe `array`[Int, List](`integer`) 19 | .withValidation(`minItems` := 12, `maxItems` := 12) 20 | } 21 | 22 | test("minSize") { 23 | schema[List[Int] Refined MinSize[W.`12`.T]] shouldBe `array`[Int, List](`integer`) 24 | .withValidation(`minItems` := 12) 25 | } 26 | 27 | test("maxSize") { 28 | schema[List[Int] Refined MaxSize[W.`12`.T]] shouldBe `array`[Int, List](`integer`) 29 | .withValidation(`maxItems` := 12) 30 | } 31 | 32 | test("empty") { 33 | schema[List[Int] Refined Empty] shouldBe `array`[Int, List](`integer`) 34 | .withValidation(`minItems` := 0, `maxItems` := 0) 35 | } 36 | 37 | test("non-empty") { 38 | schema[List[Int] Refined Not[Empty]] shouldBe `array`[Int, List](`integer`).withValidation( 39 | `minItems` := 1 40 | ) 41 | schema[List[Int] Refined NonEmpty] shouldBe `array`[Int, List](`integer`).withValidation( 42 | `minItems` := 1 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /modules/refined/src/test/scala/com.github.andyglow.jsonschema.refined/RefinedEscapedSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | import com.github.andyglow.json.JsonFormatter 4 | import com.github.andyglow.jsonschema.AsValue 5 | import eu.timepit.refined.{string => _, _} 6 | import eu.timepit.refined.api._ 7 | import eu.timepit.refined.string._ 8 | import json.Json.schema 9 | import json.schema.validation.Instance._ 10 | import json.Schema._ 11 | import json.schema.Version.Draft04 12 | import org.scalatest.matchers.should.Matchers._ 13 | import org.scalatest.funsuite._ 14 | 15 | import com.github.andyglow.jsonschema.RefinedSupport._ 16 | 17 | class RefinedEscapedSpec extends AnyFunSuite { 18 | 19 | test("JsonFormatter escapes regex specified by matchesRegexp ") { 20 | val res = schema[String Refined MatchesRegex[W.`"""\\d{3}[- ]\\d{3}[- ]\\d{4}"""`.T]] 21 | def d04 = JsonFormatter.format(AsValue.schema(res, Draft04())) 22 | 23 | res shouldBe `string`.withValidation(`pattern` := "\\d{3}[- ]\\d{3}[- ]\\d{4}") 24 | 25 | d04 shouldBe 26 | s"""{ 27 | | "$$schema": "http://json-schema.org/draft-04/schema#", 28 | | "type": "string", 29 | | "pattern": "\\\\d{3}[- ]\\\\d{3}[- ]\\\\d{4}" 30 | |}""".stripMargin 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /modules/refined/src/test/scala/com.github.andyglow.jsonschema.refined/RefinedStringsSpec.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema.refined 2 | 3 | import eu.timepit.refined.{string => _, _} 4 | import eu.timepit.refined.api._ 5 | import eu.timepit.refined.string._ 6 | import eu.timepit.refined.collection._ 7 | import json.Json.schema 8 | import json.Schema._ 9 | import json.schema.validation.Instance._ 10 | import org.scalatest.matchers.should.Matchers._ 11 | import org.scalatest.funsuite._ 12 | 13 | import com.github.andyglow.jsonschema.RefinedSupport._ 14 | 15 | class RefinedStringsSpec extends AnyFunSuite { 16 | import `string`._ 17 | 18 | test("non-empty") { 19 | schema[String Refined NonEmpty] shouldBe `string`.withValidation(`minLength` := 1) 20 | } 21 | 22 | test("trimmed") { 23 | schema[String Refined Trimmed] shouldBe `string`.withValidation( 24 | `pattern` := """^(?!\s)[\S ]*(? JsString("feature-0-name") 15 | case F1 => JsString("feature-1-name") 16 | case F2 => JsString("feature-2-name") 17 | } 18 | } 19 | 20 | implicit val RoleW: JsonWriter[Role] = new JsonWriter[Role] { 21 | import Role._ 22 | 23 | override def write(o: Role): JsValue = o match { 24 | case User => JsString("e-user") 25 | case Manager => JsString("e-manager") 26 | case Admin => JsString("e-admin") 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /modules/u-json/src/test/scala/com/github/andyglow/jsonschema/UserProfileJson.scala: -------------------------------------------------------------------------------- 1 | package com.github.andyglow.jsonschema 2 | 3 | import com.github.andyglow.jsonschema.model._ 4 | import upickle.core.Visitor 5 | import upickle.default._ 6 | 7 | object UserProfileJson { 8 | 9 | // OptionPickler 10 | // https://www.lihaoyi.com/upickle/#uJson 11 | // by default u-json treats scala options as json arrays 12 | // this makes it use standard notion 13 | implicit def OptionWriterOverride[T: Writer]: Writer[Option[T]] = 14 | implicitly[Writer[T]].comap[Option[T]] { 15 | case None => null.asInstanceOf[T] 16 | case Some(x) => x 17 | } 18 | 19 | implicit val CredentialsW: Writer[Credentials] = macroW[Credentials] 20 | 21 | implicit val NotesW: Writer[Notes] = macroW[Notes] 22 | 23 | implicit val BetaFeatureW: Writer[BetaFeature] = new Writer[BetaFeature] { 24 | override def write0[V](out: Visitor[_, V], o: BetaFeature): V = o match { 25 | case F0 => out.visitString("feature-0-name", 0) 26 | case F1 => out.visitString("feature-1-name", 0) 27 | case F2 => out.visitString("feature-2-name", 0) 28 | } 29 | } 30 | 31 | implicit val RoleW: Writer[Role] = new Writer[Role] { 32 | import Role._ 33 | 34 | override def write0[V](out: Visitor[_, V], o: Role): V = o match { 35 | case User => out.visitString("e-user", 0) 36 | case Manager => out.visitString("e-manager", 0) 37 | case Admin => out.visitString("e-admin", 0) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /project/CompilerOptions.scala: -------------------------------------------------------------------------------- 1 | object CompilerOptions { 2 | 3 | private val base = Seq("-encoding", "UTF-8", "-feature", "-unchecked", "-deprecation", "-language:existentials", "-language:higherKinds") 4 | 5 | private val opts211 = base ++ Seq("-Xfuture", "-Yno-adapted-args", "-Ywarn-dead-code", "-Ywarn-numeric-widen", "-Ywarn-value-discard", "-Ywarn-unused") 6 | 7 | private val opts212 = base ++ Seq("-Ywarn-unused:imports,-patvars,-privates,-locals,-implicits", "-Xlint:-unused,_") 8 | 9 | private val opts213 = base ++ Seq("-Ywarn-unused:imports,-patvars,-privates,-locals,-implicits", "-Xsource:2.13") 10 | 11 | def apply(v: ScalaVer): Seq[String] = { 12 | v match { 13 | case ScalaVer._211 => opts211 14 | case ScalaVer._212 => opts212 15 | case ScalaVer._213 => opts213 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /project/CustomGithubActions.scala: -------------------------------------------------------------------------------- 1 | import sbtghactions.GenerativePlugin.autoImport.{UseRef, WorkflowStep} 2 | 3 | object CustomGithubActions { 4 | 5 | lazy val generateCC = WorkflowStep.Sbt( 6 | name = Some("Generate Code Coverage Reports"), 7 | commands = List("clean", "coverage", "test"), 8 | cond = Some(s"matrix.scala == '${ScalaVer._213.full}'") 9 | ) 10 | 11 | lazy val aggregateCC = WorkflowStep.Sbt( 12 | name = Some("Aggregate Code Coverage Report"), 13 | commands = List("coverageAggregate"), 14 | cond = Some(s"matrix.scala == '${ScalaVer._213.full}'") 15 | ) 16 | 17 | lazy val uploadCC = WorkflowStep.Use( 18 | name = Some("Upload Code Coverage Report"), 19 | ref = UseRef.Public("codecov", "codecov-action", "v3"), 20 | cond = Some(s"matrix.scala == '${ScalaVer._213.full}'"), 21 | params = Map( 22 | "token" -> "${{ secrets.CODECOV_TOKEN }}" 23 | ) 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /project/ScalaVer.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | 4 | sealed abstract class ScalaVer(val full: String) 5 | 6 | object ScalaVer { 7 | 8 | case object _211 extends ScalaVer("2.11.12") 9 | 10 | case object _212 extends ScalaVer("2.12.20") 11 | 12 | case object _213 extends ScalaVer("2.13.14") 13 | 14 | val values: Seq[ScalaVer] = Set(_213, _212, _211).toSeq 15 | 16 | val default: ScalaVer = _212 17 | 18 | def fromEnv: Option[ScalaVer] = sys.env.get("SCALA_VER") flatMap fromString 19 | 20 | def fromString(full: String): Option[ScalaVer] = full match { 21 | case x if x startsWith "2.11" => Some(_211) 22 | case x if x startsWith "2.12" => Some(_212) 23 | case x if x startsWith "2.13" => Some(_213) 24 | case _ => None 25 | } 26 | 27 | lazy val scalaV = settingKey[ScalaVer]("Current Scala Version").withRank(KeyRanks.Invisible) 28 | 29 | def settings = Seq( 30 | scalaVersion := (ScalaVer.fromEnv getOrElse ScalaVer.default).full, 31 | crossScalaVersions := ScalaVer.values.map(_.full), 32 | scalaV := ScalaVer.fromString(scalaVersion.value) getOrElse ScalaVer.default 33 | ) 34 | 35 | def settings213 = Seq( 36 | scalaVersion := _213.full, 37 | crossScalaVersions := Seq(_213.full), 38 | scalaV := _213 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.9.1 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scoverage" %% "sbt-scoverage" % "2.0.12") 2 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.11.3") 3 | addSbtPlugin("com.github.sbt" % "sbt-release" % "1.4.0") 4 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") 5 | 6 | // github actions 7 | addSbtPlugin("com.github.sbt" % "sbt-github-actions" % "0.24.0") 8 | 9 | // Format 10 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") 11 | 12 | // Documentation 13 | addSbtPlugin("com.lightbend.paradox" % "sbt-paradox" % "0.10.6") 14 | addSbtPlugin("com.github.sbt" % "sbt-paradox-material-theme" % "0.7.0") 15 | addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.4.1") 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.3") 17 | addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.4") 18 | 19 | ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always 20 | -------------------------------------------------------------------------------- /src/main/paradox/builtin.md: -------------------------------------------------------------------------------- 1 | # Built-in support for Core Types 2 | 3 | - `Boolean` 4 | - `String` 5 | - Numeric 6 | - `Short` 7 | - `Int` 8 | - `Char` 9 | - `Double` 10 | - `Float` 11 | - `Long` 12 | - `BigInt` 13 | - `BigDecimal` 14 | - Date Time 15 | - `java.util.Date` 16 | - `java.sql.Timestamp` 17 | - `java.time.Instant` 18 | - `java.time.LocalDateTime` 19 | - `java.sql.Date` 20 | - `java.time.LocalDate` 21 | - `java.sql.Time` 22 | - `java.time.LocalTime` 23 | - Misc 24 | - `java.util.UUID` 25 | - `java.net.URL` 26 | - `java.net.URI` 27 | - Collections 28 | - String Map (eg. `Map[String, T]`) 29 | - Int Map (eg. `Map[Int, T]`) 30 | - `Iterable[T]` 31 | - Sum types. 32 | - Sealed Trait hierarchy of case objects (enum) 33 | - Sealed Trait hierarchy of case classes (oneof) 34 | - Product types. Case Classes 35 | - Value Classes -------------------------------------------------------------------------------- /src/main/paradox/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## SBT 4 | 5 | ### Main: 6 | 7 | ```scala 8 | libraryDependencies += "com.github.andyglow" %% "scala-jsonschema" % // <-- required 9 | ``` 10 | 11 | ### Modules 12 | 13 | ```scala 14 | libraryDependencies ++= Seq( 15 | "com.github.andyglow" %% "scala-jsonschema-core" % , // <-- transitive 16 | "com.github.andyglow" %% "scala-jsonschema-macros" % % Provided, // <-- transitive 17 | // json bridge. pick one 18 | "com.github.andyglow" %% "scala-jsonschema-play-json" % , // <-- optional 19 | "com.github.andyglow" %% "scala-jsonschema-spray-json" % , // <-- optional 20 | "com.github.andyglow" %% "scala-jsonschema-circe-json" % , // <-- optional 21 | "com.github.andyglow" %% "scala-jsonschema-json4s-json" % , // <-- optional 22 | "com.github.andyglow" %% "scala-jsonschema-ujson" % , // <-- optional 23 | // joda-time support 24 | "com.github.andyglow" %% "scala-jsonschema-joda-time" % , // <-- optional 25 | // cats support 26 | "com.github.andyglow" %% "scala-jsonschema-cats" % , // <-- optional 27 | // refined support 28 | "com.github.andyglow" %% "scala-jsonschema-refined" % , // <-- optional 29 | // zero-dependency json and jsonschema parser 30 | "com.github.andyglow" %% "scala-jsonschema-parser" % // <-- optional 31 | ) 32 | ``` 33 | -------------------------------------------------------------------------------- /src/main/paradox/index.md: -------------------------------------------------------------------------------- 1 | @@@ index 2 | 3 | * [Overview](overview.md) 4 | * [Getting Started](getting-started.md) 5 | * [Builtin](builtin.md) 6 | * [Modules](modules/index.md) 7 | 8 | @@@ -------------------------------------------------------------------------------- /src/main/paradox/modules/cats.md: -------------------------------------------------------------------------------- 1 | # Cats -------------------------------------------------------------------------------- /src/main/paradox/modules/index.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | @@@ index 4 | 5 | * [Json](json.md) 6 | * [Cats](cats.md) 7 | * [Refined](refined.md) 8 | * [Joda-Time](joda-time.md) 9 | 10 | @@@ -------------------------------------------------------------------------------- /src/main/paradox/modules/joda-time.md: -------------------------------------------------------------------------------- 1 | # Joda Time 2 | 3 | Joda Time Support allows you to use joda-time classes within your models. 4 | Here is an example. 5 | 6 | ```scala 7 | import com.github.andyglow.jsonschema.JodaTimeSupport._ 8 | import org.joda.time._ 9 | 10 | case class Event(id: String, timestamp: Instant) 11 | 12 | val eventSchema: Schema[Event] = Json.schema[Event] 13 | 14 | println(JsonFormatter.format(AsValue.schema(eventSchema))) 15 | ``` 16 | results in 17 | ``` 18 | { 19 | "$schema": "http://json-schema.org/draft-04/schema#", 20 | "type": "object", 21 | "additionalProperties": false, 22 | "properties": { 23 | "id": { 24 | "type": "string" 25 | }, 26 | "timestamp": { 27 | "$ref": "#/definitions/org.joda.time.Instant" 28 | } 29 | }, 30 | "required": [ 31 | "id", 32 | "timestamp" 33 | ], 34 | "definitions": { 35 | "org.joda.time.Instant": { 36 | "type": "string", 37 | "format": "date-time" 38 | } 39 | } 40 | } 41 | ``` -------------------------------------------------------------------------------- /src/main/paradox/modules/json.md: -------------------------------------------------------------------------------- 1 | # Json 2 | 3 | The library uses its own Json model _com.github.andyglow.json.Value_ to represent Json Schema as JSON document. 4 | But project contains additionally several modules which could connect it with library of your choice. 5 | 6 | Currently supported: 7 | - Play Json 8 | - Spray Json 9 | - Circe 10 | - Json4s 11 | - uJson 12 | 13 | ## Play Json 14 | Example: 15 | ```scala 16 | import com.github.andyglow.jsonschema.AsPlay._ 17 | import json.schema.Version._ 18 | 19 | import play.api.libs.json._ 20 | 21 | case class Foo(name: String) 22 | 23 | val fooSchema: JsValue = Json.schema[Foo].asPlay(Draft04()) 24 | ``` 25 | 26 | ## Spray Json 27 | Example: 28 | ```scala 29 | import com.github.andyglow.jsonschema.AsSpray._ 30 | import json.schema.Version._ 31 | 32 | import spray.json._ 33 | 34 | case class Foo(name: String) 35 | 36 | val fooSchema: JsValue = Json.schema[Foo].asSpray(Draft04()) 37 | ``` 38 | 39 | ## Circe 40 | Example: 41 | ```scala 42 | import com.github.andyglow.jsonschema.AsCirce._ 43 | import json.schema.Version._ 44 | import io.circe._ 45 | 46 | case class Foo(name: String) 47 | 48 | val fooSchema: Json = Json.schema[Foo].asCirce(Draft04()) 49 | ``` 50 | 51 | ## Json4s 52 | Example: 53 | ```scala 54 | import com.github.andyglow.jsonschema.AsJson4s._ 55 | import json.schema.Version._ 56 | import org.json4s.JsonAST._ 57 | 58 | case class Foo(name: String) 59 | 60 | val fooSchema: JValue = Json.schema[Foo].asJson4s(Draft04()) 61 | ``` 62 | 63 | ## uJson 64 | ```scala 65 | import com.github.andyglow.jsonschema.AsU._ 66 | import json.schema.Version._ 67 | 68 | case class Foo(name: String) 69 | 70 | val fooSchema: ujson.Value = Json.schema[Foo].asU(Draft04()) 71 | ``` -------------------------------------------------------------------------------- /src/main/paradox/modules/refined.md: -------------------------------------------------------------------------------- 1 | # Refined -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | ThisBuild / version := "0.7.12-SNAPSHOT" 2 | --------------------------------------------------------------------------------