Skip to content

Commit fafc52d

Browse files
authored
Merge pull request #4 from wsw-stack/milestone4-haitong
Milestone4 haitong
2 parents a736292 + a031005 commit fafc52d

File tree

4 files changed

+274
-0
lines changed

4 files changed

+274
-0
lines changed

README-M4.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Milestone 4 – Stream support for **JSONObject**
2+
3+
## What is Added
4+
5+
| Item | Description |
6+
|------|-------------|
7+
| `JSONObject.JSONNode` | Immutable leaf wrapper containing an absolute `path` and its `value`. |
8+
| `Stream<JSONNode> JSONObject.toStream()` | Depth-first, **lazy** flattening of any `JSONObject/JSONArray` into a `Stream`. Only leaf nodes are emitted (low memory footprint). |
9+
10+
Path conventions
11+
* Object keys: `/parent/child`
12+
* Array items: `/array[0]/child`
13+
14+
---
15+
16+
## Run the test class
17+
```bash
18+
mvn -Dtest=org.json.junit.milestone4.tests.JSONObjectStreamTest test

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import java.util.*;
1818
import java.util.Map.Entry;
1919
import java.util.regex.Pattern;
20+
import java.util.stream.IntStream;
21+
import java.util.stream.Stream;
2022

2123
/**
2224
* A JSONObject is an unordered collection of name/value pairs. Its external
@@ -3030,4 +3032,64 @@ private static String removeLeadingZerosOfNumber(String value){
30303032
if (negativeFirstChar) {return "-0";}
30313033
return "0";
30323034
}
3035+
3036+
// ---------------------------- Milestone 4 ------------------------------
3037+
3038+
/**
3039+
* Represents a node in the JSON object tree, with path and value.
3040+
*/
3041+
public static class JSONNode {
3042+
private final String path;
3043+
private final Object value;
3044+
3045+
public JSONNode(String path, Object value) {
3046+
this.path = path;
3047+
this.value = value;
3048+
}
3049+
3050+
public String getPath() {
3051+
return path;
3052+
}
3053+
3054+
public Object getValue() {
3055+
return value;
3056+
}
3057+
3058+
@Override
3059+
public String toString() {
3060+
return "JSONNode{path='" + path + "', value=" + value + '}';
3061+
}
3062+
}
3063+
3064+
/**
3065+
* Convert this JSONObject into a stream of JSONNode, allowing chained operations.
3066+
* @return Stream of JSONNode objects (each has a full path and a value)
3067+
*/
3068+
public Stream<JSONNode> toStream() {
3069+
return toStream("", this);
3070+
}
3071+
3072+
/**
3073+
* Recursive helper method to flatten JSONObject/JSONArray into JSONNode stream.
3074+
*/
3075+
private Stream<JSONNode> toStream(String path, Object value) {
3076+
if (value instanceof JSONObject) {
3077+
JSONObject obj = (JSONObject) value;
3078+
return obj.keySet().stream()
3079+
.flatMap(key -> {
3080+
String newPath = path.isEmpty() ? "/" + key : path + "/" + key;
3081+
return toStream(newPath, obj.get(key));
3082+
});
3083+
} else if (value instanceof JSONArray) {
3084+
JSONArray array = (JSONArray) value;
3085+
return IntStream.range(0, array.length())
3086+
.boxed()
3087+
.flatMap(i -> {
3088+
String newPath = path + "[" + i + "]";
3089+
return toStream(newPath, array.get(i));
3090+
});
3091+
} else {
3092+
return Stream.of(new JSONNode(path, value));
3093+
}
3094+
}
30333095
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package org.json.junit.milestone4.tests;
2+
3+
import org.json.JSONObject;
4+
import org.json.JSONObject.JSONNode;
5+
import org.json.XML;
6+
import org.junit.BeforeClass;
7+
import org.junit.Test;
8+
9+
import java.io.InputStream;
10+
import java.nio.charset.StandardCharsets;
11+
import java.util.List;
12+
import java.util.Optional;
13+
import java.util.Scanner;
14+
import java.util.stream.Collectors;
15+
16+
import static org.junit.Assert.*;
17+
18+
/**
19+
* Unit tests for the JSONObject.toStream() extension (Milestone 4) – JUnit 4 / Java 8.
20+
*/
21+
public class JSONObjectStreamTest {
22+
23+
private static JSONObject catalog;
24+
25+
@BeforeClass
26+
public static void loadXml() throws Exception {
27+
try (InputStream in = JSONObjectStreamTest.class.getResourceAsStream("/books.xml")) {
28+
assertNotNull("books.xml must be on the class-path (src/test/resources)", in);
29+
30+
String xml;
31+
try (Scanner scanner = new Scanner(in, StandardCharsets.UTF_8.name())) {
32+
scanner.useDelimiter("\\A");
33+
xml = scanner.hasNext() ? scanner.next() : "";
34+
}
35+
36+
catalog = XML.toJSONObject(xml);
37+
}
38+
}
39+
40+
@Test
41+
public void extractTitles() {
42+
List<String> titles = catalog.toStream()
43+
.filter(n -> n.getPath().endsWith("/title"))
44+
.map(n -> n.getValue().toString())
45+
.collect(Collectors.toList());
46+
47+
assertEquals(12, titles.size());
48+
assertTrue(titles.contains("XML Developer's Guide"));
49+
assertTrue(titles.contains("Visual Studio 7: A Comprehensive Guide"));
50+
}
51+
52+
@Test
53+
public void findExactPath() {
54+
String wantedPath = "/catalog/book[0]/author";
55+
Optional<JSONNode> node = catalog.toStream()
56+
.filter(n -> n.getPath().equals(wantedPath))
57+
.findFirst();
58+
59+
assertTrue(node.isPresent());
60+
assertEquals("Gambardella, Matthew", node.get().getValue());
61+
}
62+
63+
@Test
64+
public void filterCheapBooks() {
65+
List<JSONNode> cheapPrices = catalog.toStream()
66+
.filter(n -> n.getPath().endsWith("/price"))
67+
.filter(n -> Double.parseDouble(n.getValue().toString()) < 10.0)
68+
.collect(Collectors.toList());
69+
70+
assertEquals(8, cheapPrices.size());
71+
assertTrue(cheapPrices.stream()
72+
.allMatch(n -> Double.parseDouble(n.getValue().toString()) < 10.0));
73+
}
74+
}

src/test/resources/books.xml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?xml version="1.0"?>
2+
<catalog>
3+
<book id="bk101">
4+
<author>Gambardella, Matthew</author>
5+
<title>XML Developer's Guide</title>
6+
<genre>Computer</genre>
7+
<price>44.95</price>
8+
<publish_date>2000-10-01</publish_date>
9+
<description>An in-depth look at creating applications
10+
with XML.</description>
11+
</book>
12+
<book id="bk102">
13+
<author>Ralls, Kim</author>
14+
<title>Midnight Rain</title>
15+
<genre>Fantasy</genre>
16+
<price>5.95</price>
17+
<publish_date>2000-12-16</publish_date>
18+
<description>A former architect battles corporate zombies,
19+
an evil sorceress, and her own childhood to become queen
20+
of the world.</description>
21+
</book>
22+
<book id="bk103">
23+
<author>Corets, Eva</author>
24+
<title>Maeve Ascendant</title>
25+
<genre>Fantasy</genre>
26+
<price>5.95</price>
27+
<publish_date>2000-11-17</publish_date>
28+
<description>After the collapse of a nanotechnology
29+
society in England, the young survivors lay the
30+
foundation for a new society.</description>
31+
</book>
32+
<book id="bk104">
33+
<author>Corets, Eva</author>
34+
<title>Oberon's Legacy</title>
35+
<genre>Fantasy</genre>
36+
<price>5.95</price>
37+
<publish_date>2001-03-10</publish_date>
38+
<description>In post-apocalypse England, the mysterious
39+
agent known only as Oberon helps to create a new life
40+
for the inhabitants of London. Sequel to Maeve
41+
Ascendant.</description>
42+
</book>
43+
<book id="bk105">
44+
<author>Corets, Eva</author>
45+
<title>The Sundered Grail</title>
46+
<genre>Fantasy</genre>
47+
<price>5.95</price>
48+
<publish_date>2001-09-10</publish_date>
49+
<description>The two daughters of Maeve, half-sisters,
50+
battle one another for control of England. Sequel to
51+
Oberon's Legacy.</description>
52+
</book>
53+
<book id="bk106">
54+
<author>Randall, Cynthia</author>
55+
<title>Lover Birds</title>
56+
<genre>Romance</genre>
57+
<price>4.95</price>
58+
<publish_date>2000-09-02</publish_date>
59+
<description>When Carla meets Paul at an ornithology
60+
conference, tempers fly as feathers get ruffled.</description>
61+
</book>
62+
<book id="bk107">
63+
<author>Thurman, Paula</author>
64+
<title>Splish Splash</title>
65+
<genre>Romance</genre>
66+
<price>4.95</price>
67+
<publish_date>2000-11-02</publish_date>
68+
<description>A deep sea diver finds true love twenty
69+
thousand leagues beneath the sea.</description>
70+
</book>
71+
<book id="bk108">
72+
<author>Knorr, Stefan</author>
73+
<title>Creepy Crawlies</title>
74+
<genre>Horror</genre>
75+
<price>4.95</price>
76+
<publish_date>2000-12-06</publish_date>
77+
<description>An anthology of horror stories about roaches,
78+
centipedes, scorpions and other insects.</description>
79+
</book>
80+
<book id="bk109">
81+
<author>Kress, Peter</author>
82+
<title>Paradox Lost</title>
83+
<genre>Science Fiction</genre>
84+
<price>6.95</price>
85+
<publish_date>2000-11-02</publish_date>
86+
<description>After an inadvertant trip through a Heisenberg
87+
Uncertainty Device, James Salway discovers the problems
88+
of being quantum.</description>
89+
</book>
90+
<book id="bk110">
91+
<author>O'Brien, Tim</author>
92+
<title>Microsoft .NET: The Programming Bible</title>
93+
<genre>Computer</genre>
94+
<price>36.95</price>
95+
<publish_date>2000-12-09</publish_date>
96+
<description>Microsoft's .NET initiative is explored in
97+
detail in this deep programmer's reference.</description>
98+
</book>
99+
<book id="bk111">
100+
<author>O'Brien, Tim</author>
101+
<title>MSXML3: A Comprehensive Guide</title>
102+
<genre>Computer</genre>
103+
<price>36.95</price>
104+
<publish_date>2000-12-01</publish_date>
105+
<description>The Microsoft MSXML3 parser is covered in
106+
detail, with attention to XML DOM interfaces, XSLT processing,
107+
SAX and more.</description>
108+
</book>
109+
<book id="bk112">
110+
<author>Galos, Mike</author>
111+
<title>Visual Studio 7: A Comprehensive Guide</title>
112+
<genre>Computer</genre>
113+
<price>49.95</price>
114+
<publish_date>2001-04-16</publish_date>
115+
<description>Microsoft Visual Studio 7 is explored in depth,
116+
looking at how Visual Basic, Visual C++, C#, and ASP+ are
117+
integrated into a comprehensive development
118+
environment.</description>
119+
</book>
120+
</catalog>

0 commit comments

Comments
 (0)