diff --git a/src/JSON.jl b/src/JSON.jl index 3cb784d..6b9a26f 100644 --- a/src/JSON.jl +++ b/src/JSON.jl @@ -109,6 +109,61 @@ 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.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) + +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. + +See [`JSON.json`](@ref) for detailed documentation of all keyword arguments and more examples. + +# Examples +```julia +# Write to IO +io = IOBuffer() +JSON.write_json(io, Dict("key" => "value")) +String(take!(io)) # "{\"key\":\"value\"}" + +# Write to file +JSON.write_json("output.json", [1, 2, 3]) + +# Get as bytes +bytes = JSON.write_json(Vector{UInt8}, Dict("hello" => "world")) +String(bytes) # "{\"hello\":\"world\"}" +``` +""" +function write_json end + +function write_json(::Type{Vector{UInt8}}, x; pretty::Union{Integer,Bool}=false, kw...) + opts = WriteOptions(; pretty=pretty === true ? 2 : Int(pretty), kw...) + _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) + resize!(buf, pos - 1) + return buf +end + +function write_json(io::IO, x; kw...) + json(io, x; kw...) +end + +function write_json(filename::AbstractString, x; kw...) + open(filename; write=true) do io + write_json(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/src/write.jl b/src/write.jl index b81e766..378c6e8 100644 --- a/src/write.jl +++ b/src/write.jl @@ -404,11 +404,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) @@ -429,9 +435,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 4d33312..8275ca7 100644 --- a/test/json.jl +++ b/test/json.jl @@ -217,6 +217,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]" + # 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.write_json(Vector{UInt8}, obj; kw...) == output + io = IOBuffer() + @test JSON.write_json(io, obj; kw...) === nothing + @test take!(io) == output + fname = tempname() + @test JSON.write_json(fname, obj; kw...) === nothing + @test read(fname) == output + end end # non-Integer/AbstractFloat but <: Real output @test_throws MethodError JSON.json(CustomNumber(3.14))