Skip to content

Write empty Vector{Union{}} as [] instead of {} - #500

Draft
krynju wants to merge 2 commits into
JuliaIO:masterfrom
krynju:kg/fix-499-empty-union-vector
Draft

krynju wants to merge 2 commits into
JuliaIO:masterfrom
krynju:kg/fix-499-empty-union-vector

Conversation

@krynju

@krynju krynju commented Oct 7, 2026

Copy link
Copy Markdown

Fixes #499.

Union{} is a subtype of every type, so Vector{Union{}} <: AbstractVector{<:Pair} holds and empty vectors with element type Union{} dispatched to

StructUtils.arraylike(::JSONStyle, x::AbstractVector{<:Pair}) = false

and were written as JSON objects. This shows up in practice for values parsed by JSON3.jl, which types every empty array as JSON3.Array{Union{}, …}, so a JSON3 → JSON.jl round trip turned [] into {}.

This adds a more specific method:

StructUtils.arraylike(::JSONStyle, ::AbstractVector{Union{}}) = true

Before / after:

julia> JSON.json(Union{}[]), JSON.json([Union{}[]]), JSON.json([:a => 1]), JSON.json(Pair{String,Int}[])
("{}", "[{}]", "{\"a\":1}", "{}")   # before
("[]", "[[]]", "{\"a\":1}", "{}")   # after

Existing Vector{Pair} → object behaviour is unchanged. Added regression tests in test/json.jl.

🤖 Generated with Claude Code

`Union{}` is a subtype of every type, so `Vector{Union{}}` matched the
`arraylike(::JSONStyle, ::AbstractVector{<:Pair}) = false` method and
empty vectors with element type `Union{}` (e.g. JSON3.jl's empty arrays)
were written as JSON objects. Add a more specific `arraylike` method
for `AbstractVector{Union{}}` returning `true`.

Fixes JuliaIO#499

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.58%. Comparing base (4f3dfdc) to head (1e8834e).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #500   +/-   ##
=======================================
  Coverage   92.57%   92.58%           
=======================================
  Files           7        7           
  Lines        1927     1928    +1     
=======================================
+ Hits         1784     1785    +1     
  Misses        143      143           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@krynju

krynju commented Oct 7, 2026 •

Copy link
Copy Markdown
Author

Self-review notes (now rebased: this PR is the minimal Union{} fix plus tests):

  1. Method ambiguity – the new arraylike(::JSONStyle, ::AbstractVector{Union{}}) is ambiguous with StructUtils' arraylike(::StructStyle, ::Selectors.List) for List{Union{}}, the same way the existing AbstractVector{<:Pair} method already is for List{<:Pair}. I first added an arraylike(::JSONStyle, ::List) method here, but writing a List turned out to be broken on master in more ways (List{Any}, which the selector syntax returns, hits an applyeach ambiguity and cannot be written at all; List items skip lower), so that is now its own PR, Write StructUtils.Selectors.List as a JSON array #502, which resolves this ambiguity too. The two PRs are independent.

  2. Test breadth – the motivating case is JSON3.Array{Union{}}, a non-Vector AbstractVector. Tests originally only covered Vector{Union{}}, so narrowing the method to ::Vector{Union{}} would have passed CI while regressing the JSON3 round trip. Added a view(Union{}[], 1:0) case.

  3. Upstream (StructUtils) – the root cause Union{} <: Pair also makes StructUtils' dictlike(::Type{<:AbstractVector{<:Pair}}) return true for Vector{Union{}} while arraylike is true too, so make builds it via the dict path. Harmless in practice (the vector is always empty, and make is lenient about dict-vs-array sources anyway), and independent of this PR. Opened Make Vector{Union{}} arraylike, not dictlike JuliaServices/StructUtils.jl#79 to make the traits agree.

Locally all testsets pass; the trailing segfault in the juliac --trim compile stage reproduces on unmodified master with Julia 1.12.7, so it is unrelated to this change.

The motivating case is JSON3.Array{Union{}}, which is not a Vector, so
pin the AbstractVector breadth of the arraylike method.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@krynju
krynju force-pushed the kg/fix-499-empty-union-vector branch from 12766ae to 1e8834e Compare October 8, 2026 09:38
@krynju
krynju marked this pull request as draft October 8, 2026 09:54

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON.json writes an empty Vector{Union{}} as {} instead of []

1 participant