From 3f0bd4d7322ff0abf4e5514f0b3c00ac16adac51 Mon Sep 17 00:00:00 2001 From: nhz2 Date: Fri, 3 Oct 2025 13:52:50 -0400 Subject: [PATCH 1/2] Add `writefile` to improve compatibility --- src/JSON.jl | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/json.jl | 15 +++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/JSON.jl b/src/JSON.jl index 2b424d0..c7857c7 100644 --- a/src/JSON.jl +++ b/src/JSON.jl @@ -104,6 +104,67 @@ print(io::IO, obj, indent=nothing) = json(io, obj; pretty=something(indent, 0)) print(a, indent=nothing) = print(stdout, a, indent) @doc (@doc json) print +""" + JSON.writefile(Vector{UInt8}, x; kw...)::Vector{UInt8} + JSON.writefile(io::IO, x; kw...) + JSON.writefile(filename::AbstractString, x; kw...) + +Serialize `x` to JSON format. This function provides the same functionality as [`JSON.json`](@ref) + +The first method returns the JSON output as a `Vector{UInt8}`. +The second method writes JSON output to an `IO` object. +The third method writes JSON output to a file specified by `filename` which will +be created if it does not exist yet or overwritten if it does exist. + +This function is provided for backward compatibility when upgrading from +older versions of JSON.jl where the `json` function signature differed. +For new code, prefer using [`JSON.json`](@ref) directly. + +All methods accept the same keyword arguments as [`JSON.json`](@ref): + +- `omit_null`, `omit_empty`, `allownan`, `jsonlines`, `pretty`, `inline_limit` +- `ninf`, `inf`, `nan`, `float_style`, `float_precision`, `bufsize`, `style` + +See [`JSON.json`](@ref) for detailed documentation of all keyword arguments and more examples. + +# Examples +```julia +# Write to IO +io = IOBuffer() +JSON.writefile(io, Dict("key" => "value")) +String(take!(io)) # "{\"key\":\"value\"}" + +# Write to file +JSON.writefile("output.json", [1, 2, 3]) + +# Get as bytes +bytes = JSON.writefile(Vector{UInt8}, Dict("hello" => "world")) +String(bytes) # "{\"hello\":\"world\"}" +``` +""" +function writefile end + +function writefile(::Type{Vector{UInt8}}, x; pretty::Union{Integer,Bool}=false, kw...) + opts = WriteOptions(; pretty=pretty === true ? 2 : Int(pretty), kw...) + _jsonlines_pretty_check(opts.jsonlines, opts.pretty) + float_style_check(opts.float_style) + y = StructUtils.lower(opts.style, x) + buf = Vector{UInt8}(undef, sizeguess(y)) + pos = json!(buf, 1, y, opts, Any[y], nothing) + resize!(buf, pos - 1) + return buf +end + +function writefile(io::IO, x; kw...) + json(io, x; kw...) +end + +function writefile(filename::AbstractString, x; kw...) + open(filename; write=true) do io + writefile(io, x; kw...) + end +end + @compile_workload begin x = JSON.parse("{\"a\": 1, \"b\": null, \"c\": true, \"d\": false, \"e\": \"\", \"f\": [1,null,true], \"g\": {\"key\": \"value\"}}") json = JSON.json(x) diff --git a/test/json.jl b/test/json.jl index 4707799..1979df4 100644 --- a/test/json.jl +++ b/test/json.jl @@ -210,6 +210,21 @@ end # inline_limit tests @test JSON.json([1, 2]; pretty=2, inline_limit=3) == "[1,2]" @test JSON.json([1, 2, 3]; pretty=2, inline_limit=3) == "[\n 1,\n 2,\n 3\n]" + # writefile + for (obj, kw, output) in [ + ([1, 2, 3], (;), b"[1,2,3]"), + ([1, 2, 3], (;pretty=true), b"[\n 1,\n 2,\n 3\n]"), + (NaN, (;allownan=true), b"NaN"), + (NaN, (;allownan=true, nan="different nan string"), b"different nan string"), + ] + @test JSON.writefile(Vector{UInt8}, obj; kw...) == output + io = IOBuffer() + @test JSON.writefile(io, obj; kw...) === nothing + @test take!(io) == output + fname = tempname() + @test JSON.writefile(fname, obj; kw...) === nothing + @test read(fname) == output + end end # non-Integer/AbstractFloat but <: Real output @test_throws MethodError JSON.json(CustomNumber(3.14)) From 05ef959f40c1fe482bd46767641508642e2bf093 Mon Sep 17 00:00:00 2001 From: nhz2 Date: Sun, 9 Nov 2025 16:29:13 -0500 Subject: [PATCH 2/2] Change name to write_json --- src/JSON.jl | 30 ++++++++++++------------------ src/write.jl | 14 +++++++++----- test/json.jl | 8 ++++---- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/JSON.jl b/src/JSON.jl index c7857c7..1448b0f 100644 --- a/src/JSON.jl +++ b/src/JSON.jl @@ -105,9 +105,9 @@ print(a, indent=nothing) = print(stdout, a, indent) @doc (@doc json) print """ - JSON.writefile(Vector{UInt8}, x; kw...)::Vector{UInt8} - JSON.writefile(io::IO, x; kw...) - JSON.writefile(filename::AbstractString, x; kw...) + JSON.write_json(Vector{UInt8}, x; kw...)::Vector{UInt8} + JSON.write_json(io::IO, x; kw...) + JSON.write_json(filename::AbstractString, x; kw...) Serialize `x` to JSON format. This function provides the same functionality as [`JSON.json`](@ref) @@ -120,34 +120,28 @@ This function is provided for backward compatibility when upgrading from older versions of JSON.jl where the `json` function signature differed. For new code, prefer using [`JSON.json`](@ref) directly. -All methods accept the same keyword arguments as [`JSON.json`](@ref): - -- `omit_null`, `omit_empty`, `allownan`, `jsonlines`, `pretty`, `inline_limit` -- `ninf`, `inf`, `nan`, `float_style`, `float_precision`, `bufsize`, `style` - See [`JSON.json`](@ref) for detailed documentation of all keyword arguments and more examples. # Examples ```julia # Write to IO io = IOBuffer() -JSON.writefile(io, Dict("key" => "value")) +JSON.write_json(io, Dict("key" => "value")) String(take!(io)) # "{\"key\":\"value\"}" # Write to file -JSON.writefile("output.json", [1, 2, 3]) +JSON.write_json("output.json", [1, 2, 3]) # Get as bytes -bytes = JSON.writefile(Vector{UInt8}, Dict("hello" => "world")) +bytes = JSON.write_json(Vector{UInt8}, Dict("hello" => "world")) String(bytes) # "{\"hello\":\"world\"}" ``` """ -function writefile end +function write_json end -function writefile(::Type{Vector{UInt8}}, x; pretty::Union{Integer,Bool}=false, kw...) +function write_json(::Type{Vector{UInt8}}, x; pretty::Union{Integer,Bool}=false, kw...) opts = WriteOptions(; pretty=pretty === true ? 2 : Int(pretty), kw...) - _jsonlines_pretty_check(opts.jsonlines, opts.pretty) - float_style_check(opts.float_style) + _write_options_check(opts) y = StructUtils.lower(opts.style, x) buf = Vector{UInt8}(undef, sizeguess(y)) pos = json!(buf, 1, y, opts, Any[y], nothing) @@ -155,13 +149,13 @@ function writefile(::Type{Vector{UInt8}}, x; pretty::Union{Integer,Bool}=false, return buf end -function writefile(io::IO, x; kw...) +function write_json(io::IO, x; kw...) json(io, x; kw...) end -function writefile(filename::AbstractString, x; kw...) +function write_json(filename::AbstractString, x; kw...) open(filename; write=true) do io - writefile(io, x; kw...) + write_json(io, x; kw...) end end diff --git a/src/write.jl b/src/write.jl index 775ab49..353121e 100644 --- a/src/write.jl +++ b/src/write.jl @@ -403,11 +403,17 @@ float_precision_check(fs, fp) = (fs == :shortest || fp > 0) || float_precision_t @noinline _jsonlines_pretty_throw() = throw(ArgumentError("pretty printing is not supported when writing jsonlines")) _jsonlines_pretty_check(jsonlines, pretty) = jsonlines && pretty !== false && !iszero(pretty) && _jsonlines_pretty_throw() -function json(io::IO, x::T; pretty::Union{Integer,Bool}=false, kw...) where {T} - opts = WriteOptions(; pretty=pretty === true ? 2 : Int(pretty), kw...) +# throw an error if opts is not a valid WriteOptions +function _write_options_check(opts::WriteOptions) _jsonlines_pretty_check(opts.jsonlines, opts.pretty) float_style_check(opts.float_style) float_precision_check(opts.float_style, opts.float_precision) + nothing +end + +function json(io::IO, x::T; pretty::Union{Integer,Bool}=false, kw...) where {T} + opts = WriteOptions(; pretty=pretty === true ? 2 : Int(pretty), kw...) + _write_options_check(opts) y = StructUtils.lower(opts.style, x) # Use smaller initial buffer size, limited by bufsize initial_size = min(sizeguess(y), opts.bufsize) @@ -428,9 +434,7 @@ end function json(x; pretty::Union{Integer,Bool}=false, kw...) opts = WriteOptions(; pretty=pretty === true ? 2 : Int(pretty), kw...) - _jsonlines_pretty_check(opts.jsonlines, opts.pretty) - float_style_check(opts.float_style) - float_precision_check(opts.float_style, opts.float_precision) + _write_options_check(opts) y = StructUtils.lower(opts.style, x) buf = stringvec(sizeguess(y)) pos = json!(buf, 1, y, opts, Any[y], nothing) diff --git a/test/json.jl b/test/json.jl index edd1149..15aa175 100644 --- a/test/json.jl +++ b/test/json.jl @@ -210,19 +210,19 @@ end # inline_limit tests @test JSON.json([1, 2]; pretty=2, inline_limit=3) == "[1,2]" @test JSON.json([1, 2, 3]; pretty=2, inline_limit=3) == "[\n 1,\n 2,\n 3\n]" - # writefile + # write_json for (obj, kw, output) in [ ([1, 2, 3], (;), b"[1,2,3]"), ([1, 2, 3], (;pretty=true), b"[\n 1,\n 2,\n 3\n]"), (NaN, (;allownan=true), b"NaN"), (NaN, (;allownan=true, nan="different nan string"), b"different nan string"), ] - @test JSON.writefile(Vector{UInt8}, obj; kw...) == output + @test JSON.write_json(Vector{UInt8}, obj; kw...) == output io = IOBuffer() - @test JSON.writefile(io, obj; kw...) === nothing + @test JSON.write_json(io, obj; kw...) === nothing @test take!(io) == output fname = tempname() - @test JSON.writefile(fname, obj; kw...) === nothing + @test JSON.write_json(fname, obj; kw...) === nothing @test read(fname) == output end end