From 3f0bd4d7322ff0abf4e5514f0b3c00ac16adac51 Mon Sep 17 00:00:00 2001 From: nhz2 Date: Fri, 3 Oct 2025 13:52:50 -0400 Subject: [PATCH] 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))