Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streams support #768

Closed
wants to merge 10 commits into from
Empty file modified gradlew
100644 → 100755
Empty file.
10 changes: 10 additions & 0 deletions src/main/java/org/json/JSONArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import java.util.stream.Stream;


/**
Expand Down Expand Up @@ -224,6 +226,14 @@ public JSONArray(int initialCapacity) throws JSONException {
this.myArrayList = new ArrayList<Object>(initialCapacity);
}

/**
* Stream support - requires java 8
* @return Stream of array elements
*/
public Stream<Object> stream() {
return IntStream.range(0, length()).mapToObj(this::opt);
}

@Override
public Iterator<Object> iterator() {
return this.myArrayList.iterator();
Expand Down
29 changes: 18 additions & 11 deletions src/main/java/org/json/JSONObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,11 @@
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
* A JSONObject is an unordered collection of name/value pairs. Its external
Expand Down Expand Up @@ -81,7 +74,13 @@
* @author JSON.org
* @version 2016-08-15
*/
public class JSONObject {
public class JSONObject implements Iterable<String> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is the best Iterable<> that could have been chosen.

Would it make more sense to implement this closer to the Map<>.entrySet()? Something like: public class JSONObject implements Iterable<Map.Entry<String, Object>>?

Or maybe even better would be to keep it very similar to Map<> and not have it directly implement Iterable<> at all. Instead offer similar functions that the Map interface does:

  • keySet
  • entrySet
  • values

Each of those methods would return proper Iterable implementations.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation now streams entrySet() without Iterable<>.


@Override
public Iterator<String> iterator() {
return keys();
}

/**
* JSONObject.NULL is equivalent to the value that JavaScript calls null,
* whilst Java's null is equivalent to the value that JavaScript calls
Expand Down Expand Up @@ -461,6 +460,14 @@ protected JSONObject(int initialCapacity){
this.map = new HashMap<String, Object>(initialCapacity);
}

/**
* Stream support - requires java 8
* @return Stream of map entries
*/
public Stream<Map.Entry<String, Object>> stream() {
return this.entrySet().stream();
}

/**
* Accumulate values under a key. It is similar to the put method except
* that if there is already an object stored under the key then a JSONArray
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/org/json/ParserConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
/**
* Configuration base object for parsers. The configuration is immutable.
*/
@SuppressWarnings({""})
public class ParserConfiguration {
/**
* Used to indicate there's no defined limit to the maximum nesting depth when parsing a document.
Expand Down Expand Up @@ -68,10 +67,9 @@ public boolean isKeepStrings() {
/**
* When parsing the XML into JSONML, specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*
* @param <T> type parameter
* @param newVal
* new value to use for the <code>keepStrings</code> configuration option.
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
public <T extends ParserConfiguration> T withKeepStrings(final boolean newVal) {
Expand All @@ -95,6 +93,7 @@ public int getMaxNestingDepth() {
* will throw a JsonException if the maximum depth is reached.
* Using any negative value as a parameter is equivalent to setting no limit to the nesting depth,
* which means the parses will go as deep as the maximum call stack size allows.
* @param <T> type parameter
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
* @return The existing configuration will not be modified. A new configuration is returned.
*/
Expand Down
244 changes: 236 additions & 8 deletions src/test/java/org/json/junit/JSONArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,16 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

import org.json.JSONArray;
import org.json.JSONException;
Expand Down Expand Up @@ -66,6 +61,239 @@ public class JSONArrayTest {
"\"-1\""+
"]";

/**
* 1. JSONArray stream tests
* 1.1 Basic tests
* 1.2 Advanced tests
* 1.3 Exception tests
* 1.4 Integrated tests
*/

/**
* 1.1.1 Stream JSONArray to a list
*/
@Test
public void testStream_1_1_1() {
String data = "[ 1, 2, 3, true, false, null, \"abc\", \"def\", { \"a\": \"b\" }, []]";
JSONArray jsonArray = new JSONArray(data);
Object[] expectedList = {1, 2, 3, true, false, null, "abc", "def",
new JSONObject("{ \"a\": \"b\" }"), new JSONArray("[]")};

List<Object> actualList = jsonArray.stream()
.collect(Collectors.toList());
System.out.println(actualList);
assertEquals(actualList.size(), expectedList.length);
for (int i = 0; i < actualList.size(); ++i) {
Object actual = actualList.get(i);
Object expected = expectedList[i];
if (actual instanceof JSONObject) {
assertTrue(((JSONObject) actual).similar((JSONObject)expected));
} else if (actual instanceof JSONArray) {
assertTrue(((JSONArray) actual).similar((JSONArray) expected));
} else if (actual == JSONObject.NULL) {
assertNull(expected);
} else {
assertEquals(expected, actual);
}
}
}

/**
* 1.1.2 Stream JSONArray through a filter.
*/
@Test
public void testStream_1_1_2() {
String data = "[ 1, 2, 3, true, false, null, \"abc\", \"def\", { \"a\": \"b\" }, []]";
JSONArray jsonArray = new JSONArray(data);
Object[] expectedList = { 2 };

List<Object> actualList = jsonArray.stream()
.filter(x -> x instanceof Number)
.filter(x -> (int)x % 2 == 0)
.collect(Collectors.toList());
assertEquals(actualList.size(), expectedList.length);
assertEquals(expectedList[0], actualList.get(0));
}

/**
* 1.1.3 Stream JSONArray to find first.
*/
@Test
public void testStream_1_1_3() {
String data = "[ 1, 2, 3, true, false, null, \"abc\", \"def\", { \"a\": \"b\" }, []]";
JSONArray jsonArray = new JSONArray(data);
Object[] expectedList = { 2 };

Optional<Object> firstString = jsonArray.stream()
.filter(x -> x instanceof String)
.findFirst();


String actual = (String)firstString.get();
assertEquals("abc", actual);
}

/**
* 1.2.1 Stream JSONArray through a map
*/
@Test
public void testStream_1_2_1() {
String data = "[ 1, 2, 3, true, false, null, \"abc\", \"def\", { \"a\": \"b\" }, []]";
JSONArray jsonArray = new JSONArray(data);
Object[] expectedList = { "Number: 1", "Number: 2", "Number: 3" };

List<String> actualList = jsonArray.stream()
.filter(x -> x instanceof Number)
.map(x -> "Number: " + x)
.collect(Collectors.toList());

assertEquals(expectedList.length, actualList.size());
for (int i = 0; i < actualList.size(); ++i) {
assertEquals(expectedList[i], actualList.get(i));
}
}

/**
* 1.2.2 Stream JSONArray to find min and max
*/
@Test
public void testStream_1_2_2() {
String data = "[ 1, 2, 3, true, false, null, \"abc\", \"def\", { \"a\": \"b\" }, []]";
JSONArray jsonArray = new JSONArray(data);

Optional<Object> min = jsonArray.stream()
.filter(x -> x instanceof Number)
.min(Comparator.comparingInt(x -> (int) x));
Optional<Object> max = jsonArray.stream()
.filter(x -> x instanceof Number)
.max(Comparator.comparingInt(x -> (int) x));

// Assert
assertTrue(min.isPresent());
assertEquals(1, min.get());

assertTrue(max.isPresent());
assertEquals(3, max.get());
}

/**
* 1.2.3 Stream JSONArray through groupingBy
*/
@Test
public void testStream_1_2_3() {
String data = "[ 1, 2, 3, true, false, null, \"abc\", \"def\", { \"a\": \"b\" }, []]";
JSONArray jsonArray = new JSONArray(data);
Object[] expectedList = { "Number: 1", "Number: 2", "Number: 3" };

Map<Object, List<Object>> groupedByMod2 = jsonArray.stream()
.filter(x -> x instanceof Number)
.collect(Collectors.groupingBy(x -> (int) x % 2));

// Assert
assertEquals(2, groupedByMod2.size());

assertEquals(1, groupedByMod2.get(0).size());
assertEquals(2, groupedByMod2.get(0).get(0));

assertEquals(2, groupedByMod2.get(1).size());
assertEquals(1, groupedByMod2.get(1).get(0));
assertEquals(3, groupedByMod2.get(1).get(1));
}

/**
* 1.3.1 Stream empty JSONArray
*/
@Test
public void testStream_1_3_1() {
JSONArray jsonArray = new JSONArray();

List<Object> list = jsonArray.stream()
.collect(Collectors.toList());

assertTrue(list.isEmpty());
}

/**
* 1.4.1 Map from JSONObject
*/
@Test
public void testStream_1_4_1() {
JSONObject jsonObject = new JSONObject();
JSONArray jsonArray = new JSONArray();
jsonArray.put("apple");
jsonArray.put("banana");
jsonObject.put("fruits", jsonArray);
Object[] expectedList = { "apple", "banana" };

List<String> actualList = jsonObject.stream()
.filter(entry -> entry.getKey().equals("fruits"))
.flatMap(entry -> jsonObject.getJSONArray((String)entry.getKey()).stream())
.map(Object::toString)
.collect(Collectors.toList());

assertEquals(actualList.size(), expectedList.length);
for (int i = 0; i < actualList.size(); ++i) {
assertEquals(expectedList[i], actualList.get(i));
}
}

@Test
public void testStream_ComposeFromArray() {
@SuppressWarnings("boxing")
Object[] expectedList = new Object[] { 1, 2, 3, true, false, null, "abc", "def", new JSONObject("{ \"a\": \"b\" }"), new JSONArray()};
JSONArray jsonArray = new JSONArray(expectedList);

List<Object> actualList = jsonArray.stream()
.collect(Collectors.toList());
System.out.println(actualList);
assertEquals(actualList.size(), expectedList.length);
for (int i = 0; i < actualList.size(); ++i) {
Object actual = actualList.get(i);
Object expected = expectedList[i];
if (actual instanceof JSONObject) {
assertTrue("Json Object test",((JSONObject) actual).similar(expected));
} else if (actual instanceof JSONArray) {
assertTrue("Json Array test", ((JSONArray) actual).similar(expected));
} else if (JSONObject.NULL.equals(expected)) {
assertTrue("Null test", JSONObject.NULL.equals(actual));
} else {
assertEquals("value test: "+expected, expected, actual);
}
}
}

@Test
public void testStream_ComposeFromCode() {
JSONArray jsonArray = new JSONArray();
jsonArray.put(1).put(2).put(3)
.put(true).put(false)
.put((Object)null)
.put("abc").put("def")
.put(new JSONObject("{ \"a\": \"b\" }"))
.put(new JSONArray())
;
@SuppressWarnings("boxing")
Object[] expectedList = new Object[] { 1, 2, 3, true, false, null, "abc", "def", new JSONObject("{ \"a\": \"b\" }"), new JSONArray()};

List<Object> actualList = jsonArray.stream()
.collect(Collectors.toList());
System.out.println(actualList);
assertEquals(actualList.size(), expectedList.length);
for (int i = 0; i < actualList.size(); ++i) {
Object actual = actualList.get(i);
Object expected = expectedList[i];
if (actual instanceof JSONObject) {
assertTrue("Json Object test",((JSONObject) actual).similar(expected));
} else if (actual instanceof JSONArray) {
assertTrue("Json Array test", ((JSONArray) actual).similar(expected));
} else if (JSONObject.NULL.equals(expected)) {
assertTrue("Null test", JSONObject.NULL.equals(actual));
} else {
assertEquals("value test: "+expected, expected, actual);
}
}
}

/**
* Tests that the similar method is working as expected.
*/
Expand Down
Loading