-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Streams support #768
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this looks like a good start, However I'd personally like to see a few more test cases around getting data out and null
handling in both the JSONObject
and JSONArray
streams.
Looking at #673 and #674, I think we should look towards always returning valid JSON value types from the streams. It is something we missed out on with the iterators and prevents people from having a consistent interface.
We should also be sure that the streams themselves (specifically the ones provided as part of the API, not the follow-on calls as part of the tests) do not throw exceptions by using opt
operations wherever possible.
* @return Stream of array elements | ||
*/ | ||
public Stream<Object> stream() { | ||
return IntStream.range(0, length()).mapToObj(this::get); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should not be this::get
and instead should be this::opt
. Stream should not throw exceptions, which get
will do if a value is null
.
We should also be sure to include at least one test case that has null
in the array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should not be
this::get
and instead should bethis::opt
. Stream should not throw exceptions, whichget
will do if a value isnull
.We should also be sure to include at least one test case that has
null
in the array.
Seems to be working as is, but not sure why. Test cases are updated, let me know if this code or the tests should still be changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I stepped through the code and that Parser is converting null
to JSONObject.NULL
, which is expected.
Let's add these 2 additional tests, which are very similar, but add different ways of composing the data instead of just using the parser. This second test shows the issue I was worried about:
@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);
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added tests.
Replaced this::get with this::opt
@@ -81,7 +74,13 @@ | |||
* @author JSON.org | |||
* @version 2016-08-15 | |||
*/ | |||
public class JSONObject { | |||
public class JSONObject implements Iterable<String> { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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<>.
JSONArray jsonArray = new JSONArray(data); | ||
|
||
// Get employees from the engineering dept | ||
List<String> actualList = StreamSupport.stream(jsonArray.spliterator(), false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test doesn't seem to be testing the new Stream method. It appears to only be testing Iteratable<>.spliterator()
method.
This is a valid test, but I don't think this is doing the coverage that you wanted for the new method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced with new tests
public void testStream_1_3_2() { | ||
JSONArray jsonArray = null; | ||
|
||
assertThrows(NullPointerException.class, () -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this test is adding anything here. It seems obvious that an NPE would be thrown in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
public void test_2_3_2() { | ||
JSONObject jsonObject = null; | ||
|
||
assertThrows(NullPointerException.class, () -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this test is adding anything here. It seems obvious that an NPE would be thrown in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
* @return Stream of array elements | ||
*/ | ||
public Stream<Object> stream() { | ||
return IntStream.range(0, length()).mapToObj(this::get); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I stepped through the code and that Parser is converting null
to JSONObject.NULL
, which is expected.
Let's add these 2 additional tests, which are very similar, but add different ways of composing the data instead of just using the parser. This second test shows the issue I was worried about:
@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);
}
}
}
Added the new unit tests |
Closing due to lack of activity. |
What problem does this code solve?
JSONObject and JSONArray should support streams for functional programming.
There are multiple ways this could be added. If you know of a better way, please post your comments to this PR.
This change requires Java 8. Going forward, Java 6 and Java 7 library builds are no longer supported. The last release for Java 6/7 is 20230618. Users who still need Java 6/7 support should open an issue in this repo.
Risks
Low
Changes to the API?
New API methods were added
Will this require a new release?
Yes
Should the documentation be updated?
No
Does it break the unit tests?
No. New unit tests were added
Was any code refactored in this commit?
Review status
code review action items pending