Skip to content

Commit 8cc4467

Browse files
jlbosseJan Lukas Bosse
andauthored
Added indentation options to JSON3.pretty(...) (#222)
* Added a `AlignmentContext` to `JSON3.pretty(...)`s argument list to allow for configurable indentation. This fixes #161 * Changes to be committed: modified: pretty.jl * Fixed #225 Changes to be committed: modified: pretty.jl * Added `AlignmentContext` to the docs Co-authored-by: Jan Lukas Bosse <janlukas.bosse@stud.uni-goettingen.de>
1 parent 11afbb8 commit 8cc4467

3 files changed

Lines changed: 95 additions & 22 deletions

File tree

docs/src/index.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ hello_world = JSON3.read(json_string)
3535
JSON3.pretty(hello_world)
3636
```
3737

38+
The alignment of the string produced by `JSON3.pretty` can be controlled by
39+
passing an [`JSON3.AlignmentContext`](@ref) to `JSON3.pretty`. To align each
40+
level at the `:` Symbol and indent each new level by 2 additional spaces, use
41+
```@example
42+
using JSON3 # hide
43+
json_string = """{"a":"abc","aaaaaaaaaaaaaa":{"a":"abc","aaaaaaaaaaaaaa":"abc"},"c":"abc"}""";
44+
JSON3.pretty(JSON3.read(json_string), JSON3.AlignmentContext(alignment=:Colon, indent=2))
45+
```
46+
to left align the JSON string and indent each new level by 4 additional spaces
47+
(this is also the default) use
48+
```@example
49+
using JSON3 # hide
50+
json_string = """{"a":"abc","aaaaaaaaaaaaaa":{"a":"abc","aaaaaaaaaaaaaa":"abc"},"c":"abc"}""";
51+
JSON3.pretty(JSON3.read(json_string), JSON3.AlignmentContext(alignment=:Left, indent=4))
52+
```
53+
54+
3855
#### Read and write from/to a file
3956
```jl
4057
json_string = read("my_file.json", String)
@@ -161,6 +178,7 @@ JSON3.read!
161178
JSON3.write
162179
JSON3.pretty
163180
JSON3.@pretty
181+
JSON3.AlignmentContext
164182
JSON3.Object
165183
JSON3.Array
166184
Base.copy

src/pretty.jl

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,51 @@ macro pretty(json)
88
end
99

1010
"""
11-
JSON3.pretty(x; kw...)
12-
JSON3.pretty(io, x; kw...)
11+
JSON3.AlignmentContext(alignment=:Left, indent=4, level=0, offset=0)
12+
13+
Specifies the indentation of a pretty JSON string.
14+
15+
## Keyword Args
16+
* `alignment`: A Symbol specifying the alignment type. Can be `:Left` to left-align
17+
everything or `:Colon` to align at the `:`.
18+
* `indent`: The number of spaces to indent each new level with.
19+
* `level`: The indentation level.
20+
* `offset`: The indentation offset.
21+
"""
22+
Base.@kwdef mutable struct AlignmentContext
23+
alignment::Symbol = :Left
24+
indent::UInt16 = 4
25+
level::UInt16 = 0
26+
offset::UInt16 = 0
27+
function AlignmentContext(alignment, indent, level, offset)
28+
if alignment != :Left && alignment != :Colon
29+
throw(ArgumentError("Alignment :$(alignment) is not supported. " *
30+
"Only `:Left` and `:Colon` are supported so far"))
31+
end
32+
new(alignment, indent, level, offset)
33+
end
34+
end
35+
36+
"""
37+
JSON3.pretty(x, ac=JSON3.AlignmentContext(); kw...)
38+
JSON3.pretty(io, x, ac=JSON3.AlignmentContext(); kw...)
1339
1440
Pretty print a JSON string.
1541
1642
## Args
1743
1844
* `x`: A JSON string, or an object to write to JSON then pretty print.
1945
* `io`: The `IO` object to write the pretty printed string to. [default `stdout`]
46+
* `ac`: The `AlignmentContext` for the pretty printing. Defaults to left-aligned
47+
with 4 spaces indent. See [`JSON3.AlignmentContext`](@ref) for more options.
2048
2149
## Keyword Args
2250
2351
See [`JSON3.write`](@ref) and [`JSON3.read`](@ref).
2452
"""
25-
pretty(str; kw...) = pretty(stdout, str; kw...)
26-
pretty(out::IO, x; kw...) = pretty(out, JSON3.write(x; kw...); kw...)
27-
function pretty(out::IO, str::String, indent=0, offset=0; kw...)
53+
pretty(str, ac=AlignmentContext(); kw...) = pretty(stdout, str, ac; kw...)
54+
pretty(out::IO, x, ac=AlignmentContext(); kw...) = pretty(out, JSON3.write(x; kw...), ac; kw...)
55+
function pretty(out::IO, str::String, ac=AlignmentContext(); kw...)
2856
buf = codeunits(str)
2957
len = length(buf)
3058
if len == 0
@@ -40,22 +68,24 @@ function pretty(out::IO, str::String, indent=0, offset=0; kw...)
4068
obj = JSON3.read(str; kw...)
4169

4270
if length(obj) == 0
43-
Base.write(out, "}")
71+
Base.write(out, ' '^(ac.indent * ac.level + ac.offset) * "}")
4472
return
4573
end
4674

4775
ks = collect(keys(obj))
4876
maxlen = maximum(map(sizeof, ks)) + 5
49-
indent += 1
77+
ac.alignment == :Colon && (ac.offset += maxlen)
78+
ac.level += 1
5079

5180
i = 1
5281
for (key, value) in obj
53-
Base.write(out, " "^indent)
54-
Base.write(out, lpad("\"$(key)\"" * ": ", maxlen + offset, ' '))
55-
pretty(out, JSON3.write(value; kw...), indent, maxlen + offset; kw...)
82+
Base.write(out, ' '^(ac.level * ac.indent))
83+
Base.write(out, lpad("\"$(key)\"" * ": ", ac.offset, ' '))
84+
pretty(out, JSON3.write(value; kw...), ac; kw...)
5685
if i == length(obj)
57-
indent -= 1
58-
Base.write(out, "\n" * (" "^indent * " "^offset) * "}")
86+
ac.level -= 1
87+
ac.alignment == :Colon && (ac.offset -= maxlen)
88+
Base.write(out, "\n" * ' '^(ac.indent * ac.level + ac.offset) * "}")
5989
else
6090
Base.write(out, ",\n")
6191
i += 1
@@ -68,18 +98,18 @@ function pretty(out::IO, str::String, indent=0, offset=0; kw...)
6898
arr = JSON3.read(str; kw...)
6999

70100
if length(arr) == 0
71-
Base.write(out, "]")
101+
Base.write(out, ' '^(ac.indent * ac.level + ac.offset) * "]")
72102
return
73103
end
74104

75-
indent += 1
105+
ac.level += 1
76106

77107
for (i, val) in enumerate(arr)
78-
Base.write(out, " "^indent * " "^offset)
79-
pretty(out, JSON3.write(val; kw...), indent, offset; kw...)
108+
Base.write(out, ' '^(ac.indent * ac.level + ac.offset))
109+
pretty(out, JSON3.write(val; kw...), ac; kw...)
80110
if i == length(arr)
81-
indent -= 1
82-
Base.write(out, "\n" * (" "^indent * " "^offset) * "]")
111+
ac.level -= 1
112+
Base.write(out, "\n" * ' '^(ac.indent * ac.level + ac.offset) * "]")
83113
else
84114
Base.write(out, ",\n")
85115
end

test/runtests.jl

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@ JSON3.pretty(io, "{}")
906906
@test String(take!(io)) == "{\n}"
907907
JSON3.pretty(io, "[]")
908908
@test String(take!(io)) == "[\n]"
909-
JSON3.pretty(io, (a=1, b=true, c=3.14, d="hey", e=(abcdefghijklmnopqrstuvwxyz=1000, aa=1e8, dd=[nothing, nothing, nothing, 3.14])))
909+
JSON3.pretty(io, (a=1, b=true, c=3.14, d="hey", e=(abcdefghijklmnopqrstuvwxyz=1000, aa=1e8, dd=[nothing, nothing, nothing, 3.14])), JSON3.AlignmentContext(alignment=:Colon, indent=2))
910910
@test String(take!(io)) == """{
911911
"a": 1,
912912
"b": true,
@@ -923,8 +923,24 @@ JSON3.pretty(io, (a=1, b=true, c=3.14, d="hey", e=(abcdefghijklmnopqrstuvwxyz=10
923923
]
924924
}
925925
}"""
926-
927-
JSON3.pretty(io, (a=1, b=D(1, 3.14, "cool")))
926+
JSON3.pretty(io, (a=1, b=true, c=3.14, d="hey", e=(abcdefghijklmnopqrstuvwxyz=1000, aa=1e8, dd=[nothing, nothing, nothing, 3.14])))
927+
@test String(take!(io)) == """{
928+
"a": 1,
929+
"b": true,
930+
"c": 3.14,
931+
"d": "hey",
932+
"e": {
933+
"abcdefghijklmnopqrstuvwxyz": 1000,
934+
"aa": 100000000,
935+
"dd": [
936+
null,
937+
null,
938+
null,
939+
3.14
940+
]
941+
}
942+
}"""
943+
JSON3.pretty(io, (a=1, b=D(1, 3.14, "cool")), JSON3.AlignmentContext(alignment=:Colon, indent=2))
928944
@test String(take!(io)) == """{
929945
"a": 1,
930946
"b": {
@@ -933,11 +949,20 @@ JSON3.pretty(io, (a=1, b=D(1, 3.14, "cool")))
933949
"c": "cool"
934950
}
935951
}"""
952+
JSON3.pretty(io, (a=1, b=D(1, 3.14, "cool")))
953+
@test String(take!(io)) == """{
954+
"a": 1,
955+
"b": {
956+
"a": 1,
957+
"b": 3.14,
958+
"c": "cool"
959+
}
960+
}"""
936961

937962
# 77
938963
io = IOBuffer()
939964
JSON3.pretty(io, JSON3.write(Dict( "x" => Inf64), allow_inf=true), allow_inf=true )
940-
@test String(take!(io)) == "{\n \"x\": Infinity\n}"
965+
@test String(take!(io)) == "{\n \"x\": Infinity\n}"
941966

942967
end # @testset "pretty.jl"
943968

0 commit comments

Comments
 (0)