1
1
defmodule Jason.Formatter do
2
2
@ moduledoc ~S"""
3
- `Jason.Formatter` provides pretty-printing and minimizing functions for
4
- JSON-encoded data.
3
+ Pretty-printing and minimizing functions for JSON-encoded data.
5
4
6
- Input is required to be in an 8-bit-wide encoding such as UTF-8 or Latin-1,
7
- and is accepted in `iodata` (`binary` or `iolist`) format.
8
-
9
- Output is provided in either `binary` or `iolist` format.
5
+ Input is required to be in an 8-bit-wide encoding such as UTF-8 or Latin-1
6
+ in `t:iodata/0` format. Input must ve valid JSON, invalid JSON may produce
7
+ unexpected results or errors.
10
8
"""
11
9
12
10
@ type opts :: [
@@ -20,25 +18,21 @@ defmodule Jason.Formatter do
20
18
defrecordp :opts , [ :indent , :line , :record , :colon ]
21
19
22
20
@ doc ~S"""
23
- Returns a binary containing a pretty-printed representation of
24
- JSON-encoded `iodata`.
21
+ Pretty-prints JSON-encoded `input`.
25
22
26
- `iodata ` may contain multiple JSON objects or arrays, optionally separated
27
- by whitespace (e.g., one object per line). Objects in `pretty_print`ed
28
- output will be separated by newlines. No trailing newline is emitted.
23
+ `input ` may contain multiple JSON objects or arrays, optionally separated
24
+ by whitespace (e.g., one object per line). Objects in output will be
25
+ separated by newlines. No trailing newline is emitted.
29
26
30
- Options:
27
+ ## Options
31
28
32
- * `:indent` sets the indentation string used for nested objects and
33
- arrays. The default indent setting is two spaces (`" "`).
34
- * `:line_separator` sets the newline string used in nested objects.
35
- The default setting is a line feed (`"\n"`).
36
- * `:record_separator` sets the string printed between root-level objects
37
- and arrays. The default setting is `opts[:line_separator]`.
38
- * `:after_colon` sets the string printed after a colon inside objects.
39
- The default setting is one space (`" "`).
29
+ * `:indent` - used for nested objects and arrays (default: two spaces - `" "`);
30
+ * `:line_separator` - used in nested objects (default: `"\n"`);
31
+ * `:record_separator` - separates root-level objects and arrays
32
+ (default is the value for `:line_separator` option);
33
+ * `:after_colon` - printed after a colon inside objects (default: one space - `" "`).
40
34
41
- Example:
35
+ ## Examples
42
36
43
37
iex> Jason.Formatter.pretty_print(~s|{"a":{"b": [1, 2]}}|)
44
38
~s|{
@@ -49,93 +43,96 @@ defmodule Jason.Formatter do
49
43
]
50
44
}
51
45
}|
46
+
52
47
"""
53
48
@ spec pretty_print ( iodata , opts ) :: binary
54
- def pretty_print ( iodata , opts \\ [ ] ) do
55
- iodata
49
+ def pretty_print ( input , opts \\ [ ] ) do
50
+ input
56
51
|> pretty_print_to_iodata ( opts )
57
52
|> IO . iodata_to_binary ( )
58
53
end
59
54
60
55
@ doc ~S"""
61
- Returns an iolist containing a pretty-printed representation of
62
- JSON-encoded `iodata`.
56
+ Pretty-prints JSON-encoded `input` and returns iodata.
63
57
64
- See `pretty_print/2` for details and options.
58
+ This function should be preferred to `pretty_print/2`, if the pretty-printed
59
+ JSON will be handed over to one of the IO functions or sent
60
+ over the socket. The Erlang runtime is able to leverage vectorised
61
+ writes and avoid allocating a continuous buffer for the whole
62
+ resulting string, lowering memory use and increasing performance.
65
63
"""
66
64
@ spec pretty_print_to_iodata ( iodata , opts ) :: iodata
67
- def pretty_print_to_iodata ( iodata , opts \\ [ ] ) do
68
- opts = parse_opts ( opts , opts ( indent: " " , line: "\n " , record: nil , colon: " " ) )
69
- opts = opts ( opts , record: opts ( opts , :record ) || opts ( opts , :line ) )
65
+ def pretty_print_to_iodata ( input , opts \\ [ ] ) do
66
+ opts = parse_opts ( opts , " " , "\n " , nil , " " )
70
67
71
68
depth = :first
72
69
empty = false
73
70
74
- { output , _state } = pp_iodata ( iodata , [ ] , depth , empty , opts )
71
+ { output , _state } = pp_iodata ( input , [ ] , depth , empty , opts )
75
72
76
73
output
77
74
end
78
75
79
76
@ doc ~S"""
80
- Returns a binary containing a minimized representation of
81
- JSON-encoded `iodata`.
77
+ Minimizes JSON-encoded `input`.
82
78
83
- `iodata ` may contain multiple JSON objects or arrays, optionally
84
- separated by whitespace (e.g., one object per line). `minimize`d
85
- output will contain one object per line. No trailing newline is emitted.
79
+ `input ` may contain multiple JSON objects or arrays, optionally
80
+ separated by whitespace (e.g., one object per line). Minimized
81
+ output will contain one object per line. No trailing newline is emitted.
86
82
87
- The `:record_separator` option may be given to control the string
88
- used as newline (default `"\n"`). Other options are ignored.
83
+ ## Options
89
84
90
- Example:
85
+ * `:record_separator` - controls the string used as newline (default: `"\n"`).
86
+
87
+ ## Examples
91
88
92
89
iex> Jason.Formatter.minimize(~s|{ "a" : "b" , "c": \n\n 2}|)
93
90
~s|{"a":"b","c":2}|
91
+
94
92
"""
95
93
@ spec minimize ( iodata , opts ) :: binary
96
- def minimize ( iodata , opts \\ [ ] ) do
97
- iodata
94
+ def minimize ( input , opts \\ [ ] ) do
95
+ input
98
96
|> minimize_to_iodata ( opts )
99
97
|> IO . iodata_to_binary ( )
100
98
end
101
99
102
100
@ doc ~S"""
103
- Returns an iolist containing a minimized representation of
104
- JSON-encoded `iodata`.
101
+ Minimizes JSON-encoded `input` and returns iodata.
105
102
106
- See `minimize/2` for details and options.
103
+ This function should be preferred to `minimize/2`, if the minimized
104
+ JSON will be handed over to one of the IO functions or sent
105
+ over the socket. The Erlang runtime is able to leverage vectorised
106
+ writes and avoid allocating a continuous buffer for the whole
107
+ resulting string, lowering memory use and increasing performance.
107
108
"""
108
109
@ spec minimize_to_iodata ( iodata , opts ) :: iodata
109
- def minimize_to_iodata ( iodata , opts ) do
110
- opts = parse_opts ( opts , opts ( indent: "" , line: "" , record: "\n " , colon: "" ) )
110
+ def minimize_to_iodata ( input , opts ) do
111
+ record = Keyword . get ( opts , :record_separator , "\n " )
112
+ opts = opts ( indent: "" , line: "" , record: record , colon: "" )
111
113
112
114
depth = :first
113
115
empty = false
114
116
115
- { output , _state } = pp_iodata ( iodata , [ ] , depth , empty , opts )
117
+ { output , _state } = pp_iodata ( input , [ ] , depth , empty , opts )
116
118
117
119
output
118
120
end
119
121
120
- defp parse_opts ( opts , defaults ) do
121
- Enum . reduce ( opts , defaults , fn
122
- { :indent , indent } , opts ->
123
- opts ( opts , indent: IO . iodata_to_binary ( indent ) )
124
-
125
- { :line_separator , line } , opts ->
126
- line = IO . iodata_to_binary ( line )
127
- opts ( opts , line: line , record: opts ( opts , :record ) || line )
128
-
129
- { :record_separator , record } , opts ->
130
- opts ( opts , record: IO . iodata_to_binary ( record ) )
122
+ defp parse_opts ( [ { option , value } | opts ] , indent , line , record , colon ) do
123
+ value = IO . iodata_to_binary ( value )
124
+ case option do
125
+ :indent -> parse_opts ( opts , value , line , record , colon )
126
+ :record_separator -> parse_opts ( opts , indent , line , value , colon )
127
+ :after_colon -> parse_opts ( opts , indent , line , record , value )
128
+ :line_separator -> parse_opts ( opts , indent , value , record || value , colon )
129
+ end
130
+ end
131
131
132
- { :after_colon , colon } , opts ->
133
- opts ( opts , colon: IO . iodata_to_binary ( colon ) )
134
- end )
132
+ defp parse_opts ( [ ] , indent , line , record , colon ) do
133
+ opts ( indent: indent , line: line , record: record || line , colon: colon )
135
134
end
136
135
137
- @ spec tab ( String . t ( ) , non_neg_integer ) :: iodata ( )
138
- ## Returns an iolist containing `depth` instances of `opts[:indent]`
139
136
for depth <- 1 .. 16 do
140
137
defp tab ( " " , unquote ( depth ) ) , do: unquote ( String . duplicate ( " " , depth ) )
141
138
end
0 commit comments