Skip to content

Commit 525a9d0

Browse files
committed
Unsafe JSONObject
JSONObject extends HashMap new methods addUpdateListener and update with PropertyChangeListener
1 parent 66a5997 commit 525a9d0

File tree

3 files changed

+85
-68
lines changed

3 files changed

+85
-68
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<groupId>com.github.leonardofel</groupId>
55
<artifactId>json-java-put-null-fix</artifactId>
6-
<version>3.0.34</version>
6+
<version>3.0.35.unsafe</version>
77
<packaging>jar</packaging>
88

99
<name>JSON in Java WITH WORKING .put(null)</name>

src/main/java/org/json/JSONObject.java

Lines changed: 44 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.json;
22

3+
import java.beans.PropertyChangeListener;
4+
import java.beans.PropertyChangeSupport;
35
import java.io.Closeable;
46

57
/*
@@ -42,9 +44,10 @@ of this software and associated documentation files (the "Software"), to deal
4244
import java.util.Iterator;
4345
import java.util.Locale;
4446
import java.util.Map;
45-
import java.util.Map.Entry;
4647
import java.util.ResourceBundle;
4748
import java.util.Set;
49+
import java.util.function.BiFunction;
50+
import java.util.function.Supplier;
4851
import java.util.regex.Pattern;
4952

5053
/**
@@ -100,7 +103,7 @@ of this software and associated documentation files (the "Software"), to deal
100103
* @author JSON.org
101104
* @version 2016-08-15
102105
*/
103-
public class JSONObject {
106+
public class JSONObject extends HashMap<String, Object> {
104107
/**
105108
* JSONObject.NULL is equivalent to the value that JavaScript calls null,
106109
* whilst Java's null is equivalent to the value that JavaScript calls
@@ -159,11 +162,6 @@ public String toString() {
159162
*/
160163
static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?");
161164

162-
/**
163-
* The map where the JSONObject's properties are kept.
164-
*/
165-
private final Map<String, Object> map;
166-
167165
/**
168166
* It is sometimes more convenient and less ambiguous to have a
169167
* <code>NULL</code> object than to use Java's <code>null</code> value.
@@ -182,7 +180,7 @@ public JSONObject() {
182180
// implementations to rearrange their items for a faster element
183181
// retrieval based on associative access.
184182
// Therefore, an implementation mustn't rely on the order of the item.
185-
this.map = new HashMap<String, Object>();
183+
super();
186184
}
187185

188186
/**
@@ -286,18 +284,14 @@ public JSONObject(JSONTokener x) throws JSONException {
286284
* If a key in the map is <code>null</code>
287285
*/
288286
public JSONObject(Map<?, ?> m) {
289-
if (m == null) {
290-
this.map = new HashMap<String, Object>();
291-
} else {
292-
this.map = new HashMap<String, Object>(m.size());
293-
for (final Entry<?, ?> e : m.entrySet()) {
294-
if (e.getKey() == null) {
295-
throw new NullPointerException("Null key.");
296-
}
297-
final Object value = e.getValue();
298-
if (value != null) {
299-
this.map.put(String.valueOf(e.getKey()), wrap(value));
300-
}
287+
super(m.size());
288+
for (final Entry<?, ?> e : m.entrySet()) {
289+
if (e.getKey() == null) {
290+
throw new NullPointerException("Null key.");
291+
}
292+
final Object value = e.getValue();
293+
if (value != null) {
294+
super.put(String.valueOf(e.getKey()), wrap(value));
301295
}
302296
}
303297
}
@@ -453,17 +447,6 @@ public JSONObject(String baseName, Locale locale) throws JSONException {
453447
}
454448
}
455449

456-
/**
457-
* Constructor to specify an initial capacity of the internal map. Useful for library
458-
* internal calls where we know, or at least can best guess, how big this JSONObject
459-
* will be.
460-
*
461-
* @param initialCapacity initial capacity of the internal map.
462-
*/
463-
protected JSONObject(int initialCapacity) {
464-
this.map = new HashMap<String, Object>(initialCapacity);
465-
}
466-
467450
/**
468451
* Accumulate values under a key. It is similar to the put method except
469452
* that if there is already an object stored under the key then a JSONArray
@@ -569,7 +552,7 @@ public static String doubleToString(double d) {
569552
*/
570553
public Object get(String key) throws JSONException {
571554
try {
572-
return this.map.get(key);
555+
return super.get(key);
573556
} catch (Exception e) {
574557
throw new JSONException(e.getMessage());
575558
}
@@ -870,7 +853,7 @@ public String getString(String key) throws JSONException {
870853
* @return true if the key exists in the JSONObject.
871854
*/
872855
public boolean has(String key) {
873-
return this.map.containsKey(key);
856+
return super.containsKey(key);
874857
}
875858

876859
/**
@@ -944,23 +927,7 @@ public Iterator<String> keys() {
944927
* @return A keySet.
945928
*/
946929
public Set<String> keySet() {
947-
return this.map.keySet();
948-
}
949-
950-
/**
951-
* Get a set of entries of the JSONObject. These are raw values and may not
952-
* match what is returned by the JSONObject get* and opt* functions. Modifying
953-
* the returned EntrySet or the Entry objects contained therein will modify the
954-
* backing JSONObject. This does not return a clone or a read-only view.
955-
*
956-
* Use with caution.
957-
*
958-
* @see Map#entrySet()
959-
*
960-
* @return An Entry Set
961-
*/
962-
protected Set<Entry<String, Object>> entrySet() {
963-
return this.map.entrySet();
930+
return super.keySet();
964931
}
965932

966933
/**
@@ -969,7 +936,7 @@ protected Set<Entry<String, Object>> entrySet() {
969936
* @return The number of keys in the JSONObject.
970937
*/
971938
public int length() {
972-
return this.map.size();
939+
return super.size();
973940
}
974941

975942
/**
@@ -978,7 +945,7 @@ public int length() {
978945
* @return true if JSONObject is empty, otherwise false.
979946
*/
980947
public boolean isEmpty() {
981-
return this.map.isEmpty();
948+
return super.isEmpty();
982949
}
983950

984951
/**
@@ -989,10 +956,10 @@ public boolean isEmpty() {
989956
* is empty.
990957
*/
991958
public JSONArray names() {
992-
if (this.map.isEmpty()) {
959+
if (super.isEmpty()) {
993960
return null;
994961
}
995-
return new JSONArray(this.map.keySet());
962+
return new JSONArray(super.keySet());
996963
}
997964

998965
/**
@@ -1035,7 +1002,7 @@ public Object opt(String key) {
10351002
if (key == null) {
10361003
return null;
10371004
} else {
1038-
final Object value = this.map.get(key);
1005+
final Object value = super.get(key);
10391006
if (JSONObject.NULL.equals(value)) {
10401007
return null;
10411008
} else {
@@ -1527,7 +1494,7 @@ private void populateMap(Object bean) {
15271494
try {
15281495
final Object result = method.invoke(bean);
15291496
if (result != null) {
1530-
this.map.put(key, wrap(result));
1497+
super.put(key, wrap(result));
15311498
// we don't use the result anywhere outside of wrap
15321499
// if it's a resource we should be sure to close it
15331500
// after calling toString
@@ -1826,6 +1793,22 @@ public JSONObject putMap(String key, Map<?, ?> value) throws JSONException {
18261793
return this.put(key, new JSONObject(value));
18271794
}
18281795

1796+
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
1797+
private final String propertyName = "map";
1798+
public void addUpdateListener(PropertyChangeListener propertyChangeListener) {
1799+
propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
1800+
}
1801+
1802+
public void update(String key, Object newValue) throws JSONException {
1803+
if (propertyChangeSupport.hasListeners(this.propertyName)) {
1804+
final Object oldValue = this.opt(key);
1805+
this.put(key, newValue);
1806+
propertyChangeSupport.firePropertyChange(this.propertyName, oldValue, newValue);
1807+
} else {
1808+
throw new JSONException("updateListener not initialized");
1809+
}
1810+
}
1811+
18291812
/**
18301813
* <p><img src='https://media1.tenor.com/images/23d9d746fc87b3a93298af43dae21f6a/tenor.gif' /></p>
18311814
*
@@ -1853,16 +1836,18 @@ public JSONObject put(String key, Object value) throws JSONException {
18531836
// just...
18541837
// works
18551838
// btw idc
1856-
this.map.put(key, JSONObject.NULL);
1839+
super.put(key, JSONObject.NULL);
18571840
} else {
18581841
testValidity(value);
18591842

18601843
if (value instanceof Collection) {
18611844
this.putCollection(key, (Collection<?>) value);
1845+
} else if (value instanceof JSONObject) {
1846+
super.put(key, value);
18621847
} else if (value instanceof Map) {
18631848
this.putMap(key, (Map<?, ?>) value);
18641849
} else {
1865-
this.map.put(key, value);
1850+
super.put(key, value);
18661851
}
18671852
}
18681853
return this;
@@ -2076,7 +2061,7 @@ public static Writer quote(String string, Writer w) throws IOException {
20762061
* no value.
20772062
*/
20782063
public Object remove(String key) {
2079-
return this.map.remove(key);
2064+
return super.remove(key);
20802065
}
20812066

20822067
/**

src/test/java/org/json/junit/JSONTest.java

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.json.junit;
22

33
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertFalse;
45
import static org.junit.Assert.assertTrue;
56
import static org.junit.Assert.fail;
67

@@ -9,6 +10,7 @@
910
import java.util.Collections;
1011
import java.util.HashMap;
1112
import java.util.Map;
13+
import java.util.concurrent.atomic.AtomicBoolean;
1214

1315
import org.json.JSONObject;
1416
import org.junit.Test;
@@ -395,13 +397,43 @@ public void testPutFloat() {
395397

396398
@Test
397399
public void lastTest() {
398-
//final Byte a_Byte; //json.getByte("a");
399-
final Double a_Double; //json.getDouble("a"); //testNullDouble
400-
final Float a_Float; //json.getFloat("a"); //testNullFloat
401-
final Integer a_Integer; //json.getInteger("a"); //testNullInteger
402-
final Long a_Long; //json.getLong("a"); //testNullLong
403-
//final Short a_Short; //json.getShort("a");
404-
final BigDecimal a_BigDecimal; //json.getBigDecimal("a");
405-
final BigInteger a_BigInteger; //json.getBigInteger("a");
400+
final Byte a_Byte /* = new JSONObject().optByte("a") */;
401+
final Double a_Double = new JSONObject().optDouble("a"); //testNullDouble
402+
final Float a_Float = new JSONObject().optFloat("a"); //testNullFloat
403+
final Integer a_Integer = new JSONObject().optInteger("a"); //testNullInteger
404+
final Long a_Long = new JSONObject().optLong("a"); //testNullLong
405+
final Short a_Short /* = new JSONObject().getShort("a") */;
406+
final BigDecimal a_BigDecimal /* = new JSONObject().getBigDecimal("a") */;
407+
final BigInteger a_BigInteger /* = new JSONObject().getBigInteger("a") */;
408+
}
409+
410+
@Test
411+
public void computeTest() {
412+
JSONObject j = new JSONObject();
413+
414+
var r1 = j.opt("myKey");
415+
assertEquals(r1, null);
416+
417+
j.compute("myKey", (k, v) -> v == null ? "myNull" : "unexpected");
418+
419+
var r2 = j.opt("myKey");
420+
assertEquals(r2, "myNull");
421+
}
422+
423+
424+
@Test
425+
public void updateTest() {
426+
final JSONObject j = new JSONObject();
427+
final AtomicBoolean atomicBoolean = new AtomicBoolean();
428+
assertFalse(atomicBoolean.get());
429+
j.addUpdateListener(evt -> {
430+
final Object oldValue = evt.getOldValue();
431+
assertEquals(oldValue, null);
432+
final Object newValue = evt.getNewValue();
433+
assertEquals(newValue, "propertyChange");
434+
atomicBoolean.set(true);
435+
});
436+
j.update("myMapListener", "propertyChange");
437+
assertTrue(atomicBoolean.get());
406438
}
407439
}

0 commit comments

Comments
 (0)