• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nats-io / json.java / #34

03 Jun 2025 08:10PM UTC coverage: 97.001% (-0.1%) from 97.139%
#34

push

github

web-flow
Merge pull request #13 from nats-io/java21-2

Working on build

744 of 767 relevant lines covered (97.0%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.29
/src/main/java/io/nats/json/JsonWriteUtils.java
1
// Copyright 2020-2024 The NATS Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at:
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
13

14
package io.nats.json;
15

16
import static io.nats.json.DateTimeUtils.DEFAULT_TIME;
17
import static io.nats.json.Encoding.jsonEncode;
18
import static io.nats.json.JsonValueUtils.instance;
19

20
import java.time.Duration;
21
import java.time.ZonedDateTime;
22
import java.util.Arrays;
23
import java.util.List;
24
import java.util.Map;
25

26
public abstract class JsonWriteUtils {
27
    public static final String Q = "\"";
28
    public static final String QCOLONQ = "\":\"";
29
    public static final String QCOLON = "\":";
30
    public static final String QCOMMA = "\",";
31
    public static final String COMMA = ",";
32

33
    private JsonWriteUtils() {} /* ensures cannot be constructed */
34

35
    // ----------------------------------------------------------------------------------------------------
36
    // BUILD A STRING OF JSON
37
    // ----------------------------------------------------------------------------------------------------
38
    public static StringBuilder beginJson() {
39
        return new StringBuilder("{");
1✔
40
    }
41

42
    public static StringBuilder beginArray() {
43
        return new StringBuilder("[");
1✔
44
    }
45

46
    public static StringBuilder beginJsonPrefixed(String prefix) {
47
        return prefix == null ? beginJson()
1✔
48
            : new StringBuilder(prefix).append('{');
1✔
49
    }
50

51
    public static StringBuilder endJson(StringBuilder sb) {
52
        int lastIndex = sb.length() - 1;
1✔
53
        if (sb.charAt(lastIndex) == ',') {
1✔
54
            sb.setCharAt(lastIndex, '}');
1✔
55
            return sb;
1✔
56
        }
57
        sb.append("}");
1✔
58
        return sb;
1✔
59
    }
60

61
    public static StringBuilder endArray(StringBuilder sb) {
62
        int lastIndex = sb.length() - 1;
1✔
63
        if (sb.charAt(lastIndex) == ',') {
1✔
64
            sb.setCharAt(lastIndex, ']');
1✔
65
            return sb;
1✔
66
        }
67
        sb.append("]");
1✔
68
        return sb;
1✔
69
    }
70

71
    public static StringBuilder beginFormattedJson() {
72
        return new StringBuilder("{\n    ");
1✔
73
    }
74

75
    public static String endFormattedJson(StringBuilder sb) {
76
        sb.setLength(sb.length()-1);
1✔
77
        sb.append("\n}");
1✔
78
        return sb.toString().replaceAll(",", ",\n    ");
1✔
79
    }
80

81
    /**
82
     * Appends a json field to a string builder.
83
     * @param sb string builder
84
     * @param fname fieldname
85
     * @param json raw json
86
     */
87
    public static void addRawJson(StringBuilder sb, String fname, String json) {
88
        if (json != null && !json.isEmpty()) {
1✔
89
            sb.append(Q);
1✔
90
            jsonEncode(sb, fname);
1✔
91
            sb.append(QCOLON);
1✔
92
            sb.append(json);
1✔
93
            sb.append(COMMA);
1✔
94
        }
95
    }
1✔
96

97
    /**
98
     * Appends a json field to a string builder.
99
     * @param sb string builder
100
     * @param fname fieldname
101
     * @param value field value
102
     */
103
    public static void addField(StringBuilder sb, String fname, String value) {
104
        if (value != null && !value.isEmpty()) {
1✔
105
            sb.append(Q);
1✔
106
            jsonEncode(sb, fname);
1✔
107
            sb.append(QCOLONQ);
1✔
108
            jsonEncode(sb, value);
1✔
109
            sb.append(QCOMMA);
1✔
110
        }
111
    }
1✔
112

113
    /**
114
     * Appends a json field to a string builder. Empty and null string are added as value of empty string
115
     * @param sb string builder
116
     * @param fname fieldname
117
     * @param value field value
118
     */
119
    public static void addFieldEvenEmpty(StringBuilder sb, String fname, String value) {
120
        if (value == null) {
1✔
121
            value = "";
1✔
122
        }
123
        sb.append(Q);
1✔
124
        jsonEncode(sb, fname);
1✔
125
        sb.append(QCOLONQ);
1✔
126
        jsonEncode(sb, value);
1✔
127
        sb.append(QCOMMA);
1✔
128
    }
1✔
129

130
    /**
131
     * Appends a json field to a string builder.
132
     * @param sb string builder
133
     * @param fname fieldname
134
     * @param value field value
135
     */
136
    public static void addField(StringBuilder sb, String fname, Boolean value) {
137
        if (value != null) {
1✔
138
            sb.append(Q);
1✔
139
            jsonEncode(sb, fname);
1✔
140
            sb.append(QCOLON).append(value ? "true" : "false").append(COMMA);
1✔
141
        }
142
    }
1✔
143

144
    /**
145
     * Appends a json field to a string builder.
146
     * @param sb string builder
147
     * @param fname fieldname
148
     * @param value field value
149
     */
150
    public static void addFldWhenTrue(StringBuilder sb, String fname, Boolean value) {
151
        if (value != null && value) {
1✔
152
            addField(sb, fname, true);
1✔
153
        }
154
    }
1✔
155

156
    /**
157
     * Appends a json field to a string builder.
158
     * @param sb string builder
159
     * @param fname fieldname
160
     * @param value field value
161
     */
162
    public static void addField(StringBuilder sb, String fname, Integer value) {
163
        if (value != null && value >= 0) {
1✔
164
            sb.append(Q);
1✔
165
            jsonEncode(sb, fname);
1✔
166
            sb.append(QCOLON).append(value).append(COMMA);
1✔
167
        }
168
    }
1✔
169

170
    /**
171
     * Appends a json field to a string builder.
172
     * @param sb string builder
173
     * @param fname fieldname
174
     * @param value field value
175
     */
176
    public static void addFieldWhenGtZero(StringBuilder sb, String fname, Integer value) {
177
        if (value != null && value > 0) {
1✔
178
            sb.append(Q);
×
179
            jsonEncode(sb, fname);
×
180
            sb.append(QCOLON).append(value).append(COMMA);
×
181
        }
182
    }
1✔
183

184
    /**
185
     * Appends a json field to a string builder.
186
     * @param sb string builder
187
     * @param fname fieldname
188
     * @param value field value
189
     */
190
    public static void addField(StringBuilder sb, String fname, Long value) {
191
        if (value != null && value >= 0) {
1✔
192
            sb.append(Q);
1✔
193
            jsonEncode(sb, fname);
1✔
194
            sb.append(QCOLON).append(value).append(COMMA);
1✔
195
        }
196
    }
1✔
197

198
    /**
199
     * Appends a json field to a string builder.
200
     * @param sb string builder
201
     * @param fname fieldname
202
     * @param value field value
203
     */
204
    public static void addFieldWhenGtZero(StringBuilder sb, String fname, Long value) {
205
        if (value != null && value > 0) {
1✔
206
            sb.append(Q);
1✔
207
            jsonEncode(sb, fname);
1✔
208
            sb.append(QCOLON).append(value).append(COMMA);
1✔
209
        }
210
    }
1✔
211

212
    /**
213
     * Appends a json field to a string builder.
214
     * @param sb string builder
215
     * @param fname fieldname
216
     * @param value field value
217
     */
218
    public static void addFieldWhenGteMinusOne(StringBuilder sb, String fname, Long value) {
219
        if (value != null && value >= -1) {
1✔
220
            sb.append(Q);
1✔
221
            jsonEncode(sb, fname);
1✔
222
            sb.append(QCOLON).append(value).append(COMMA);
1✔
223
        }
224
    }
1✔
225

226
    /**
227
     * Appends a json field to a string builder.
228
     * @param sb string builder
229
     * @param fname fieldname
230
     * @param value field value
231
     * @param gt the number the value must be greater than
232
     */
233
    public static void addFieldWhenGreaterThan(StringBuilder sb, String fname, Long value, long gt) {
234
        if (value != null && value > gt) {
1✔
235
            sb.append(Q);
1✔
236
            jsonEncode(sb, fname);
1✔
237
            sb.append(QCOLON).append(value).append(COMMA);
1✔
238
        }
239
    }
1✔
240

241
    /**
242
     * Appends a json field to a string builder.
243
     * @param sb string builder
244
     * @param fname fieldname
245
     * @param value duration value
246
     */
247
    public static void addFieldAsNanos(StringBuilder sb, String fname, Duration value) {
248
        if (value != null && !value.isZero() && !value.isNegative()) {
1✔
249
            sb.append(Q);
1✔
250
            jsonEncode(sb, fname);
1✔
251
            sb.append(QCOLON).append(value.toNanos()).append(COMMA);
1✔
252
        }
253
    }
1✔
254

255
    /**
256
     * Appends a json object to a string builder.
257
     * @param sb string builder
258
     * @param fname fieldname
259
     * @param value JsonSerializable value
260
     */
261
    public static void addField(StringBuilder sb, String fname, JsonSerializable value) {
262
        if (value != null) {
1✔
263
            sb.append(Q);
1✔
264
            jsonEncode(sb, fname);
1✔
265
            sb.append(QCOLON).append(value.toJson()).append(COMMA);
1✔
266
        }
267
    }
1✔
268

269
    public static void addField(StringBuilder sb, String fname, Map<String, String> map) {
270
        if (map != null && !map.isEmpty()) {
1✔
271
            addField(sb, fname, instance(map));
1✔
272
        }
273
    }
1✔
274

275
    @SuppressWarnings("rawtypes")
276
    public static void addEnumWhenNot(StringBuilder sb, String fname, Enum e, Enum dontAddIfThis) {
277
        if (e != null && e != dontAddIfThis) {
1✔
278
            addField(sb, fname, e.toString());
1✔
279
        }
280
    }
1✔
281

282
    public interface ListAdder<T> {
283
        void append(StringBuilder sb, T t);
284
    }
285

286
    /**
287
     * Appends a json field to a string builder.
288
     * @param <T> the list type
289
     * @param sb string builder
290
     * @param fname fieldname
291
     * @param list value list
292
     * @param adder implementation to add value, including its quotes if required
293
     */
294
    public static <T> void _addList(StringBuilder sb, String fname, List<T> list, ListAdder<T> adder) {
295
        sb.append(Q);
1✔
296
        jsonEncode(sb, fname);
1✔
297
        sb.append("\":[");
1✔
298
        for (int i = 0; i < list.size(); i++) {
1✔
299
            if (i > 0) {
1✔
300
                sb.append(COMMA);
1✔
301
            }
302
            adder.append(sb, list.get(i));
1✔
303
        }
304
        sb.append("],");
1✔
305
    }
1✔
306

307
    /**
308
     * Appends a json field to a string builder.
309
     * @param sb string builder
310
     * @param fname fieldname
311
     * @param strings field value
312
     */
313
    public static void addStrings(StringBuilder sb, String fname, String[] strings) {
314
        if (strings != null && strings.length > 0) {
1✔
315
            _addStrings(sb, fname, Arrays.asList(strings));
1✔
316
        }
317
    }
1✔
318

319
    /**
320
     * Appends a json field to a string builder.
321
     * @param sb string builder
322
     * @param fname fieldname
323
     * @param strings field value
324
     */
325
    public static void addStrings(StringBuilder sb, String fname, List<String> strings) {
326
        if (strings != null && !strings.isEmpty()) {
1✔
327
            _addStrings(sb, fname, strings);
1✔
328
        }
329
    }
1✔
330

331
    private static void _addStrings(StringBuilder sb, String fname, List<String> strings) {
332
        _addList(sb, fname, strings, (sbs, s) -> {
1✔
333
            sb.append(Q);
1✔
334
            jsonEncode(sb, s);
1✔
335
            sb.append(Q);
1✔
336
        });
1✔
337
    }
1✔
338

339
    /**
340
     * Appends a json field to a string builder.
341
     * @param sb string builder
342
     * @param fname fieldname
343
     * @param jsons field value
344
     */
345
    public static void addJsons(StringBuilder sb, String fname, List<? extends JsonSerializable> jsons) {
346
        if (jsons != null && !jsons.isEmpty()) {
1✔
347
            _addList(sb, fname, jsons, (sbs, s) -> sbs.append(s.toJson()));
1✔
348
        }
349
    }
1✔
350

351
    /**
352
     * Appends a json field to a string builder.
353
     * @param sb string builder
354
     * @param fname fieldname
355
     * @param durations list of durations
356
     */
357
    public static void addDurations(StringBuilder sb, String fname, List<Duration> durations) {
358
        if (durations != null && !durations.isEmpty()) {
1✔
359
            _addList(sb, fname, durations, (sbs, dur) -> sbs.append(dur.toNanos()));
1✔
360
        }
361
    }
1✔
362

363
    /**
364
     * Appends a date/time to a string builder as a rfc 3339 formatted field.
365
     * @param sb string builder
366
     * @param fname fieldname
367
     * @param zonedDateTime field value
368
     */
369
    public static void addField(StringBuilder sb, String fname, ZonedDateTime zonedDateTime) {
370
        if (zonedDateTime != null && !DEFAULT_TIME.equals(zonedDateTime)) {
1✔
371
            sb.append(Q);
1✔
372
            jsonEncode(sb, fname);
1✔
373
            sb.append(QCOLONQ)
1✔
374
                .append(DateTimeUtils.toRfc3339(zonedDateTime))
1✔
375
                .append(QCOMMA);
1✔
376
        }
377
    }
1✔
378

379
    // ----------------------------------------------------------------------------------------------------
380
    // PRINT UTILS
381
    // ----------------------------------------------------------------------------------------------------
382
    public static String toKey(Class<?> c) {
383
        return "\"" + c.getSimpleName() + "\":";
1✔
384
    }
385

386
    private static final String INDENT = "                                        ";
387
    private static String indent(int level) {
388
        return level == 0 ? "" : INDENT.substring(0, level * 4);
1✔
389
    }
390

391
    /**
392
     * This isn't perfect but good enough for debugging
393
     * @param o the object
394
     * @return the formatted string
395
     */
396
    public static String getFormatted(Object o) {
397
        StringBuilder sb = new StringBuilder();
1✔
398
        int level = 0;
1✔
399
        int arrayLevel = 0;
1✔
400
        boolean lastWasClose = false;
1✔
401
        boolean indentNext = true;
1✔
402
        String indent = "";
1✔
403
        String s = o.toString();
1✔
404
        for (int x = 0; x < s.length(); x++) {
1✔
405
            char c = s.charAt(x);
1✔
406
            if (c == '{') {
1✔
407
                if (arrayLevel > 0 && lastWasClose) {
1✔
408
                    sb.append(indent);
1✔
409
                }
410
                sb.append(c).append('\n');
1✔
411
                indent = indent(++level);
1✔
412
                indentNext = true;
1✔
413
                lastWasClose = false;
1✔
414
            }
415
            else if (c == '}') {
1✔
416
                indent = indent(--level);
1✔
417
                sb.append('\n').append(indent).append(c);
1✔
418
                lastWasClose = true;
1✔
419
            }
420
            else if (c == ',') {
1✔
421
                sb.append(",\n");
1✔
422
                indentNext = true;
1✔
423
            }
424
            else {
425
                if (c == '[') {
1✔
426
                    arrayLevel++;
1✔
427
                }
428
                else if (c == ']') {
1✔
429
                    arrayLevel--;
1✔
430
                }
431
                if (indentNext) {
1✔
432
                    if (c != ' ') {
1✔
433
                        sb.append(indent).append(c);
1✔
434
                        indentNext = false;
1✔
435
                    }
436
                }
437
                else {
438
                    sb.append(c);
1✔
439
                }
440
                lastWasClose = lastWasClose && Character.isWhitespace(c);
1✔
441
            }
442
        }
443
        return sb.toString();
1✔
444
    }
445

446
    public static void printFormatted(Object o) {
447
        System.out.println(getFormatted(o));
1✔
448
    }
1✔
449

450
    // ----------------------------------------------------------------------------------------------------
451
    // SAFE NUMBER PARSING HELPERS
452
    // ----------------------------------------------------------------------------------------------------
453
    public static Long safeParseLong(String s) {
454
        try {
455
            return Long.parseLong(s);
1✔
456
        }
457
        catch (Exception e1) {
1✔
458
            try {
459
                return Long.parseUnsignedLong(s);
1✔
460
            }
461
            catch (Exception e2) {
1✔
462
                return null;
1✔
463
            }
464
        }
465
    }
466

467
    public static long safeParseLong(String s, long dflt) {
468
        Long l = safeParseLong(s);
1✔
469
        return l == null ? dflt : l;
1✔
470
    }
471
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc