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

nats-io / nats.java / #1990

09 Jun 2025 08:59PM UTC coverage: 95.608% (-0.02%) from 95.631%
#1990

push

github

scottf
Better Json Print Formatter

9 of 9 new or added lines in 1 file covered. (100.0%)

8 existing lines in 3 files now uncovered.

11733 of 12272 relevant lines covered (95.61%)

0.96 hits per line

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

97.56
/src/main/java/io/nats/client/support/JsonUtils.java
1
// Copyright 2020 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.client.support;
15

16
import io.nats.client.impl.Headers;
17

18
import java.nio.charset.StandardCharsets;
19
import java.time.Duration;
20
import java.time.ZonedDateTime;
21
import java.util.*;
22
import java.util.function.Consumer;
23
import java.util.function.IntConsumer;
24
import java.util.function.LongConsumer;
25
import java.util.regex.Matcher;
26
import java.util.regex.Pattern;
27

28
import static io.nats.client.support.DateTimeUtils.DEFAULT_TIME;
29
import static io.nats.client.support.Encoding.jsonDecode;
30
import static io.nats.client.support.Encoding.jsonEncode;
31
import static io.nats.client.support.JsonValueUtils.instance;
32
import static io.nats.client.support.NatsConstants.COLON;
33

34
/**
35
 * Internal json parsing helpers.
36
 * Read helpers deprecated Prefer using the {@link JsonParser}
37
 */
38
public abstract class JsonUtils {
39
    public static final String EMPTY_JSON = "{}";
40

41
    private static final String STRING_RE  = "\"(.+?)\"";
42
    private static final String BOOLEAN_RE =  "(true|false)";
43
    private static final String INTEGER_RE =  "(-?\\d+)";
44
    private static final String STRING_ARRAY_RE = "\\[\\s*(\".+?\")\\s*\\]";
45
    private static final String NUMBER_ARRAY_RE = "\\[\\s*(.+?)\\s*\\]";
46
    private static final String BEFORE_FIELD_RE = "\"";
47
    private static final String AFTER_FIELD_RE = "\"\\s*:\\s*";
48

49
    private static final String Q = "\"";
50
    private static final String QCOLONQ = "\":\"";
51
    private static final String QCOLON = "\":";
52
    private static final String QCOMMA = "\",";
53
    private static final String COMMA = ",";
54
    public static final String OPENQ = "{\"";
55
    public static final String CLOSE = "}";
56

57
    private JsonUtils() {} /* ensures cannot be constructed */
58

59
    // ----------------------------------------------------------------------------------------------------
60
    // BUILD A STRING OF JSON
61
    // ----------------------------------------------------------------------------------------------------
62
    public static StringBuilder beginJson() {
63
        return new StringBuilder("{");
1✔
64
    }
65

66
    public static StringBuilder beginArray() {
67
        return new StringBuilder("[");
1✔
68
    }
69

70
    public static StringBuilder beginJsonPrefixed(String prefix) {
71
        return prefix == null ? beginJson()
1✔
72
            : new StringBuilder(prefix).append('{');
1✔
73
    }
74

75
    public static StringBuilder endJson(StringBuilder sb) {
76
        int lastIndex = sb.length() - 1;
1✔
77
        if (sb.charAt(lastIndex) == ',') {
1✔
78
            sb.setCharAt(lastIndex, '}');
1✔
79
            return sb;
1✔
80
        }
81
        sb.append("}");
1✔
82
        return sb;
1✔
83
    }
84

85
    public static StringBuilder endArray(StringBuilder sb) {
86
        int lastIndex = sb.length() - 1;
1✔
87
        if (sb.charAt(lastIndex) == ',') {
1✔
88
            sb.setCharAt(lastIndex, ']');
1✔
89
            return sb;
1✔
90
        }
91
        sb.append("]");
1✔
92
        return sb;
1✔
93
    }
94

95
    public static StringBuilder beginFormattedJson() {
96
        return new StringBuilder("{\n    ");
1✔
97
    }
98

99
    public static String endFormattedJson(StringBuilder sb) {
100
        sb.setLength(sb.length()-1);
1✔
101
        sb.append("\n}");
1✔
102
        return sb.toString().replaceAll(",", ",\n    ");
1✔
103
    }
104

105
    /**
106
     * Appends a json field to a string builder.
107
     * @param sb string builder
108
     * @param fname fieldname
109
     * @param json raw json
110
     */
111
    public static void addRawJson(StringBuilder sb, String fname, String json) {
112
        if (json != null && json.length() > 0) {
1✔
113
            sb.append(Q);
1✔
114
            jsonEncode(sb, fname);
1✔
115
            sb.append(QCOLON);
1✔
116
            sb.append(json);
1✔
117
            sb.append(COMMA);
1✔
118
        }
119
    }
1✔
120

121
    /**
122
     * Appends a json field to a string builder.
123
     * @param sb string builder
124
     * @param fname fieldname
125
     * @param value field value
126
     */
127
    public static void addField(StringBuilder sb, String fname, String value) {
128
        if (value != null && value.length() > 0) {
1✔
129
            sb.append(Q);
1✔
130
            jsonEncode(sb, fname);
1✔
131
            sb.append(QCOLONQ);
1✔
132
            jsonEncode(sb, value);
1✔
133
            sb.append(QCOMMA);
1✔
134
        }
135
    }
1✔
136

137
    /**
138
     * Appends a json field to a string builder. Empty and null string are added as value of empty string
139
     * @param sb string builder
140
     * @param fname fieldname
141
     * @param value field value
142
     */
143
    public static void addFieldEvenEmpty(StringBuilder sb, String fname, String value) {
144
        if (value == null) {
1✔
145
            value = "";
1✔
146
        }
147
        sb.append(Q);
1✔
148
        jsonEncode(sb, fname);
1✔
149
        sb.append(QCOLONQ);
1✔
150
        jsonEncode(sb, value);
1✔
151
        sb.append(QCOMMA);
1✔
152
    }
1✔
153

154
    /**
155
     * Appends a json field to a string builder.
156
     * @param sb string builder
157
     * @param fname fieldname
158
     * @param value field value
159
     */
160
    public static void addField(StringBuilder sb, String fname, Boolean value) {
161
        if (value != null) {
1✔
162
            sb.append(Q);
1✔
163
            jsonEncode(sb, fname);
1✔
164
            sb.append(QCOLON).append(value ? "true" : "false").append(COMMA);
1✔
165
        }
166
    }
1✔
167

168
    /**
169
     * Appends a json field to a string builder.
170
     * @param sb string builder
171
     * @param fname fieldname
172
     * @param value field value
173
     */
174
    public static void addFldWhenTrue(StringBuilder sb, String fname, Boolean value) {
175
        if (value != null && value) {
1✔
176
            addField(sb, fname, true);
1✔
177
        }
178
    }
1✔
179

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

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

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

222
    /**
223
     * Appends a json field to a string builder.
224
     * @param sb string builder
225
     * @param fname fieldname
226
     * @param value field value
227
     */
228
    public static void addFieldWhenGtZero(StringBuilder sb, String fname, Long value) {
229
        if (value != null && value > 0) {
1✔
230
            sb.append(Q);
1✔
231
            jsonEncode(sb, fname);
1✔
232
            sb.append(QCOLON).append(value).append(COMMA);
1✔
233
        }
234
    }
1✔
235

236
    /**
237
     * Appends a json field to a string builder.
238
     * @param sb string builder
239
     * @param fname fieldname
240
     * @param value field value
241
     */
242
    public static void addFieldWhenGteMinusOne(StringBuilder sb, String fname, Long value) {
243
        if (value != null && value >= -1) {
1✔
244
            sb.append(Q);
1✔
245
            jsonEncode(sb, fname);
1✔
246
            sb.append(QCOLON).append(value).append(COMMA);
1✔
247
        }
248
    }
1✔
249

250
    /**
251
     * Appends a json field to a string builder.
252
     * @param sb string builder
253
     * @param fname fieldname
254
     * @param value field value
255
     * @param gt the number the value must be greater than
256
     */
257
    public static void addFieldWhenGreaterThan(StringBuilder sb, String fname, Long value, long gt) {
258
        if (value != null && value > gt) {
1✔
259
            sb.append(Q);
1✔
260
            jsonEncode(sb, fname);
1✔
261
            sb.append(QCOLON).append(value).append(COMMA);
1✔
262
        }
263
    }
1✔
264

265
    /**
266
     * Appends a json field to a string builder.
267
     * @param sb string builder
268
     * @param fname fieldname
269
     * @param value duration value
270
     */
271
    public static void addFieldAsNanos(StringBuilder sb, String fname, Duration value) {
272
        if (value != null && !value.isZero() && !value.isNegative()) {
1✔
273
            sb.append(Q);
1✔
274
            jsonEncode(sb, fname);
1✔
275
            sb.append(QCOLON).append(value.toNanos()).append(COMMA);
1✔
276
        }
277
    }
1✔
278

279
    /**
280
     * Appends a json object to a string builder.
281
     * @param sb string builder
282
     * @param fname fieldname
283
     * @param value JsonSerializable value
284
     */
285
    public static void addField(StringBuilder sb, String fname, JsonSerializable value) {
286
        if (value != null) {
1✔
287
            sb.append(Q);
1✔
288
            jsonEncode(sb, fname);
1✔
289
            sb.append(QCOLON).append(value.toJson()).append(COMMA);
1✔
290
        }
291
    }
1✔
292

293
    public static void addField(StringBuilder sb, String fname, Map<String, String> map) {
294
        if (map != null && map.size() > 0) {
1✔
295
            addField(sb, fname, instance(map));
1✔
296
        }
297
    }
1✔
298

299
    @SuppressWarnings("rawtypes")
300
    public static void addEnumWhenNot(StringBuilder sb, String fname, Enum e, Enum dontAddIfThis) {
301
        if (e != null && e != dontAddIfThis) {
1✔
302
            addField(sb, fname, e.toString());
1✔
303
        }
304
    }
1✔
305

306
    public interface ListAdder<T> {
307
        void append(StringBuilder sb, T t);
308
    }
309

310
    /**
311
     * Appends a json field to a string builder.
312
     * @param <T> the list type
313
     * @param sb string builder
314
     * @param fname fieldname
315
     * @param list value list
316
     * @param adder implementation to add value, including its quotes if required
317
     */
318
    public static <T> void _addList(StringBuilder sb, String fname, List<T> list, ListAdder<T> adder) {
319
        sb.append(Q);
1✔
320
        jsonEncode(sb, fname);
1✔
321
        sb.append("\":[");
1✔
322
        for (int i = 0; i < list.size(); i++) {
1✔
323
            if (i > 0) {
1✔
324
                sb.append(COMMA);
1✔
325
            }
326
            adder.append(sb, list.get(i));
1✔
327
        }
328
        sb.append("],");
1✔
329
    }
1✔
330

331
    /**
332
     * Appends an empty JSON array to a string builder with the specified field name.
333
     * @param sb the string builder to append to
334
     * @param fname the name of the JSON field
335
     */
336
    private static void _addEmptyList(StringBuilder sb, String fname) {
337
        sb.append(Q);
×
338
        jsonEncode(sb, fname);
×
339
        sb.append("\":[],");
×
340
    }
×
341

342
    /**
343
     * Appends a json field to a string builder.
344
     * @param sb string builder
345
     * @param fname fieldname
346
     * @param strings field value
347
     */
348
    public static void addStrings(StringBuilder sb, String fname, String[] strings) {
349
        if (strings != null && strings.length > 0) {
1✔
350
            _addStrings(sb, fname, Arrays.asList(strings));
1✔
351
        }
352
    }
1✔
353

354
    /**
355
     * Appends a json field to a string builder.
356
     * @param sb string builder
357
     * @param fname fieldname
358
     * @param strings field value
359
     */
360
    public static void addStrings(StringBuilder sb, String fname, List<String> strings) {
361
        if (strings != null && strings.size() > 0) {
1✔
362
            _addStrings(sb, fname, strings);
1✔
363
        }
364
    }
1✔
365

366
    private static void _addStrings(StringBuilder sb, String fname, List<String> strings) {
367
        _addList(sb, fname, strings, (sbs, s) -> {
1✔
368
            sb.append(Q);
1✔
369
            jsonEncode(sb, s);
1✔
370
            sb.append(Q);
1✔
371
        });
1✔
372
    }
1✔
373

374
    /**
375
     * Appends a json field to a string builder.
376
     * @param sb string builder
377
     * @param fname fieldname
378
     * @param jsons field value
379
     */
380
    public static void addJsons(StringBuilder sb, String fname, List<? extends JsonSerializable> jsons) {
381
        addJsons(sb, fname, jsons, false);
1✔
382
    }
1✔
383

384
    /**
385
     * Appends a json field to a string builder and the additional flag to indicate if an empty list to be added.
386
     * @param sb string builder
387
     * @param fname fieldname
388
     * @param jsons field value
389
     * @param addEmptyList flag to indicate if an empty list to be added
390
     */
391
    public static void addJsons(StringBuilder sb, String fname, List<? extends JsonSerializable> jsons, boolean addEmptyList) {
392
        if (jsons != null && !jsons.isEmpty()) {
1✔
393
            _addList(sb, fname, jsons, (sbs, s) -> sbs.append(s.toJson()));
1✔
394
        }
395
        else if (addEmptyList) {
1✔
396
            _addEmptyList(sb, fname);
×
397
        }
398
    }
1✔
399

400
    /**
401
     * Appends a json field to a string builder.
402
     * @param sb string builder
403
     * @param fname fieldname
404
     * @param durations list of durations
405
     */
406
    public static void addDurations(StringBuilder sb, String fname, List<Duration> durations) {
407
        if (durations != null && durations.size() > 0) {
1✔
408
            _addList(sb, fname, durations, (sbs, dur) -> sbs.append(dur.toNanos()));
1✔
409
        }
410
    }
1✔
411

412
    /**
413
     * Appends a date/time to a string builder as a rfc 3339 formatted field.
414
     * @param sb string builder
415
     * @param fname fieldname
416
     * @param zonedDateTime field value
417
     */
418
    public static void addField(StringBuilder sb, String fname, ZonedDateTime zonedDateTime) {
419
        if (zonedDateTime != null && !DEFAULT_TIME.equals(zonedDateTime)) {
1✔
420
            sb.append(Q);
1✔
421
            jsonEncode(sb, fname);
1✔
422
            sb.append(QCOLONQ)
1✔
423
                .append(DateTimeUtils.toRfc3339(zonedDateTime))
1✔
424
                .append(QCOMMA);
1✔
425
        }
426
    }
1✔
427

428
    public static void addField(StringBuilder sb, String fname, Headers headers) {
429
        if (headers != null && headers.size() > 0) {
1✔
430
            sb.append(Q);
1✔
431
            jsonEncode(sb, fname);
1✔
432
            sb.append("\":{");
1✔
433
            for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
1✔
434
                addStrings(sb, entry.getKey(), entry.getValue());
1✔
435
            }
1✔
436
            endJson(sb);
1✔
437
            sb.append(",");
1✔
438
        }
439
    }
1✔
440

441
    // ----------------------------------------------------------------------------------------------------
442
    // PRINT UTILS
443
    // ----------------------------------------------------------------------------------------------------
444
    @Deprecated
445
    public static String normalize(String s) {
446
        return Character.toString(s.charAt(0)).toUpperCase() + s.substring(1).toLowerCase();
1✔
447
    }
448

449
    public static String toKey(Class<?> c) {
450
        return "\"" + c.getSimpleName() + "\":";
1✔
451
    }
452

453
    @Deprecated
454
    public static String objectString(String name, Object o) {
455
        if (o == null) {
1✔
456
            return name + "=null";
1✔
457
        }
458
        return o.toString();
1✔
459
    }
460

461
    private static final int INDENT_WIDTH = 4;
462
    private static final String INDENT = "                                        ";
463
    private static String indent(int level) {
464
        return level == 0 ? "" : INDENT.substring(0, level * INDENT_WIDTH);
1✔
465
    }
466

467
    public static String getFormatted(Object o) {
468
        String s = o.toString();
1✔
469
        String newline = System.lineSeparator();
1✔
470

471
        StringBuilder sb = new StringBuilder();
1✔
472
        boolean begin_quotes = false;
1✔
473

474
        boolean opened = false;
1✔
475
        int indentLevel = 0;
1✔
476
        String indent = "";
1✔
477
        for (int x = 0; x < s.length(); x++) {
1✔
478
            char c = s.charAt(x);
1✔
479

480
            if (c == '\"') {
1✔
481
                if (opened) {
1✔
482
                    sb.append(newline).append(indent);
1✔
483
                    opened = false;
1✔
484
                }
485
                sb.append(c);
1✔
486
                begin_quotes = !begin_quotes;
1✔
487
                continue;
1✔
488
            }
489

490
            if (!begin_quotes) {
1✔
491
                switch (c) {
1✔
492
                    case '{':
493
                    case '[':
494
                        sb.append(c);
1✔
495
                        opened = true;
1✔
496
                        indent = indent(++indentLevel);
1✔
497
                        continue;
1✔
498
                    case '}':
499
                    case ']':
500
                        indent = indent(--indentLevel);
1✔
501
                        if (!opened) {
1✔
502
                            sb.append(newline).append(indent);
1✔
503
                        }
504
                        sb.append(c);
1✔
505
                        opened = false;
1✔
506
                        continue;
1✔
507
                    case ':':
508
                        sb.append(c).append(" ");
1✔
509
                        continue;
1✔
510
                    case ',':
511
                        sb.append(c).append(newline).append(indentLevel > 0 ? indent : "");
1✔
512
                        continue;
1✔
513
                    default:
514
                        if (Character.isWhitespace(c)) continue;
1✔
515
                        if (opened) {
1✔
516
                            sb.append(newline).append(indent);
1✔
517
                            opened = false;
1✔
518
                        }
519
                }
520
            }
521

522
            sb.append(c).append(c == '\\' ? "" + s.charAt(++x) : "");
1✔
523
        }
524

525
        return sb.toString();
1✔
526
    }
527

528
    public static void printFormatted(Object o) {
529
        System.out.println(getFormatted(o));
1✔
530
    }
1✔
531

532
    // ----------------------------------------------------------------------------------------------------
533
    // SAFE NUMBER PARSING HELPERS
534
    // ----------------------------------------------------------------------------------------------------
535
    public static Long safeParseLong(String s) {
536
        try {
537
            return Long.parseLong(s);
1✔
538
        }
539
        catch (Exception e1) {
1✔
540
            try {
541
                return Long.parseUnsignedLong(s);
1✔
542
            }
543
            catch (Exception e2) {
1✔
544
                return null;
1✔
545
            }
546
        }
547
    }
548

549
    public static long safeParseLong(String s, long dflt) {
550
        Long l = safeParseLong(s);
1✔
551
        return l == null ? dflt : l;
1✔
552
    }
553

554
    // ----------------------------------------------------------------------------------------------------
555
    // REGEX READING OF JSON. DEPRECATED PREFER USING THE JsonParser
556
    // ----------------------------------------------------------------------------------------------------
557
    @Deprecated
1✔
558
    public enum FieldType {
559
        jsonString(STRING_RE),
1✔
560
        jsonBoolean(BOOLEAN_RE),
1✔
561
        jsonInteger(INTEGER_RE),
1✔
562
        jsonNumber(INTEGER_RE),
1✔
563
        jsonStringArray(STRING_ARRAY_RE);
1✔
564

565
        final String re;
566
        FieldType(String re) {
1✔
567
            this.re = re;
1✔
568
        }
1✔
569
    }
570

571
    @Deprecated
572
    public static Pattern string_pattern(String field) {
573
        return buildPattern(field, STRING_RE);
1✔
574
    }
575

576
    @Deprecated
577
    public static Pattern number_pattern(String field) {
578
        return integer_pattern(field);
1✔
579
    }
580

581
    @Deprecated
582
    public static Pattern integer_pattern(String field) {
583
        return buildPattern(field, INTEGER_RE);
1✔
584
    }
585

586
    @Deprecated
587
    public static Pattern boolean_pattern(String field) {
588
        return buildPattern(field, BOOLEAN_RE);
1✔
589
    }
590

591
    @Deprecated
592
    public static Pattern string_array_pattern(String field) {
593
        return buildPattern(field, STRING_ARRAY_RE);
1✔
594
    }
595

596
    @Deprecated
597
    public static Pattern number_array_pattern(String field) {
598
        return buildPattern(field, NUMBER_ARRAY_RE);
1✔
599
    }
600

601
    /**
602
     * Builds a json parsing pattern
603
     * @param fieldName name of the field
604
     * @param type type of the field.
605
     * @return pattern.
606
     */
607
    @Deprecated
608
    public static Pattern buildPattern(String fieldName, FieldType type) {
609
        return buildPattern(fieldName, type.re);
1✔
610
    }
611

612
    @Deprecated
613
    public static Pattern buildPattern(String fieldName, String typeRE) {
614
        return Pattern.compile(BEFORE_FIELD_RE + fieldName + AFTER_FIELD_RE + typeRE, Pattern.CASE_INSENSITIVE);
1✔
615
    }
616

617
    /**
618
     * Extract a JSON object string by object name. Returns empty object '{}' if not found.
619
     * @param objectName object name
620
     * @param json source json
621
     * @return object json string
622
     */
623
    @Deprecated
624
    public static String getJsonObject(String objectName, String json) {
625
        return getJsonObject(objectName, json, EMPTY_JSON);
1✔
626
    }
627

628
    @Deprecated
629
    public static String getJsonObject(String objectName, String json, String dflt) {
630
        int[] indexes = getBracketIndexes(objectName, json, '{', '}', 0);
1✔
631
        return indexes == null ? dflt : json.substring(indexes[0], indexes[1] + 1);
1✔
632
    }
633

634
    @Deprecated
635
    public static String removeObject(String json, String objectName) {
636
        int[] indexes = getBracketIndexes(objectName, json, '{', '}', 0);
1✔
637
        if (indexes != null) {
1✔
638
            // remove the entire object replacing it with a dummy field b/c getBracketIndexes doesn't consider
639
            // if there is or isn't another object after it, so I don't have to worry about it being/not being the last object
UNCOV
640
            json = json.substring(0, indexes[0]) + "\"rmvd" + objectName.hashCode() + "\":\"\"" + json.substring(indexes[1] + 1);
×
641
        }
642
        return json;
1✔
643
    }
644

645
    /**
646
     * Extract a list JSON object strings for list object name. Returns empty list '{}' if not found.
647
     * Assumes that there are no brackets '{' or '}' in the actual data.
648
     * @param objectName list object name
649
     * @param json source json
650
     * @return object json string
651
     */
652
    @Deprecated
653
    public static List<String> getObjectList(String objectName, String json) {
654
        List<String> items = new ArrayList<>();
1✔
655
        int[] indexes = getBracketIndexes(objectName, json, '[', ']', -1);
1✔
656
        if (indexes != null) {
1✔
657
            StringBuilder item = new StringBuilder();
1✔
658
            int depth = 0;
1✔
659
            for (int x = indexes[0] + 1; x < indexes[1]; x++) {
1✔
660
                char c = json.charAt(x);
1✔
661
                if (c == '{') {
1✔
662
                    item.append(c);
1✔
663
                    depth++;
1✔
664
                } else if (c == '}') {
1✔
665
                    item.append(c);
1✔
666
                    if (--depth == 0) {
1✔
667
                        items.add(item.toString());
1✔
668
                        item.setLength(0);
1✔
669
                    }
670
                } else if (depth > 0) {
1✔
671
                    item.append(c);
1✔
672
                }
673
            }
674
        }
675
        return items;
1✔
676
    }
677

678
    private static int[] getBracketIndexes(String objectName, String json, char start, char end, int fromIndex) {
679
        int[] result = new int[] {-1, -1};
1✔
680
        int objStart = json.indexOf(Q + objectName + Q, fromIndex);
1✔
681
        if (objStart != -1) {
1✔
682
            int startIx;
683
            if (fromIndex != -1) {
1✔
684
                int colonMark = json.indexOf(COLON, objStart) + 1;
1✔
685
                startIx = json.indexOf(start, colonMark);
1✔
686
                for (int x = colonMark; x < startIx; x++) {
1✔
687
                    char c = json.charAt(x);
1✔
688
                    if (!Character.isWhitespace(c)) {
1✔
689
                        return getBracketIndexes(objectName, json, start, end, colonMark);
1✔
690
                    }
691
                }
692
            }
1✔
693
            else {
694
                startIx = json.indexOf(start, objStart);
1✔
695
            }
696
            int depth = 1;
1✔
697
            for (int x = startIx + 1; x < json.length(); x++) {
1✔
698
                char c = json.charAt(x);
1✔
699
                if (c == start) {
1✔
700
                    depth++;
1✔
701
                }
702
                else if (c == end) {
1✔
703
                    if (--depth == 0) {
1✔
704
                        result[0] = startIx;
1✔
705
                        result[1] = x;
1✔
706
                        return result;
1✔
707
                    }
708
                }
709
            }
710
        }
711
        return null;
1✔
712
    }
713

714
    /**
715
     * Get a map of objects
716
     * @param json the json
717
     * @return the map of json object strings by key
718
     */
719
    @Deprecated
720
    public static Map<String, String> getMapOfObjects(String json) {
721
        Map<String, String> map = new HashMap<>();
1✔
722
        int s1 = json.indexOf('"');
1✔
723
        while (s1 != -1) {
1✔
724
            int s2 = json.indexOf('"', s1 + 1);
1✔
725
            String key = json.substring(s1 + 1, s2).trim();
1✔
726
            int[] indexes = getBracketIndexes(key, json, '{', '}', s1);
1✔
727
            if (indexes != null) {
1✔
UNCOV
728
                map.put(key, json.substring(indexes[0], indexes[1] + 1));
×
UNCOV
729
                s1 = json.indexOf('"', indexes[1]);
×
730
            }
731
            else {
732
                s1 = -1;
1✔
733
            }
734
        }
1✔
735

736
        return map;
1✔
737
    }
738

739
    /**
740
     * Get a map of objects
741
     * @param json the json
742
     * @return the map of json object strings by key
743
     */
744
    @Deprecated
745
    public static Map<String, List<String>> getMapOfLists(String json) {
746
        Map<String, List<String>> map = new HashMap<>();
1✔
747
        int s1 = json.indexOf('"');
1✔
748
        while (s1 != -1) {
1✔
749
            int s2 = json.indexOf('"', s1 + 1);
1✔
750
            String key = json.substring(s1 + 1, s2).trim();
1✔
751
            int[] indexes = getBracketIndexes(key, json, '[', ']', s1);
1✔
752
            if (indexes != null) {
1✔
UNCOV
753
                map.put(key, toList(json.substring(indexes[0] + 1, indexes[1])));
×
UNCOV
754
                s1 = json.indexOf('"', indexes[1]);
×
755
            }
756
            else {
757
                s1 = -1;
1✔
758
            }
759
        }
1✔
760

761
        return map;
1✔
762
    }
763

764
    /**
765
     * Get a map of longs
766
     * @param json the json
767
     * @return the map of longs by key
768
     */
769
    @Deprecated
770
    public static Map<String, Long> getMapOfLongs(String json) {
771
        Map<String, Long> map = new HashMap<>();
1✔
772
        int s1 = json.indexOf('"');
1✔
773
        while (s1 != -1) {
1✔
774
            int s2 = json.indexOf('"', s1 + 1);
1✔
775
            int c1 = json.indexOf(':', s2);
1✔
776
            int c2 = json.indexOf(',', s2);
1✔
777
            if (c2 == -1) {
1✔
778
                c2 = json.indexOf('}', s2);
1✔
779
            }
780
            String key = json.substring(s1 + 1, s2).trim();
1✔
781
            long count = safeParseLong(json.substring(c1 + 1, c2).trim(), 0);
1✔
782
            map.put(key, count);
1✔
783
            s1 = json.indexOf('"', c2);
1✔
784
        }
1✔
785
        return map;
1✔
786
    }
787

788
    /**
789
     * Extract a list strings for list object name. Returns empty array if not found.
790
     * Assumes that there are no brackets '{' or '}' in the actual data.
791
     * @deprecated Prefer using the {@link JsonParser}
792
     * @param objectName object name
793
     * @param json source json
794
     * @return a string list, empty if no values are found.
795
     */
796
    @Deprecated
797
    public static List<String> getStringList(String objectName, String json) {
798
        String flat = json.replaceAll("\r", "").replaceAll("\n", "");
1✔
799
        Matcher m = string_array_pattern(objectName).matcher(flat);
1✔
800
        if (m.find()) {
1✔
801
            String arrayString = m.group(1);
1✔
802
            return toList(arrayString);
1✔
803
        }
804
        return new ArrayList<>();
1✔
805
    }
806

807
    private static List<String> toList(String arrayString) {
808
        List<String> list = new ArrayList<>();
1✔
809
        String[] raw = arrayString.split(",");
1✔
810
        for (String s : raw) {
1✔
811
            String cleaned = s.trim().replace("\"", "");
1✔
812
            if (cleaned.length() > 0) {
1✔
813
                list.add(jsonDecode(cleaned));
1✔
814
            }
815
        }
816
        return list;
1✔
817
    }
818

819
    /**
820
     * Extract a list longs for list object name. Returns empty array if not found.
821
     * @deprecated Prefer using the {@link JsonParser}
822
     * @param objectName object name
823
     * @param json source json
824
     * @return a long list, empty if no values are found.
825
     */
826
    @Deprecated
827
    public static List<Long> getLongList(String objectName, String json) {
828
        String flat = json.replaceAll("\r", "").replaceAll("\n", "");
1✔
829
        List<Long> list = new ArrayList<>();
1✔
830
        Matcher m = number_array_pattern(objectName).matcher(flat);
1✔
831
        if (m.find()) {
1✔
832
            String arrayString = m.group(1);
1✔
833
            String[] raw = arrayString.split(",");
1✔
834

835
            for (String s : raw) {
1✔
836
                list.add(safeParseLong(s.trim()));
1✔
837
            }
838
        }
839
        return list;
1✔
840
    }
841

842
    /**
843
     * Extract a list durations for list object name. Returns empty array if not found.
844
     * @param objectName object name
845
     * @param json source json
846
     * @return a duration list, empty if no values are found.
847
     */
848
    @Deprecated
849
    public static List<Duration> getDurationList(String objectName, String json) {
850
        List<Long> longs = getLongList(objectName, json);
1✔
851
        List<Duration> list = new ArrayList<>(longs.size());
1✔
852
        for (Long l : longs) {
1✔
853
            list.add(Duration.ofNanos(l));
1✔
854
        }
1✔
855
        return list;
1✔
856
    }
857

858
    @Deprecated
859
    public static byte[] simpleMessageBody(String name, Number value) {
860
        return (OPENQ + name + QCOLON + value + CLOSE).getBytes();
1✔
861
    }
862

863
    @Deprecated
864
    public static byte[] simpleMessageBody(String name, String value) {
865
        return (OPENQ + name + QCOLONQ + value + Q + CLOSE).getBytes();
1✔
866
    }
867

868
    @Deprecated
869
    public static String readString(String json, Pattern pattern) {
870
        return readString(json, pattern, null);
1✔
871
    }
872

873
    @Deprecated
874
    public static String readString(String json, Pattern pattern, String dflt) {
875
        Matcher m = pattern.matcher(json);
1✔
876
        return m.find() ? jsonDecode(m.group(1)) : dflt;
1✔
877
    }
878

879
    @Deprecated
880
    public static String readStringMayHaveQuotes(String json, String field, String dflt) {
881
        String jfield = "\"" + field + "\"";
1✔
882
        int at = json.indexOf(jfield);
1✔
883
        if (at != -1) {
1✔
884
            at = json.indexOf('"', at + jfield.length());
1✔
885
            StringBuilder sb = new StringBuilder();
1✔
886
            while (true) {
887
                char c = json.charAt(++at);
1✔
888
                if (c == '\\') {
1✔
889
                    char c2 = json.charAt(++at);
1✔
890
                    if (c2 == '"') {
1✔
891
                        sb.append('"');
1✔
892
                    }
893
                    else {
894
                        sb.append(c);
1✔
895
                        sb.append(c2);
1✔
896
                    }
897
                }
1✔
898
                else if (c == '"') {
1✔
899
                    break;
1✔
900
                }
901
                else {
902
                    sb.append(c);
1✔
903
                }
904
            }
1✔
905
            return jsonDecode(sb.toString());
1✔
906
        }
907
        return dflt;
1✔
908
    }
909

910
    @Deprecated
911
    public static byte[] readBytes(String json, Pattern pattern) {
912
        String s = readString(json, pattern, null);
1✔
913
        return s == null ? null : s.getBytes(StandardCharsets.UTF_8);
1✔
914
    }
915

916
    @Deprecated
917
    public static byte[] readBase64(String json, Pattern pattern) {
918
        Matcher m = pattern.matcher(json);
1✔
919
        String b64 = m.find() ? m.group(1) : null;
1✔
920
        return b64 == null ? null : Base64.getDecoder().decode(b64);
1✔
921
    }
922

923
    @Deprecated
924
    public static boolean readBoolean(String json, Pattern pattern) {
925
        Matcher m = pattern.matcher(json);
1✔
926
        return m.find() && Boolean.parseBoolean(m.group(1));
1✔
927
    }
928

929
    @Deprecated
930
    public static Boolean readBoolean(String json, Pattern pattern, Boolean dflt) {
931
        Matcher m = pattern.matcher(json);
1✔
932
        if (m.find()) {
1✔
933
            return Boolean.parseBoolean(m.group(1));
1✔
934
        }
935
        return dflt;
1✔
936
    }
937

938
    @Deprecated
939
    public static Integer readInteger(String json, Pattern pattern) {
940
        Matcher m = pattern.matcher(json);
1✔
941
        return m.find() ? Integer.parseInt(m.group(1)) : null;
1✔
942
    }
943

944
    @Deprecated
945
    public static int readInt(String json, Pattern pattern, int dflt) {
946
        Matcher m = pattern.matcher(json);
1✔
947
        return m.find() ? Integer.parseInt(m.group(1)) : dflt;
1✔
948
    }
949

950
    @Deprecated
951
    public static void readInt(String json, Pattern pattern, IntConsumer c) {
952
        Matcher m = pattern.matcher(json);
1✔
953
        if (m.find()) {
1✔
954
            c.accept(Integer.parseInt(m.group(1)));
1✔
955
        }
956
    }
1✔
957

958
    @Deprecated
959
    public static Long readLong(String json, Pattern pattern) {
960
        Matcher m = pattern.matcher(json);
1✔
961
        return m.find() ? safeParseLong(m.group(1)) : null;
1✔
962
    }
963

964
    @Deprecated
965
    public static long readLong(String json, Pattern pattern, long dflt) {
966
        Matcher m = pattern.matcher(json);
1✔
967
        return m.find() ? safeParseLong(m.group(1), dflt) : dflt;
1✔
968
    }
969

970
    @Deprecated
971
    public static void readLong(String json, Pattern pattern, LongConsumer c) {
972
        Matcher m = pattern.matcher(json);
1✔
973
        if (m.find()) {
1✔
974
            Long l = safeParseLong(m.group(1));
1✔
975
            if (l != null) {
1✔
976
                c.accept(l);
1✔
977
            }
978
        }
979
    }
1✔
980

981
    @Deprecated
982
    public static ZonedDateTime readDate(String json, Pattern pattern) {
983
        Matcher m = pattern.matcher(json);
1✔
984
        return m.find() ? DateTimeUtils.parseDateTime(m.group(1)) : null;
1✔
985
    }
986

987
    @Deprecated
988
    public static Duration readNanos(String json, Pattern pattern) {
989
        Matcher m = pattern.matcher(json);
1✔
990
        return m.find() ? Duration.ofNanos(Long.parseLong(m.group(1))) : null;
1✔
991
    }
992

993
    @Deprecated
994
    public static Duration readNanos(String json, Pattern pattern, Duration dflt) {
995
        Matcher m = pattern.matcher(json);
1✔
996
        return m.find() ? Duration.ofNanos(Long.parseLong(m.group(1))) : dflt;
1✔
997
    }
998

999
    @Deprecated
1000
    public static void readNanos(String json, Pattern pattern, Consumer<Duration> c) {
1001
        Matcher m = pattern.matcher(json);
1✔
1002
        if (m.find()) {
1✔
1003
            c.accept(Duration.ofNanos(Long.parseLong(m.group(1))));
1✔
1004
        }
1005
    }
1✔
1006

1007
    public static <T> boolean listEquals(List<T> l1, List<T> l2)
1008
    {
1009
        if (l1 == null)
1✔
1010
        {
1011
            return l2 == null;
1✔
1012
        }
1013

1014
        if (l2 == null)
1✔
1015
        {
1016
            return false;
1✔
1017
        }
1018

1019
        return l1.equals(l2);
1✔
1020
    }
1021

1022
    public static boolean mapEquals(Map<String, String> map1, Map<String, String> map2) {
1023
        if (map1 == null) {
1✔
1024
            return map2 == null;
1✔
1025
        }
1026
        if (map2 == null || map1.size() != map2.size()) {
1✔
1027
            return false;
1✔
1028
        }
1029
        for (String key : map1.keySet()) {
1✔
1030
            if (!Objects.equals(map1.get(key), map2.get(key))) {
1✔
1031
                return false;
1✔
1032
            }
1033
        }
1✔
1034
        return true;
1✔
1035
    }
1036
}
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

© 2026 Coveralls, Inc