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

nats-io / json.java / #46

09 Jun 2025 10:12PM UTC coverage: 99.356% (+2.4%) from 96.99%
#46

push

github

web-flow
Merge pull request #16 from nats-io/three-dot-oh

Start 3.0.0 API Changes and Improvements.

205 of 205 new or added lines in 7 files covered. (100.0%)

1 existing line in 1 file now uncovered.

772 of 777 relevant lines covered (99.36%)

0.99 hits per line

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

99.48
/src/main/java/io/nats/json/JsonValue.java
1
// Copyright 2023-2025 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 java.math.BigDecimal;
17
import java.math.BigInteger;
18
import java.time.Duration;
19
import java.util.*;
20

21
import static io.nats.json.Encoding.jsonEncode;
22
import static io.nats.json.JsonWriteUtils.addField;
23

24
public class JsonValue implements JsonSerializable {
25

26
    /**
27
     * Possible types of the underlying value
28
     */
29
    public enum Type {
1✔
30
        STRING, BOOL, INTEGER, LONG, DOUBLE, FLOAT, BIG_DECIMAL, BIG_INTEGER, MAP, ARRAY, NULL;
1✔
31
    }
32

33
    private static final char QUOTE = '"';
34
    private static final char COMMA = ',';
35
    private static final String NULL_STR = "null";
36

37
    public static final JsonValue NULL = new JsonValue(Type.NULL);
1✔
38
    public static final JsonValue TRUE = new JsonValue(true);
1✔
39
    public static final JsonValue FALSE = new JsonValue(false);
1✔
40
    public static final JsonValue EMPTY_MAP = new JsonValue(Type.MAP);
1✔
41
    public static final JsonValue EMPTY_ARRAY = new JsonValue(Type.ARRAY);
1✔
42

43
    /**
44
     * The underlying string
45
     */
46
    public final String string;
47

48
    public final Boolean bool;
49
    public final Integer i;
50
    public final Long l;
51
    public final Double d;
52
    public final Float f;
53
    public final BigDecimal bd;
54
    public final BigInteger bi;
55
    public final Map<String, JsonValue> map;
56
    public final List<JsonValue> array;
57
    public final Type type;
58
    public final Object object;
59
    public final Number number;
60

61
    public final List<String> mapOrder;
62

63
    public static JsonValue instance(Object o) {
64
        return switch (o) {
1✔
65
            case null -> JsonValue.NULL;
1✔
66
            case String string -> new JsonValue(string);
1✔
67
            case JsonValue jsonValue -> jsonValue;
1✔
68
            case JsonSerializable jsonSerializable -> jsonSerializable.toJsonValue();
1✔
69
            case Boolean b -> new JsonValue(b);
1✔
70
            case Integer i -> new JsonValue(i);
1✔
71
            case Long l -> new JsonValue(l);
1✔
72
            case Double d -> new JsonValue(d);
1✔
73
            case Float v -> new JsonValue(v);
1✔
74
            case BigDecimal bigDecimal -> new JsonValue(bigDecimal);
1✔
75
            case BigInteger bigInteger -> new JsonValue(bigInteger);
1✔
76
            case Collection<?> list -> _instance(list);
1✔
77
            case Map<?, ?> map -> _instance(map);
1✔
78
            case Duration dur -> new JsonValue(dur.toNanos());
1✔
79
            default -> new JsonValue(o.toString());
1✔
80
        };
81
    }
82

83
    private static JsonValue _instance(Collection<?> list) {
84
        List<JsonValue> jv = new ArrayList<>();
1✔
85
        for (Object o : list) {
1✔
86
            jv.add(JsonValue.instance(o));
1✔
87
        }
1✔
88
        return new JsonValue(jv);
1✔
89
    }
90

91
    private static JsonValue _instance(Map<?, ?> map) {
92
        Map<String, JsonValue> jv = new HashMap<>();
1✔
93
        for(Map.Entry<?, ?> entry : map.entrySet()) {
1✔
94
            jv.put(entry.getKey().toString(), JsonValue.instance(entry.getValue()));
1✔
95
        }
1✔
96
        return new JsonValue(jv);
1✔
97
    }
98

99
    public JsonValue(String string) {
100
        this(string, null, null, null, null, null, null, null, null, null);
1✔
101
    }
1✔
102

103
    public JsonValue(char c) {
104
        this("" + c, null, null, null, null, null, null, null, null, null);
1✔
105
    }
1✔
106

107
    public JsonValue(Boolean bool) {
108
        this(null, bool, null, null, null, null, null, null, null, null);
1✔
109
    }
1✔
110

111
    public JsonValue(int i) {
112
        this(null, null, i, null, null, null, null, null, null, null);
1✔
113
    }
1✔
114

115
    public JsonValue(long l) {
116
        this(null, null, null, l, null, null, null, null, null, null);
1✔
117
    }
1✔
118

119
    public JsonValue(double d) {
120
        this(null, null, null, null, d, null, null, null, null, null);
1✔
121
    }
1✔
122

123
    public JsonValue(float f) {
124
        this(null, null, null, null, null, f, null, null, null, null);
1✔
125
    }
1✔
126

127
    public JsonValue(BigDecimal bd) {
128
        this(null, null, null, null, null, null, bd, null, null, null);
1✔
129
    }
1✔
130

131
    public JsonValue(BigInteger bi) {
132
        this(null, null, null, null, null, null, null, bi, null, null);
1✔
133
    }
1✔
134

135
    public JsonValue(Map<String, JsonValue> map) {
136
        this(null, null, null, null, null, null, null, null, map, null);
1✔
137
    }
1✔
138

139
    public JsonValue(Collection<JsonValue> list) {
140
        this(null, null, null, null, null, null, null, null, null, list);
1✔
141
    }
1✔
142

143
    public JsonValue(JsonValue[] values) {
144
        this(null, null, null, null, null, null, null, null, null, values == null ? null : Arrays.asList(values));
1✔
145
    }
1✔
146

147
    private JsonValue(String string, Boolean bool, Integer i, Long l, Double d, Float f, BigDecimal bd, BigInteger bi,
148
        Map<String, JsonValue> map, Collection<JsonValue> array)
149
    {
1✔
150
        this.map = map;
1✔
151
        mapOrder = new ArrayList<>();
1✔
152
        if (array == null) {
1✔
153
            this.array = null;
1✔
154
        }
155
        else {
156
            this.array = new ArrayList<>(array);
1✔
157
        }
158
        this.string = string;
1✔
159
        this.bool = bool;
1✔
160
        this.i = i;
1✔
161
        this.l = l;
1✔
162
        this.d = d;
1✔
163
        this.f = f;
1✔
164
        this.bd = bd;
1✔
165
        this.bi = bi;
1✔
166
        if (i != null) {
1✔
167
            this.type = Type.INTEGER;
1✔
168
            number = i;
1✔
169
            object = number;
1✔
170
        }
171
        else if (l != null) {
1✔
172
            this.type = Type.LONG;
1✔
173
            number = l;
1✔
174
            object = number;
1✔
175
        }
176
        else if (d != null) {
1✔
177
            this.type = Type.DOUBLE;
1✔
178
            number = this.d;
1✔
179
            object = number;
1✔
180
        }
181
        else if (f != null) {
1✔
182
            this.type = Type.FLOAT;
1✔
183
            number = this.f;
1✔
184
            object = number;
1✔
185
        }
186
        else if (bd != null) {
1✔
187
            this.type = Type.BIG_DECIMAL;
1✔
188
            number = this.bd;
1✔
189
            object = number;
1✔
190
        }
191
        else if (bi != null) {
1✔
192
            this.type = Type.BIG_INTEGER;
1✔
193
            number = this.bi;
1✔
194
            object = number;
1✔
195
        }
196
        else {
197
            number = null;
1✔
198
            if (map != null) {
1✔
199
                this.type = Type.MAP;
1✔
200
                object = map;
1✔
201
            }
202
            else if (string != null) {
1✔
203
                this.type = Type.STRING;
1✔
204
                object = string;
1✔
205
            }
206
            else if (bool != null) {
1✔
207
                this.type = Type.BOOL;
1✔
208
                object = bool;
1✔
209
            }
210
            else if (array != null) {
1✔
211
                this.type = Type.ARRAY;
1✔
212
                object = array;
1✔
213
            }
214
            else {
215
                this.type = Type.NULL;
1✔
216
                object = null;
1✔
217
            }
218
        }
219
    }
1✔
220

221
    /**
222
     * Special internal constructor for empty and null
223
     * @param type the type;
224
     */
225
    private JsonValue(Type type) {
1✔
226
        this.type = type;
1✔
227

228
        string = null;
1✔
229
        bool = null;
1✔
230
        i = null;
1✔
231
        l = null;
1✔
232
        d = null;
1✔
233
        f = null;
1✔
234
        bd = null;
1✔
235
        bi = null;
1✔
236
        number = null;
1✔
237
        mapOrder = new ArrayList<>();
1✔
238

239
        if (type == Type.MAP) {
1✔
240
            map = Collections.unmodifiableMap(new HashMap<>());
1✔
241
            array = null;
1✔
242
            object = map;
1✔
243
        }
244
        else if (type == Type.ARRAY) {
1✔
245
            map = null;
1✔
246
            array = Collections.unmodifiableList(new ArrayList<>());
1✔
247
            object = array;
1✔
248
        }
249
        else { // Type.NULL
250
            map = null;
1✔
251
            array = null;
1✔
252
            object = null;
1✔
253
        }
254
    }
1✔
255

256
    public String toString(Class<?> c) {
257
        return toString(c.getSimpleName());
1✔
258
    }
259

260
    public String toString(String key) {
261
        return QUOTE + key + QUOTE + ":" + toJson();
1✔
262
    }
263

264
    @Override
265
    public String toString() {
UNCOV
266
        return toJson();
×
267
    }
268

269
    @Override
270
    public JsonValue toJsonValue() {
271
        return this;
1✔
272
    }
273

274
    @Override
275
    public String toJson() {
276
        return switch (type) {
1✔
277
            case STRING -> valueString(string);
1✔
278
            case BOOL -> valueString(bool);
1✔
279
            case MAP -> valueString(map);
1✔
280
            case ARRAY -> valueString(array);
1✔
281
            case INTEGER -> i.toString();
1✔
282
            case LONG -> l.toString();
1✔
283
            case DOUBLE -> d.toString();
1✔
284
            case FLOAT -> f.toString();
1✔
285
            case BIG_DECIMAL -> bd.toString();
1✔
286
            case BIG_INTEGER -> bi.toString();
1✔
287
            default -> NULL_STR;
1✔
288
        };
289
    }
290

291
    private String valueString(String s) {
292
        return QUOTE + jsonEncode(s) + QUOTE;
1✔
293
    }
294

295
    private String valueString(boolean b) {
296
        return Boolean.toString(b).toLowerCase();
1✔
297
    }
298

299
    private String valueString(Map<String, JsonValue> map) {
300
        StringBuilder sbo = new StringBuilder("{");
1✔
301
        if (!mapOrder.isEmpty()) {
1✔
302
            for (String key : mapOrder) {
1✔
303
                addField(sbo, key, map.get(key));
1✔
304
            }
1✔
305
        }
306
        else {
307
            for (String key : map.keySet()) {
1✔
308
                addField(sbo, key, map.get(key));
1✔
309
            }
1✔
310
        }
311
        return JsonWriteUtils.endJson(sbo).toString();
1✔
312
    }
313

314
    private String valueString(List<JsonValue> list) {
315
        StringBuilder sba = JsonWriteUtils.beginArray();
1✔
316
        for (JsonValue v : list) {
1✔
317
            sba.append(v.toJson());
1✔
318
            sba.append(COMMA);
1✔
319
        }
1✔
320
        return JsonWriteUtils.endArray(sba).toString();
1✔
321
    }
322

323
    @Override
324
    public boolean equals(Object o) {
325
        if (this == o) return true;
1✔
326
        if (o == null || getClass() != o.getClass()) return false;
1✔
327

328
        JsonValue jsonValue = (JsonValue) o;
1✔
329

330
        if (type != jsonValue.type) return false;
1✔
331
        if (!Objects.equals(map, jsonValue.map)) return false;
1✔
332
        if (!Objects.equals(array, jsonValue.array)) return false;
1✔
333
        if (!Objects.equals(string, jsonValue.string)) return false;
1✔
334
        if (!Objects.equals(bool, jsonValue.bool)) return false;
1✔
335
        if (!Objects.equals(i, jsonValue.i)) return false;
1✔
336
        if (!Objects.equals(l, jsonValue.l)) return false;
1✔
337
        if (!Objects.equals(d, jsonValue.d)) return false;
1✔
338
        if (!Objects.equals(f, jsonValue.f)) return false;
1✔
339
        if (!Objects.equals(bd, jsonValue.bd)) return false;
1✔
340
        return Objects.equals(bi, jsonValue.bi);
1✔
341
    }
342

343
    @Override
344
    public int hashCode() {
345
        int result = map != null ? map.hashCode() : 0;
1✔
346
        result = 31 * result + (array != null ? array.hashCode() : 0);
1✔
347
        result = 31 * result + (string != null ? string.hashCode() : 0);
1✔
348
        result = 31 * result + (bool != null ? bool.hashCode() : 0);
1✔
349
        result = 31 * result + (i != null ? i.hashCode() : 0);
1✔
350
        result = 31 * result + (l != null ? l.hashCode() : 0);
1✔
351
        result = 31 * result + (d != null ? d.hashCode() : 0);
1✔
352
        result = 31 * result + (f != null ? f.hashCode() : 0);
1✔
353
        result = 31 * result + (bd != null ? bd.hashCode() : 0);
1✔
354
        result = 31 * result + (bi != null ? bi.hashCode() : 0);
1✔
355
        result = 31 * result + (type != null ? type.hashCode() : 0);
1✔
356
        return result;
1✔
357
    }
358
}
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