Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 27 additions & 32 deletions docs/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ schema = Schema(spec; parent_dir="./schemas")

### 2. `SingleIssue` Type Replaced by `ValidationResult`

The `SingleIssue` type from v1.x has been replaced by `ValidationResult`. For
backwards compatibility, `SingleIssue` is aliased to `ValidationResult`, so
existing `isa` checks will continue to work.
The `SingleIssue` type from v1.x has been removed and replaced by
`ValidationResult`.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit weird to keep this only so isa works. If anyone is constructing them or trying to query field values it will break.


**v1.x:**
```julia
Expand All @@ -56,6 +55,31 @@ if result !== nothing
end
```

### 3. `diagnose` Function

The previously deprecated `diagnose` function has been removed. Use
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. This was from a long time ago.

`validate(schema, data)` instead.

### 4. Inverse Argument Order

The `validate` and `isvalid` functions where `schema` is the second argument
have been removed. `schema` must be the first argument.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto here. There should be only one way to do things.

```julia
validate(data, schema) # old
validate(schema, data) # new

isvalid(data, schema) # old
isvalid(schema, data) # new
```

### 5. `required` Without `properties`

v1.x supported non-standard schemas with `required` field and no `properties`.
In v2.0, you must specify `properties` if `required` is present.
```julia
schema = Schema(Dict("type" => "object", "required" => ["foo"])) # Not allowed
```

## API Compatibility

The following v1.x patterns are fully supported in v2.0:
Expand Down Expand Up @@ -86,42 +110,13 @@ using JSONSchema
isvalid(schema, data) # Returns true or false
```

### `schema.data` Field Access

```julia
schema = Schema(Dict("type" => "object"))
schema.data["type"] # Works - maps to schema.spec
```
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this was ever part of the public API


### Boolean Schemas

```julia
Schema(true) # Accepts everything
Schema(false) # Rejects everything
```

### Inverse Argument Order

```julia
validate(data, schema) # Works - swaps to validate(schema, data)
isvalid(data, schema) # Works - swaps to isvalid(schema, data)
```

### `required` Without `properties`

```julia
schema = Schema(Dict("type" => "object", "required" => ["foo"]))
isvalid(schema, Dict("bar" => 1)) # Returns false (v1.x behavior)
```
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a weird one. The current code you added to work-around was un-tested. I've removed, we can always add it back if someone complains


### `diagnose` Function (Deprecated)

```julia
diagnose(data, schema) # Works but emits deprecation warning
```

Use `validate(schema, data)` instead.

## New Features in v2.0

### Schema Generation from Types
Expand Down
3 changes: 0 additions & 3 deletions src/JSONSchema.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import URIs
using JSON: JSONWriteStyle, Object

export Schema, SchemaContext, ValidationResult, schema, validate
# Backwards compatibility exports (v1.5.0)
export diagnose, SingleIssue

include("schema.jl")
include("compat.jl")

end
88 changes: 0 additions & 88 deletions src/compat.jl

This file was deleted.

50 changes: 22 additions & 28 deletions src/schema.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ end
Schema(spec::AbstractString) = Schema(JSON.parse(spec))
Schema(spec::AbstractVector{UInt8}) = Schema(JSON.parse(spec))

# Boolean schemas are part of the draft6 specification.
function Schema(b::Bool)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if b
# true schema accepts everything - empty schema
return Schema{Any}(Any, Object{String, Any}(), nothing)
else
# false schema rejects everything - use "not: {}" pattern
return Schema{Any}(Any, Object{String, Any}("not" => Object{String, Any}()), nothing)
end
end

# Helper functions for $ref support

"""
Expand Down Expand Up @@ -797,11 +808,6 @@ end
# Also support JSON.Schema (which is an alias for JSONSchema.Schema)
# and inverse argument order for v1.5.0 compatibility
function validate(schema, instance; resolver=nothing)
# Handle inverse argument order (v1.5.0 compat): validate(data, schema)
if instance isa Schema
return validate(instance, schema; resolver=resolver)
end

# Handle JSON.Schema (which is aliased to JSONSchema.Schema)
if typeof(schema).name.module === JSON && hasfield(typeof(schema), :type) && hasfield(typeof(schema), :spec)
return validate(Schema{typeof(schema).parameters[1]}(schema.type, schema.spec, nothing), instance; resolver=resolver)
Expand Down Expand Up @@ -883,11 +889,6 @@ function Base.isvalid(schema::Schema{T}, instance::T; verbose::Bool=false) where
return is_valid
end

# Also support inverse argument order for v1.5.0 compatibility
function Base.isvalid(instance, schema::Schema; verbose::Bool=false)
return Base.isvalid(schema, instance; verbose=verbose)
end

# Internal: Validate an instance against a schema
function _validate_instance(schema_obj, instance, ::Type{T}, path::String, errors::Vector{String}, verbose::Bool, root::Object{String, Any}) where {T}
# Handle $ref - resolve and validate against resolved schema
Expand All @@ -906,30 +907,30 @@ function _validate_instance(schema_obj, instance, ::Type{T}, path::String, error
if isstructtype(T) && isconcretetype(T) && haskey(schema_obj, "properties")
properties = schema_obj["properties"]
required = get(schema_obj, "required", String[])

style = StructUtils.DefaultStyle()
all_field_tags = StructUtils.fieldtags(style, T)

for i in 1:fieldcount(T)
fname = fieldname(T, i)
ftype = fieldtype(T, i)
fvalue = getfield(instance, fname)

# Get field tags
field_tags = haskey(all_field_tags, fname) ? all_field_tags[fname] : nothing
tags = field_tags isa NamedTuple && haskey(field_tags, :json) ? field_tags.json : nothing

# Skip ignored fields
if tags isa NamedTuple && get(tags, :ignore, false)
continue
end

# Get JSON name (may be renamed)
json_name = string(fname)
if tags isa NamedTuple && haskey(tags, :name)
json_name = string(tags.name)
end

# Check if field is in schema
if haskey(properties, json_name)
field_schema = properties[json_name]
Expand Down Expand Up @@ -1176,13 +1177,6 @@ function _validate_value(schema, value, ::Type{T}, tags, path::String, errors::V

# Dict/Object validation (properties, patternProperties, propertyNames for Dicts)
if value isa AbstractDict
# Validate required fields even without properties (v1.5.0 compat)
# This is called from compat.jl and handles the case where "required"
# is specified without "properties"
if !haskey(schema, "properties") && haskey(schema, "required")
_validate_required_for_dict(schema, value, path, errors)
end

# Validate properties for Dict
if haskey(schema, "properties")
properties = schema["properties"]
Expand Down Expand Up @@ -1349,13 +1343,13 @@ function _validate_string(schema, tags, value::String, path::String, errors::Vec
if min_len !== nothing && length(value) < min_len
push!(errors, "$path: string length $(length(value)) is less than minimum $min_len")
end

# Check maxLength
max_len = get(schema, "maxLength", nothing)
if max_len !== nothing && length(value) > max_len
push!(errors, "$path: string length $(length(value)) exceeds maximum $max_len")
end

# Check pattern
pattern = get(schema, "pattern", nothing)
if pattern !== nothing
Expand All @@ -1368,7 +1362,7 @@ function _validate_string(schema, tags, value::String, path::String, errors::Vec
# Invalid regex pattern - skip validation
end
end

# Format validation (basic checks)
format = get(schema, "format", nothing)
if format !== nothing
Expand Down Expand Up @@ -1417,7 +1411,7 @@ function _validate_number(schema, tags, value::Number, path::String, errors::Vec
push!(errors, "$path: value $value is less than minimum $min_val")
end
end

# Check maximum
max_val = get(schema, "maximum", nothing)
exclusive_max = get(schema, "exclusiveMaximum", false)
Expand All @@ -1428,7 +1422,7 @@ function _validate_number(schema, tags, value::Number, path::String, errors::Vec
push!(errors, "$path: value $value exceeds maximum $max_val")
end
end

# Check multipleOf
multiple = get(schema, "multipleOf", nothing)
if multiple !== nothing
Expand Down
Loading