Skip to content

OpenAPI nullable flag lost for primitive and array fields in Union{Nothing,T}Β #291

@kahliburke

Description

@kahliburke

Description

Union{Nothing, T} fields on @json structs correctly detect nullability via process_field_type (line 746 of autodoc.jl), and the nullable flag is set on current_field at line 756. However, for primitive and array field types, current_field is then reassigned to a new Dict returned by create_primitive_field_schema (line 779) or create_array_field_schema (line 775), discarding the nullable flag.

The fix from PR #255 works correctly for custom struct $ref fields (where current_field is mutated in-place), but not for primitive or array types.

Reproducer

using Oxygen
using HTTP

@kwdef struct MyRequest
    name::String
    max_items::Union{Nothing, Int} = nothing          # primitive nullable
    tags::Union{Nothing, Vector{String}} = nothing     # array nullable
end

@oxidize

@post "/test" function(req, body::Json{MyRequest})
    return body.payload
end

serve(port=9999, async=true)

# Fetch schema
resp = HTTP.get("http://localhost:9999/docs/schema")
schema = JSON.parse(String(resp.body))
props = schema["components"]["schemas"]["MyRequest"]["properties"]

# Expected: max_items and tags should have "nullable": true
println(props["max_items"])  # {"type": "integer"} β€” missing nullable
println(props["tags"])       # {"type": "array", ...} β€” missing nullable

Root Cause

In convertobject! (src/autodoc.jl):

# Line 755-757: nullable flag is set correctly
if is_nullable
    current_field["nullable"] = true
end

# ...but then at line 775/779, current_field is reassigned to a new Dict:
current_field = create_array_field_schema(...)      # line 775 β€” new Dict, no nullable
current_field = create_primitive_field_schema(...)   # line 779 β€” new Dict, no nullable

Only the $ref path (line 769) preserves nullable because it mutates the existing current_field dict rather than replacing it.

Suggested Fix

Preserve the nullable flag after reassignment:

# After the existing if/elseif chain for struct/array/primitive:
if is_nullable
    current_field["nullable"] = true
end

Or pass is_nullable into the helper functions and have them include it in the returned Dict.

Similarly, default values emitted by create_primitive_field_schema via string(p.default) produce "nothing" for Union{Nothing,T} fields with default=nothing, which is incorrect β€” these should either omit the default or use null.

Environment

  • Oxygen v1.10.0
  • Julia 1.12

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions