From 7a98f180700ac150798c2bb7ee155c01e8040f8e Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 25 Jan 2022 21:00:59 -0800 Subject: [PATCH 01/24] milestone test units --- src/test/java/org/json/junit/XMLMyTest.java | 139 ++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 src/test/java/org/json/junit/XMLMyTest.java diff --git a/src/test/java/org/json/junit/XMLMyTest.java b/src/test/java/org/json/junit/XMLMyTest.java new file mode 100644 index 000000000..1149bfb8b --- /dev/null +++ b/src/test/java/org/json/junit/XMLMyTest.java @@ -0,0 +1,139 @@ +package org.json.junit; + +import org.json.JSONObject; +import org.json.JSONPointer; +import org.json.XML; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.*; +import java.util.Arrays; + +import static org.junit.Assert.fail; + +/** + * @program: JSON-java + * @description: + * @author: Mr. Su + * @create: 2022-01-25 11:26 + **/ + +public class XMLMyTest { + JSONPointer path; + Reader xmlReader; + String xmlFileName; + InputStream xmlStream = null; + JSONObject replacement; + @Before + public void load() { + + xmlFileName = "Issue537.xml"; + replacement = XML.toJSONObject("testContent"); + +// JSONPointer path = new JSONPointer("/clinical_study/required_header/download_date");//String + JSONPointer path = new JSONPointer("/clinical_study/required_header");//JSONObject +// JSONPointer path = new JSONPointer("/clinical_study/condition_browse/mesh_term"); //mesh_term is JSONArray +// JSONPointer path = new JSONPointer("/clinical_study/condition_browse/mesh_term/1"); +// JSONPointer path = new JSONPointer("/clinical_study/condition_browse/mesh_term1"); //no exist +// JSONPointer path = new JSONPointer("/condition_browse/mesh_term"); //no exist +// JSONPointer path = new JSONPointer("/clinical_study/condition_browse1/mesh_term"); //no exist +// JSONPointer path = new JSONPointer("/clinical_study/condition_browse/mesh_term/3"); //out of array + this.path = path; + xmlStream = XMLMyTest.class.getClassLoader().getResourceAsStream(xmlFileName); + this.xmlReader = new InputStreamReader(xmlStream); + + } + + @After + public void prepare() throws IOException { + xmlStream.close(); + } + + /* + * static JSONObject toJSONObject(Reader reader, JSONPointer path) + */ + //not using library + @Test + public void testToJSONWithReaderAndPointer1() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointer(false); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointer1 Run time:" + (endTime - startTime) + "ms"); + } + //using library + @Test + public void testToJSONWithReaderAndPointer2() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointer(true); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointer2 Run time:" + (endTime - startTime) + "ms"); + } + + private void _testToJSONWithReaderAndPointer(boolean isInLibrary) throws IOException { + Object actual; + if (isInLibrary) { + //do query inside library + actual = XML.toJSONObject(xmlReader, path); + write2File(actual, "inLib"); + } else { + //do query outside library + actual = path.queryFrom(XML.toJSONObject(xmlReader)); + write2File(actual, "outLib"); + } +// System.out.println(actual == null ? actual : actual.toString()); + } + + + private void write2File(Object actual, String ex) throws IOException { + String xmlRealPath = this.getClass().getResource("/" + xmlFileName).getPath(); + FileWriter out = new FileWriter(xmlRealPath.replace("xml", "json").replace(".json", "_" + ex + "_sub.json")); + BufferedWriter bw = new BufferedWriter(out); + bw.write(actual == null ? "" : actual.toString()); + bw.close(); + } + + /* + * static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) + */ + //not using library + @Test + public void testToJSONWithReaderAndPointerWithReplace1() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointerWithReplace(false); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointerWithReplace1 Run time:" + (endTime - startTime) + "ms"); + } + //using library + @Test + public void testToJSONWithReaderAndPointerWithReplace2() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointerWithReplace(true); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointerWithReplace2 Run time:" + (endTime - startTime) + "ms"); + } + + private void _testToJSONWithReaderAndPointerWithReplace(boolean isInLibrary) throws IOException { + Object actual; + if (isInLibrary) { + //do query inside library + actual = XML.toJSONObject(xmlReader, path, replacement); + write2File(actual, "replace_inLib"); + } else { + //do query outside library + String[] strs = path.toString().split("/"); + String keyName = strs[strs.length - 1]; + String parentPath = ""; + for(int i=1; i Date: Tue, 25 Jan 2022 21:03:01 -0800 Subject: [PATCH 02/24] milestone 2 Readme --- README.md | 350 +++++++++++++++++++++++++++++++++++++---------- README_Origin.md | 109 +++++++++++++++ 2 files changed, 389 insertions(+), 70 deletions(-) create mode 100644 README_Origin.md diff --git a/README.md b/README.md index 471bf439c..be89402a8 100644 --- a/README.md +++ b/README.md @@ -1,109 +1,319 @@ -![Json-Java logo](https://github.com/stleary/JSON-java/blob/master/images/JsonJava.png?raw=true) +

Milestone 2

-image credit: Ismael Pérez Ortiz +

Summary of Support & Not Support

+

toJSONObject(Reader reader, JSONPointer path)

-JSON in Java [package org.json] -=============================== +

support path:

-[![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json) +``` +JSONObject: /clinical_study/required_header -**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20211205/json-20211205.jar)** +JSONArray: /clinical_study/condition_browse/mesh_term +Index of JSONArray: /clinical_study/condition_browse/mesh_term/1 +``` -# Overview +

not support path:

-[JSON](http://www.JSON.org/) is a light-weight language-independent data interchange format. +``` +Nested JSONArray: /clinical_study/1/condition_browse/mesh_term/1 +``` -The JSON-Java package is a reference implementation that demonstrates how to parse JSON documents into Java objects and how to generate new JSON documents from the Java classes. +

toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

-Project goals include: -* Reliable and consistent results -* Adherence to the JSON specification -* Easy to build, use, and include in other projects -* No external dependencies -* Fast execution and low memory footprint -* Maintain backward compatibility -* Designed and tested to use on Java versions 1.6 - 1.11 +

support path:

-The files in this package implement JSON encoders and decoders. The package can also convert between JSON and XML, HTTP headers, Cookies, and CDL. +``` +JSONObject: /clinical_study/required_header -The license includes this restriction: ["The software shall be used for good, not evil."](https://en.wikipedia.org/wiki/Douglas_Crockford#%22Good,_not_Evil%22) If your conscience cannot live with that, then choose a different package. +JSONArray: /clinical_study/condition_browse/mesh_term +``` -# If you would like to contribute to this project +

not support path:

-For more information on contributions, please see [CONTRIBUTING.md](https://github.com/stleary/JSON-java/blob/master/docs/CONTRIBUTING.md) +``` +Index of JSONArray: /clinical_study/condition_browse/mesh_term/1 +``` -Bug fixes, code improvements, and unit test coverage changes are welcome! Because this project is currently in the maintenance phase, the kinds of changes that can be accepted are limited. For more information, please read the [FAQ](https://github.com/stleary/JSON-java/wiki/FAQ). +

List of Functions

+

org/json/XML.java:

-# Build Instructions + static Object toJSONObject(Reader reader, JSONPointer path) + static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) + static JSONObject getJsonObjectWithPath(Reader reader, JSONPointer path, JSONObject replace) + static boolean parseWithPath(XMLTokener x, JSONObject context, String name, + XMLParserConfiguration config, String[] paths, + int level, int pLevel, JSONObject replacement) -The org.json package can be built from the command line, Maven, and Gradle. The unit tests can be executed from Maven, Gradle, or individually in an IDE e.g. Eclipse. - -**Building from the command line** -*Build the class files from the package root directory src/main/java* -```` -javac org/json/*.java -```` +

Test Units (Junit)

+

org/json/junit/XMLMyTest.java:

-*Create the jar file in the current directory* -```` -jar cf json-java.jar org/json/*.class -```` + testToJSONWithReaderAndPointer1();//not using library + testToJSONWithReaderAndPointer2();//using library + //_testToJSONWithReaderAndPointer(boolean isInLibrary) -*Compile a program that uses the jar (see example code below)* -```` -javac -cp .;json-java.jar Test.java (Windows) -javac -cp .:json-java.jar Test.java (Unix Systems) -```` + testToJSONWithReaderAndPointerWithReplace1();//not using library + testToJSONWithReaderAndPointerWithReplace2();//using library + //_testToJSONWithReaderAndPointerWithReplace(boolean isInLibrary) -*Test file contents* +

Test Result (Issue537.xml)

-```` -import org.json.JSONObject; -public class Test { - public static void main(String args[]){ - JSONObject jo = new JSONObject("{ \"abc\" : \"def\" }"); - System.out.println(jo.toString()); - } -} -```` +

Task2 and task5 using library inside run less time than using library outside.

-*Execute the Test file* -```` -java -cp .;json-java.jar Test (Windows) -java -cp .:json-java.jar Test (Unix Systems) -```` +``` +testToJSONWithReaderAndPointer1 Run time:8ms +testToJSONWithReaderAndPointer2 Run time:3ms +testToJSONWithReaderAndPointerWithReplace1 Run time:5ms +testToJSONWithReaderAndPointerWithReplace2 Run time:5ms +``` -*Expected output* +

This document's intention is to explain to new-comers the basics of this project

-```` -{"abc":"def"} -```` + +

Part 1: Implementations functions

- -**Tools to build the package and execute the unit tests** +

static JSONObject toJSONObject(Reader reader, JSONPointer path)

-Execute the test suite with Maven: ``` -mvn clean test + /** + * milestone 2 + * Read an XML file into a JSON object, and extract some smaller sub-object inside, + * given a certain path (use JSONPointer). + * @param reader + * The source string. + * @param path Configuration options for the parser. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException Thrown if there is an errors while parsing the string + */ + public static Object toJSONObject(Reader reader, JSONPointer path) { + try { + getJsonObjectWithPath(reader, path, null); + } catch (JSONFoundExecption e) { + System.out.println(e.getMessage()); + if (e.getCode().equals("200")) { + return e.getJsonObject(); + } + } + return null; + } +``` +``` + /* + * + * @param level: JSON level depth + * @param pLevel: path level depth + * @param replace: JSONObject + * */ + private static JSONObject getJsonObjectWithPath(Reader reader, JSONPointer path, JSONObject replace) throws JSONFoundExecption { + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(reader); + String[] paths = path.toString().split("/"); + paths = Arrays.copyOfRange(paths, 1, paths.length); + + while (x.more()) { + x.skipPast("<"); + if (x.more()) { + parseWithPath(x, jo, null, XMLParserConfiguration.ORIGINAL, paths, 0, 0, replace); + } + } + return jo; + } +``` +``` + private static boolean parseWithPath(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, String[] paths, int level, int pLevel, JSONObject replacement) + throws JSONException, JSONFoundExecption{ + if (token == BANG) { + } else if (c == '[') { + } else if (token == QUEST) { + } else if (token == SLASH) { + } else if (token instanceof Character) { + } else { + + //add by Sunsheng Su + if(level == pLevel){ + if(paths[pLevel].equals(tagName)){ + if(pLevel < paths.length-1){ + pLevel++; + } + + //for toJSONObject with replacement + if(replacement != null && pLevel == level){ //paths.length -1 + x.skipPast(""); + //from professor Cristina test demo and responses to Justin, replacement has the last key of paths + context.put(tagName, replacement.get(tagName)); + return false; + } + } + else if(replacement == null){ + x.skipPast(""); + return false; + } + } + + for (;;){ + if (token == null) { + if (token instanceof String) { + } else if (token == SLASH) { + } else if (token == GT) { + for (;;) { + if (token == null) { + } else if (token instanceof String) { + } else if (token == LT) { + if (parseWithPath(x, jsonObject, tagName, config, paths, level+1, pLevel, replacement)) { + if (config.getForceList().contains(tagName)) { + } else { + } + + /*add by Sunsheng Su + pLevel == path.length-1 (no need) + pLevel not equal to level unless pLevel reach the last one + this block code only executed when tagName has been found totally, including children + */ + if(replacement == null && pLevel == paths.length-1 && level == pLevel-1){ + //for normal path, , like /a/b/c, this is b level and to get tagName c + if (paths[pLevel].split("[^0-9]").length != 1){ + throw new JSONFoundExecption( jsonObject.opt(paths[pLevel])); + } + //for path with index, like /a/b/c/1, this is c level, and accumulate until 1. + else{ + int index = Integer.parseInt(paths[pLevel]); + if(index == 0){ + throw new JSONFoundExecption( jsonObject.opt(config.getcDataTagName())); + } + else if(context.get(paths[pLevel-1]) instanceof JSONArray){ + if(context.getJSONArray(paths[pLevel-1]).length() == index+1) + throw new JSONFoundExecption( jsonObject.opt(config.getcDataTagName())); + } + } + } + return false; + } + } + } + } + } + } + } ``` -Execute the test suite with Gradlew: +

static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

+ +``` + /** + * milestone 2 + * Read an XML file into a JSON object, replace a sub-object on a certain key path + * with another JSON object that you construct, + * + * @param reader + * The source string. + * @param path Configuration options for the parser. + * @param replacement Configuration options for the parser. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException Thrown if there is an errors while parsing the string + */ + public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) { + + JSONObject jo = null; + try { + jo = getJsonObjectWithPath(reader, path, replacement); + } catch (JSONFoundExecption e) { + e.printStackTrace(); + } + return jo; + } ``` -gradlew clean build test + +

Part 2: Test methods

+

We don't need to have a JSON docuemnt to work. This project also admits conversions from other type of files.

+

Secondly, we can also convert from JSON to those type of files.

+ +

Test toJSONObject(Reader reader, JSONPointer path)

+ ``` + /* + * static JSONObject toJSONObject(Reader reader, JSONPointer path) + */ + //not using library + @Test + public void testToJSONWithReaderAndPointer1() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointer(false); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointer1 Run time:" + (endTime - startTime) + "ms"); + } + //using library + @Test + public void testToJSONWithReaderAndPointer2() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointer(true); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointer2 Run time:" + (endTime - startTime) + "ms"); + } -# Notes + private void _testToJSONWithReaderAndPointer(boolean isInLibrary) throws IOException { + Object actual; + if (isInLibrary) { + //do query inside library + actual = XML.toJSONObject(xmlReader, path); + write2File(actual, "inLib"); + } else { + //do query outside library + actual = path.queryFrom(XML.toJSONObject(xmlReader)); + write2File(actual, "outLib"); + } +// System.out.println(actual == null ? actual : actual.toString()); + } +``` -For more information, please see [NOTES.md](https://github.com/stleary/JSON-java/blob/master/docs/NOTES.md) -# Files +

Test toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

-For more information on files, please see [FILES.md](https://github.com/stleary/JSON-java/blob/master/docs/FILES.md) +``` + /* + * static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) + */ + //not using library + @Test + public void testToJSONWithReaderAndPointerWithReplace1() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointerWithReplace(false); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointerWithReplace1 Run time:" + (endTime - startTime) + "ms"); + } + //using library + @Test + public void testToJSONWithReaderAndPointerWithReplace2() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointerWithReplace(true); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointerWithReplace2 Run time:" + (endTime - startTime) + "ms"); + } -# Release history: + private void _testToJSONWithReaderAndPointerWithReplace(boolean isInLibrary) throws IOException { + Object actual; + if (isInLibrary) { + //do query inside library + actual = XML.toJSONObject(xmlReader, path, replacement); + write2File(actual, "replace_inLib"); + } else { + //do query outside library + String[] strs = path.toString().split("/"); + String keyName = strs[strs.length - 1]; + String parentPath = ""; + for(int i=1; iimage credit: Ismael Pérez Ortiz + + +JSON in Java [package org.json] +=============================== + +[![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json) + +**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20211205/json-20211205.jar)** + + +# Overview + +[JSON](http://www.JSON.org/) is a light-weight language-independent data interchange format. + +The JSON-Java package is a reference implementation that demonstrates how to parse JSON documents into Java objects and how to generate new JSON documents from the Java classes. + +Project goals include: +* Reliable and consistent results +* Adherence to the JSON specification +* Easy to build, use, and include in other projects +* No external dependencies +* Fast execution and low memory footprint +* Maintain backward compatibility +* Designed and tested to use on Java versions 1.6 - 1.11 + +The files in this package implement JSON encoders and decoders. The package can also convert between JSON and XML, HTTP headers, Cookies, and CDL. + +The license includes this restriction: ["The software shall be used for good, not evil."](https://en.wikipedia.org/wiki/Douglas_Crockford#%22Good,_not_Evil%22) If your conscience cannot live with that, then choose a different package. + +# If you would like to contribute to this project + +For more information on contributions, please see [CONTRIBUTING.md](https://github.com/stleary/JSON-java/blob/master/docs/CONTRIBUTING.md) + +Bug fixes, code improvements, and unit test coverage changes are welcome! Because this project is currently in the maintenance phase, the kinds of changes that can be accepted are limited. For more information, please read the [FAQ](https://github.com/stleary/JSON-java/wiki/FAQ). + +# Build Instructions + +The org.json package can be built from the command line, Maven, and Gradle. The unit tests can be executed from Maven, Gradle, or individually in an IDE e.g. Eclipse. + +**Building from the command line** + +*Build the class files from the package root directory src/main/java* +```` +javac org/json/*.java +```` + +*Create the jar file in the current directory* +```` +jar cf json-java.jar org/json/*.class +```` + +*Compile a program that uses the jar (see example code below)* +```` +javac -cp .;json-java.jar Test.java (Windows) +javac -cp .:json-java.jar Test.java (Unix Systems) +```` + +*Test file contents* + +```` +import org.json.JSONObject; +public class Test { + public static void main(String args[]){ + JSONObject jo = new JSONObject("{ \"abc\" : \"def\" }"); + System.out.println(jo.toString()); + } +} +```` + +*Execute the Test file* +```` +java -cp .;json-java.jar Test (Windows) +java -cp .:json-java.jar Test (Unix Systems) +```` + +*Expected output* + +```` +{"abc":"def"} +```` + + +**Tools to build the package and execute the unit tests** + +Execute the test suite with Maven: +``` +mvn clean test +``` + +Execute the test suite with Gradlew: + +``` +gradlew clean build test +``` + +# Notes + +For more information, please see [NOTES.md](https://github.com/stleary/JSON-java/blob/master/docs/NOTES.md) + +# Files + +For more information on files, please see [FILES.md](https://github.com/stleary/JSON-java/blob/master/docs/FILES.md) + +# Release history: + +For the release history, please see [RELEASES.md](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) From 561041e98032b3a60214cf0308c40fa41c6b1205 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 25 Jan 2022 21:03:52 -0800 Subject: [PATCH 03/24] milestone 2 implement 2 function toJSONObject(Reader reader, JSONPointer path) toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) --- src/main/java/org/json/XML.java | 388 +++++++++++++++++++++++++++++++- 1 file changed, 378 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 9b2ba8939..7aa9d833c 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -24,12 +24,16 @@ of this software and associated documentation files (the "Software"), to deal SOFTWARE. */ +import org.json.execption.JSONFoundExecption; + import java.io.Reader; import java.io.StringReader; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Arrays; import java.util.Iterator; +import java.util.List; /** @@ -119,7 +123,7 @@ public void remove() { /** * Replace special characters with XML escapes: * - *
{@code 
+     * 
{@code
      * & (ampersand) is replaced by &amp;
      * < (less than) is replaced by &lt;
      * > (greater than) is replaced by &gt;
@@ -281,6 +285,8 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
             c = x.next();
             if (c == '-') {
                 if (x.next() == '-') {
+                    //denoting this line is annotation
+                    //ignore 
                     x.skipPast("-->");
                     return false;
                 }
@@ -335,7 +341,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
             throw x.syntaxError("Misshaped tag");
 
             // Open tag <
-
+            //if token is tagName
         } else {
             tagName = (String) token;
             token = null;
@@ -348,6 +354,8 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
                 }
                 // attribute = value
                 if (token instanceof String) {
+                    //why two consecutive String, then =
+                    //maybe is loop cause, last loop
                     string = (String) token;
                     token = x.nextToken();
                     if (token == EQ) {
@@ -385,6 +393,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
                         if (nilAttributeFound) {
                             context.append(tagName, JSONObject.NULL);
                         } else if (jsonObject.length() > 0) {
+                            //in loop
                             context.append(tagName, jsonObject);
                         } else {
                             context.put(tagName, new JSONArray());
@@ -402,7 +411,10 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
 
                 } else if (token == GT) {
                     // Content, between <...> and 
+                    // after a String, if it is >, then analysis inner content
                     for (;;) {
+                        //loop goal: first time temporally save String, second time put into parent JSONObject
+                        //if has nested element, do recursion
                         token = x.nextContent();
                         if (token == null) {
                             if (tagName != null) {
@@ -423,6 +435,9 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
 
                         } else if (token == LT) {
                             // Nested element
+                            // new <
+                            //when subStruct come across opposite one, subStruct's recursion return true to subStruct
+                            //when subStruct has been accumulated, return false to parentis done
                             if (parse(x, jsonObject, tagName, config)) {
                                 if (config.getForceList().contains(tagName)) {
                                     // Force the value to be an array
@@ -436,15 +451,300 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
                                     }
                                 } else {
                                     if (jsonObject.length() == 0) {
+                                        //let values of same tagName, save as JSONArray
                                         context.accumulate(tagName, "");
                                     } else if (jsonObject.length() == 1
                                             && jsonObject.opt(config.getcDataTagName()) != null) {
+                                        //enter into subStruct and then return true, come here when subStruct is value
+                                        //config.getcDataTagName():"content" is temporally saving key
                                         context.accumulate(tagName, jsonObject.opt(config.getcDataTagName()));
                                     } else {
+                                        //when subStruct has nested element like 
111Ave
context.accumulate(tagName, jsonObject); } } - + + return false; + } + } + } + } else { + throw x.syntaxError("Misshaped tag"); + } + } + } + } + + + + + /** + * Scan the content following the named tag, attaching it to the context. + * + * @param x + * The XMLTokener containing the source string. + * @param context + * The JSONObject that will include the new material. + * @param name + * The tag name. + * @param paths paths of JSONPointer + * @return true if the close tag is processed. + * @throws JSONException + */ + private static boolean parseWithPath(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, String[] paths, int level, int pLevel, JSONObject replacement) + throws JSONException, JSONFoundExecption { + char c; + int i; + JSONObject jsonObject = null; + String string; + String tagName; + Object token; + XMLXsiTypeConverter xmlXsiTypeConverter; + + // Test for and skip past these forms: + // + // + // + // + // Report errors for these forms: + // <> + // <= + // << + + token = x.nextToken(); + + // + x.skipPast("-->"); + return false; + } + x.back(); + } else if (c == '[') { + token = x.nextToken(); + if ("CDATA".equals(token)) { + if (x.next() == '[') { + string = x.nextCDATA(); + if (string.length() > 0) { + context.accumulate(config.getcDataTagName(), string); + } + return false; + } + } + throw x.syntaxError("Expected 'CDATA['"); + } + i = 1; + do { + token = x.nextMeta(); + if (token == null) { + throw x.syntaxError("Missing '>' after ' 0); + return false; + } else if (token == QUEST) { + + // "); + return false; + } else if (token == SLASH) { + + // Close tag "); + //from professor Cristina test demo and responses to Justin, replacement has the last key of paths + context.put(tagName, replacement.get(tagName)); + return false; + } + } + else if(replacement == null){ + x.skipPast(""); + return false; + } + } + + for (;;) { + if (token == null) { + token = x.nextToken(); + } + // attribute = value + if (token instanceof String) { + //why two consecutive String, then = + //maybe is loop cause, last loop + string = (String) token; + token = x.nextToken(); + if (token == EQ) { + token = x.nextToken(); + if (!(token instanceof String)) { + throw x.syntaxError("Missing value"); + } + + if (config.isConvertNilAttributeToNull() + && NULL_ATTR.equals(string) + && Boolean.parseBoolean((String) token)) { + nilAttributeFound = true; + } else if(config.getXsiTypeMap() != null && !config.getXsiTypeMap().isEmpty() + && TYPE_ATTR.equals(string)) { + xmlXsiTypeConverter = config.getXsiTypeMap().get(token); + } else if (!nilAttributeFound) { + jsonObject.accumulate(string, + config.isKeepStrings() + ? ((String) token) + : stringToValue((String) token)); + } + token = null; + } else { + jsonObject.accumulate(string, ""); + } + + } else if (token == SLASH) { + // Empty tag <.../> + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped tag"); + } + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (nilAttributeFound) { + context.append(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + //in loop + context.append(tagName, jsonObject); + } else { + context.put(tagName, new JSONArray()); + } + } else { + if (nilAttributeFound) { + context.accumulate(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.accumulate(tagName, jsonObject); + } else { + context.accumulate(tagName, ""); + } + } + return false; + + } else if (token == GT) { + // Content, between <...> and + // after a String, if it is >, then analysis inner content + for (;;) { + //loop goal: first time temporally save String, second time put into parent JSONObject + //if has nested element, do recursion + token = x.nextContent(); + if (token == null) { + if (tagName != null) { + throw x.syntaxError("Unclosed tag " + tagName); + } + return false; + } else if (token instanceof String) { + string = (String) token; + if (string.length() > 0) { + if(xmlXsiTypeConverter != null) { + jsonObject.accumulate(config.getcDataTagName(), + stringToValue(string, xmlXsiTypeConverter)); + } else { + jsonObject.accumulate(config.getcDataTagName(), + config.isKeepStrings() ? string : stringToValue(string)); + } + } + + } else if (token == LT) { + // still has <, denoting Nested element + // new < + //when subStruct come across opposite one, subStruct's recursion return true to subStruct. So, the tagName's content will be add + //when subStruct has been accumulated, return false to calling position + //address -> "<" -> street -> "<" -> /street -> true to add street into JSONObject -> false -> address + //address -> "<" -> zipcode -> "<" -> /zipcode -> true to add zipcode into JSONObject -> false -> address + //address -> "<" -> /address -> true to add address(JSONObject[street,zipcode]) to JSONObject(upper level) + + if (parseWithPath(x, jsonObject, tagName, config, paths, level+1, pLevel, replacement)) { + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (jsonObject.length() == 0) { + context.put(tagName, new JSONArray()); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + context.append(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.append(tagName, jsonObject); + } + } else { + if (jsonObject.length() == 0) { + //let values of same tagName, save as JSONArray + context.accumulate(tagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + //todo why jsonObject.length == 1 1:array reason 2:same name with content + //enter into subStruct and then return true, come here when subStruct is value + //config.getcDataTagName(): key:"content" is temporally saved with String + context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + //when subStruct has nested element like
111Ave
+ context.accumulate(tagName, jsonObject); + } + } + + //add by Sunsheng Su + //pLevel == path.length-1 (no need) + //pLevel not equal to level unless pLevel reach the last one + //this block code only executed when tagName has been found totally, including children + if(replacement == null && pLevel == paths.length-1 && level == pLevel-1){ + //for normal path, , like /a/b/c, this is b level and to get tagName c + if (paths[pLevel].split("[^0-9]").length != 1){ + throw new JSONFoundExecption( jsonObject.opt(paths[pLevel])); + } + //for path with index, like /a/b/c/1, this is c level, and accumulate until 1. + else{ + int index = Integer.parseInt(paths[pLevel]); + if(index == 0){ + throw new JSONFoundExecption( jsonObject.opt(config.getcDataTagName())); + } + else if(context.get(paths[pLevel-1]) instanceof JSONArray){ + if(context.getJSONArray(paths[pLevel-1]).length() == index+1) + throw new JSONFoundExecption( jsonObject.opt(config.getcDataTagName())); + } + } + } return false; } } @@ -508,7 +808,7 @@ public static Object stringToValue(String string) { } return string; } - + /** * direct copy of {@link JSONObject#stringToNumber(String)} to maintain Android support. */ @@ -555,7 +855,7 @@ private static Number stringToNumber(final String val) throws NumberFormatExcept // integer representation. // This will narrow any values to the smallest reasonable Object representation // (Integer, Long, or BigInteger) - + // BigInteger down conversion: We use a similar bitLength compare as // BigInteger#intValueExact uses. Increases GC, but objects hold // only what they need. i.e. Less runtime overhead if the value is @@ -571,7 +871,7 @@ private static Number stringToNumber(final String val) throws NumberFormatExcept } throw new NumberFormatException("val ["+val+"] is not a valid number."); } - + /** * direct copy of {@link JSONObject#isDecimalNotation(String)} to maintain Android support. */ @@ -589,7 +889,7 @@ private static boolean isDecimalNotation(final String val) { * name/value pairs and arrays of values. JSON does not does not like to * distinguish between elements and attributes. Sequences of similar * elements are represented as JSONArrays. Content text may be placed in a - * "content" member. Comments, prologs, DTDs, and
{@code 
+     * "content" member. Comments, prologs, DTDs, and 
{@code
      * <[ [ ]]>}
* are ignored. * @@ -610,7 +910,7 @@ public static JSONObject toJSONObject(String string) throws JSONException { * name/value pairs and arrays of values. JSON does not does not like to * distinguish between elements and attributes. Sequences of similar * elements are represented as JSONArrays. Content text may be placed in a - * "content" member. Comments, prologs, DTDs, and
{@code 
+     * "content" member. Comments, prologs, DTDs, and 
{@code
      * <[ [ ]]>}
* are ignored. * @@ -690,7 +990,7 @@ public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration conf * name/value pairs and arrays of values. JSON does not does not like to * distinguish between elements and attributes. Sequences of similar * elements are represented as JSONArrays. Content text may be placed in a - * "content" member. Comments, prologs, DTDs, and
{@code 
+     * "content" member. Comments, prologs, DTDs, and 
{@code
      * <[ [ ]]>}
* are ignored. * @@ -716,7 +1016,7 @@ public static JSONObject toJSONObject(String string, boolean keepStrings) throws * name/value pairs and arrays of values. JSON does not does not like to * distinguish between elements and attributes. Sequences of similar * elements are represented as JSONArrays. Content text may be placed in a - * "content" member. Comments, prologs, DTDs, and
{@code 
+     * "content" member. Comments, prologs, DTDs, and 
{@code
      * <[ [ ]]>}
* are ignored. * @@ -733,6 +1033,74 @@ public static JSONObject toJSONObject(String string, XMLParserConfiguration conf return toJSONObject(new StringReader(string), config); } + + /** + * milestone 2 + * Read an XML file into a JSON object, and extract some smaller sub-object inside, + * given a certain path (use JSONPointer). + * @param reader + * The source string. + * @param path Configuration options for the parser. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException Thrown if there is an errors while parsing the string + */ + public static Object toJSONObject(Reader reader, JSONPointer path) { + try { + getJsonObjectWithPath(reader, path, null); + } catch (JSONFoundExecption e) { +// System.out.println(e.getMessage()); + if (e.getCode().equals("200")) { + return e.getJsonObject(); + } + } + return null; + } + + /* + * + * @param level: JSON level depth + * @param pLevel: path level depth + * @param replace: JSONObject + * */ + private static JSONObject getJsonObjectWithPath(Reader reader, JSONPointer path, JSONObject replace) throws JSONFoundExecption { + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(reader); + String[] paths = path.toString().split("/"); + paths = Arrays.copyOfRange(paths, 1, paths.length); + + while (x.more()) { + x.skipPast("<"); + if (x.more()) { + parseWithPath(x, jo, null, XMLParserConfiguration.ORIGINAL, paths, 0, 0, replace); + } + } + return jo; + } + + /** + * milestone 2 + * Read an XML file into a JSON object, replace a sub-object on a certain key path + * with another JSON object that you construct, + * + * @param reader + * The source string. + * @param path Configuration options for the parser. + * @param replacement Configuration options for the parser. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException Thrown if there is an errors while parsing the string + */ + public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) { + + JSONObject jo = null; + try { + jo = getJsonObjectWithPath(reader, path, replacement); + } catch (JSONFoundExecption e) { + e.printStackTrace(); + } + return jo; + } + + /** * Convert a JSONObject into a well-formed, element-normal XML string. * From 2bc357dc3a03236cd6c89d0d89144e079c40114d Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 25 Jan 2022 21:04:26 -0800 Subject: [PATCH 04/24] support function toJSONObject(Reader reader, JSONPointer path) --- .../json/execption/JSONFoundExecption.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/java/org/json/execption/JSONFoundExecption.java diff --git a/src/main/java/org/json/execption/JSONFoundExecption.java b/src/main/java/org/json/execption/JSONFoundExecption.java new file mode 100644 index 000000000..47d503ab1 --- /dev/null +++ b/src/main/java/org/json/execption/JSONFoundExecption.java @@ -0,0 +1,55 @@ +package org.json.execption; + +import org.json.JSONObject; + +/** + * @program: JSON-java + * @description: + * @author: Mr. Su + * @create: 2022-01-22 15:33 + **/ + +public class JSONFoundExecption extends Exception { + private String code; + + private Object jsonObject; + + + + public JSONFoundExecption(String code, String message) { + super(message); + this.setCode(code); + } + + public JSONFoundExecption(String code, String message, Throwable cause) { + super(message, cause); + this.setCode(code); + } + public JSONFoundExecption(Object jsonObject) { + super("success"); + if(jsonObject==null){ + this.setCode("404"); + } + else{ + this.setCode("200"); + } + this.setJsonObject(jsonObject); + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Object getJsonObject() { + return jsonObject; + } + + public void setJsonObject(Object jsonObject) { + this.jsonObject = jsonObject; + } + +} From a903104b97b063ad3ee0aad1dd360535c47034a7 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 25 Jan 2022 21:06:24 -0800 Subject: [PATCH 05/24] delete 1 line --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index be89402a8..183c57ed4 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,6 @@ testToJSONWithReaderAndPointerWithReplace1 Run time:5ms testToJSONWithReaderAndPointerWithReplace2 Run time:5ms ``` -

This document's intention is to explain to new-comers the basics of this project

-

Part 1: Implementations functions

From 343ed72c0757e4df7b798028b9b973ebb35ac0c1 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 25 Jan 2022 21:08:37 -0800 Subject: [PATCH 06/24] add some lines --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 183c57ed4..1d2805136 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ Index of JSONArray: /clinical_study/condition_browse/mesh_term/1 XMLParserConfiguration config, String[] paths, int level, int pLevel, JSONObject replacement) +

org/json/execption/JSONFoundExecption.java:

+ + JSONFoundExecption(Object jsonObject) +

Test Units (Junit)

org/json/junit/XMLMyTest.java:

From 5f21b2274f7b91cc91dfb5161f468703e546722f Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 25 Jan 2022 21:29:57 -0800 Subject: [PATCH 07/24] add some comments --- src/main/java/org/json/XML.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 7aa9d833c..4fe77fef2 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -487,7 +487,14 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP * The JSONObject that will include the new material. * @param name * The tag name. - * @param paths paths of JSONPointer + * @param paths + * paths of JSONPointer + * @param level + * depth level of xml/JSONObject + * @param pLevel + * depth level of paths + * @param replacement + * JSONObject that is going to replace the path's object * @return true if the close tag is processed. * @throws JSONException */ @@ -598,6 +605,7 @@ private static boolean parseWithPath(XMLTokener x, JSONObject context, String na return false; } } + //for toJSONObject with extract object, skip other tagNames else if(replacement == null){ x.skipPast(""); return false; From 21431c5f6478a6669a01605b92cbccdbb3698fd0 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 8 Feb 2022 14:57:02 -0800 Subject: [PATCH 08/24] add milestone 3 readme --- README.md | 322 +-------------------------------------------------- README_M2.md | 321 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+), 320 deletions(-) create mode 100644 README_M2.md diff --git a/README.md b/README.md index 1d2805136..127542b8f 100644 --- a/README.md +++ b/README.md @@ -1,321 +1,3 @@ -

Milestone 2

- -

Summary of Support & Not Support

- -

toJSONObject(Reader reader, JSONPointer path)

- -

support path:

- -``` -JSONObject: /clinical_study/required_header - -JSONArray: /clinical_study/condition_browse/mesh_term - -Index of JSONArray: /clinical_study/condition_browse/mesh_term/1 -``` - -

not support path:

- -``` -Nested JSONArray: /clinical_study/1/condition_browse/mesh_term/1 -``` - -

toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

- -

support path:

- -``` -JSONObject: /clinical_study/required_header - -JSONArray: /clinical_study/condition_browse/mesh_term -``` - -

not support path:

- -``` -Index of JSONArray: /clinical_study/condition_browse/mesh_term/1 -``` - -

List of Functions

-

org/json/XML.java:

- - static Object toJSONObject(Reader reader, JSONPointer path) - static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) - static JSONObject getJsonObjectWithPath(Reader reader, JSONPointer path, JSONObject replace) - static boolean parseWithPath(XMLTokener x, JSONObject context, String name, - XMLParserConfiguration config, String[] paths, - int level, int pLevel, JSONObject replacement) - -

org/json/execption/JSONFoundExecption.java:

- - JSONFoundExecption(Object jsonObject) - - -

Test Units (Junit)

-

org/json/junit/XMLMyTest.java:

- - testToJSONWithReaderAndPointer1();//not using library - testToJSONWithReaderAndPointer2();//using library - //_testToJSONWithReaderAndPointer(boolean isInLibrary) - - testToJSONWithReaderAndPointerWithReplace1();//not using library - testToJSONWithReaderAndPointerWithReplace2();//using library - //_testToJSONWithReaderAndPointerWithReplace(boolean isInLibrary) - -

Test Result (Issue537.xml)

- -

Task2 and task5 using library inside run less time than using library outside.

- -``` -testToJSONWithReaderAndPointer1 Run time:8ms -testToJSONWithReaderAndPointer2 Run time:3ms -testToJSONWithReaderAndPointerWithReplace1 Run time:5ms -testToJSONWithReaderAndPointerWithReplace2 Run time:5ms -``` - - -

Part 1: Implementations functions

- -

static JSONObject toJSONObject(Reader reader, JSONPointer path)

- -``` - /** - * milestone 2 - * Read an XML file into a JSON object, and extract some smaller sub-object inside, - * given a certain path (use JSONPointer). - * @param reader - * The source string. - * @param path Configuration options for the parser. - * @return A JSONObject containing the structured data from the XML string. - * @throws JSONException Thrown if there is an errors while parsing the string - */ - public static Object toJSONObject(Reader reader, JSONPointer path) { - try { - getJsonObjectWithPath(reader, path, null); - } catch (JSONFoundExecption e) { - System.out.println(e.getMessage()); - if (e.getCode().equals("200")) { - return e.getJsonObject(); - } - } - return null; - } -``` -``` - /* - * - * @param level: JSON level depth - * @param pLevel: path level depth - * @param replace: JSONObject - * */ - private static JSONObject getJsonObjectWithPath(Reader reader, JSONPointer path, JSONObject replace) throws JSONFoundExecption { - JSONObject jo = new JSONObject(); - XMLTokener x = new XMLTokener(reader); - String[] paths = path.toString().split("/"); - paths = Arrays.copyOfRange(paths, 1, paths.length); - - while (x.more()) { - x.skipPast("<"); - if (x.more()) { - parseWithPath(x, jo, null, XMLParserConfiguration.ORIGINAL, paths, 0, 0, replace); - } - } - return jo; - } -``` -``` - private static boolean parseWithPath(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, String[] paths, int level, int pLevel, JSONObject replacement) - throws JSONException, JSONFoundExecption{ - if (token == BANG) { - } else if (c == '[') { - } else if (token == QUEST) { - } else if (token == SLASH) { - } else if (token instanceof Character) { - } else { - - //add by Sunsheng Su - if(level == pLevel){ - if(paths[pLevel].equals(tagName)){ - if(pLevel < paths.length-1){ - pLevel++; - } - - //for toJSONObject with replacement - if(replacement != null && pLevel == level){ //paths.length -1 - x.skipPast(""); - //from professor Cristina test demo and responses to Justin, replacement has the last key of paths - context.put(tagName, replacement.get(tagName)); - return false; - } - } - else if(replacement == null){ - x.skipPast(""); - return false; - } - } - - for (;;){ - if (token == null) { - if (token instanceof String) { - } else if (token == SLASH) { - } else if (token == GT) { - for (;;) { - if (token == null) { - } else if (token instanceof String) { - } else if (token == LT) { - if (parseWithPath(x, jsonObject, tagName, config, paths, level+1, pLevel, replacement)) { - if (config.getForceList().contains(tagName)) { - } else { - } - - /*add by Sunsheng Su - pLevel == path.length-1 (no need) - pLevel not equal to level unless pLevel reach the last one - this block code only executed when tagName has been found totally, including children - */ - if(replacement == null && pLevel == paths.length-1 && level == pLevel-1){ - //for normal path, , like /a/b/c, this is b level and to get tagName c - if (paths[pLevel].split("[^0-9]").length != 1){ - throw new JSONFoundExecption( jsonObject.opt(paths[pLevel])); - } - //for path with index, like /a/b/c/1, this is c level, and accumulate until 1. - else{ - int index = Integer.parseInt(paths[pLevel]); - if(index == 0){ - throw new JSONFoundExecption( jsonObject.opt(config.getcDataTagName())); - } - else if(context.get(paths[pLevel-1]) instanceof JSONArray){ - if(context.getJSONArray(paths[pLevel-1]).length() == index+1) - throw new JSONFoundExecption( jsonObject.opt(config.getcDataTagName())); - } - } - } - return false; - } - } - } - } - } - } - } -``` - - -

static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

- -``` - /** - * milestone 2 - * Read an XML file into a JSON object, replace a sub-object on a certain key path - * with another JSON object that you construct, - * - * @param reader - * The source string. - * @param path Configuration options for the parser. - * @param replacement Configuration options for the parser. - * @return A JSONObject containing the structured data from the XML string. - * @throws JSONException Thrown if there is an errors while parsing the string - */ - public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) { - - JSONObject jo = null; - try { - jo = getJsonObjectWithPath(reader, path, replacement); - } catch (JSONFoundExecption e) { - e.printStackTrace(); - } - return jo; - } -``` - -

Part 2: Test methods

-

We don't need to have a JSON docuemnt to work. This project also admits conversions from other type of files.

-

Secondly, we can also convert from JSON to those type of files.

- -

Test toJSONObject(Reader reader, JSONPointer path)

- -``` - /* - * static JSONObject toJSONObject(Reader reader, JSONPointer path) - */ - //not using library - @Test - public void testToJSONWithReaderAndPointer1() throws IOException { - long startTime = System.currentTimeMillis(); - _testToJSONWithReaderAndPointer(false); - long endTime = System.currentTimeMillis(); //get end time - System.out.println("testToJSONWithReaderAndPointer1 Run time:" + (endTime - startTime) + "ms"); - } - //using library - @Test - public void testToJSONWithReaderAndPointer2() throws IOException { - long startTime = System.currentTimeMillis(); - _testToJSONWithReaderAndPointer(true); - long endTime = System.currentTimeMillis(); //get end time - System.out.println("testToJSONWithReaderAndPointer2 Run time:" + (endTime - startTime) + "ms"); - } - - private void _testToJSONWithReaderAndPointer(boolean isInLibrary) throws IOException { - Object actual; - if (isInLibrary) { - //do query inside library - actual = XML.toJSONObject(xmlReader, path); - write2File(actual, "inLib"); - } else { - //do query outside library - actual = path.queryFrom(XML.toJSONObject(xmlReader)); - write2File(actual, "outLib"); - } -// System.out.println(actual == null ? actual : actual.toString()); - } -``` - - -

Test toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

- -``` - /* - * static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) - */ - //not using library - @Test - public void testToJSONWithReaderAndPointerWithReplace1() throws IOException { - long startTime = System.currentTimeMillis(); - _testToJSONWithReaderAndPointerWithReplace(false); - long endTime = System.currentTimeMillis(); //get end time - System.out.println("testToJSONWithReaderAndPointerWithReplace1 Run time:" + (endTime - startTime) + "ms"); - } - //using library - @Test - public void testToJSONWithReaderAndPointerWithReplace2() throws IOException { - long startTime = System.currentTimeMillis(); - _testToJSONWithReaderAndPointerWithReplace(true); - long endTime = System.currentTimeMillis(); //get end time - System.out.println("testToJSONWithReaderAndPointerWithReplace2 Run time:" + (endTime - startTime) + "ms"); - } - - private void _testToJSONWithReaderAndPointerWithReplace(boolean isInLibrary) throws IOException { - Object actual; - if (isInLibrary) { - //do query inside library - actual = XML.toJSONObject(xmlReader, path, replacement); - write2File(actual, "replace_inLib"); - } else { - //do query outside library - String[] strs = path.toString().split("/"); - String keyName = strs[strs.length - 1]; - String parentPath = ""; - for(int i=1; iMilestone 2 + +

Summary of Support & Not Support

+ +

toJSONObject(Reader reader, JSONPointer path)

+ +

support path:

+ +``` +JSONObject: /clinical_study/required_header + +JSONArray: /clinical_study/condition_browse/mesh_term + +Index of JSONArray: /clinical_study/condition_browse/mesh_term/1 +``` + +

not support path:

+ +``` +Nested JSONArray: /clinical_study/1/condition_browse/mesh_term/1 +``` + +

toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

+ +

support path:

+ +``` +JSONObject: /clinical_study/required_header + +JSONArray: /clinical_study/condition_browse/mesh_term +``` + +

not support path:

+ +``` +Index of JSONArray: /clinical_study/condition_browse/mesh_term/1 +``` + +

List of Functions

+

org/json/XML.java:

+ + static Object toJSONObject(Reader reader, JSONPointer path) + static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) + static JSONObject getJsonObjectWithPath(Reader reader, JSONPointer path, JSONObject replace) + static boolean parseWithPath(XMLTokener x, JSONObject context, String name, + XMLParserConfiguration config, String[] paths, + int level, int pLevel, JSONObject replacement) + +

org/json/execption/JSONFoundExecption.java:

+ + JSONFoundExecption(Object jsonObject) + + +

Test Units (Junit)

+

org/json/junit/XMLMyTest.java:

+ + testToJSONWithReaderAndPointer1();//not using library + testToJSONWithReaderAndPointer2();//using library + //_testToJSONWithReaderAndPointer(boolean isInLibrary) + + testToJSONWithReaderAndPointerWithReplace1();//not using library + testToJSONWithReaderAndPointerWithReplace2();//using library + //_testToJSONWithReaderAndPointerWithReplace(boolean isInLibrary) + +

Test Result (Issue537.xml)

+ +

Task2 and task5 using library inside run less time than using library outside.

+ +``` +testToJSONWithReaderAndPointer1 Run time:8ms +testToJSONWithReaderAndPointer2 Run time:3ms +testToJSONWithReaderAndPointerWithReplace1 Run time:5ms +testToJSONWithReaderAndPointerWithReplace2 Run time:5ms +``` + + +

Part 1: Implementations functions

+ +

static JSONObject toJSONObject(Reader reader, JSONPointer path)

+ +``` + /** + * milestone 2 + * Read an XML file into a JSON object, and extract some smaller sub-object inside, + * given a certain path (use JSONPointer). + * @param reader + * The source string. + * @param path Configuration options for the parser. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException Thrown if there is an errors while parsing the string + */ + public static Object toJSONObject(Reader reader, JSONPointer path) { + try { + getJsonObjectWithPath(reader, path, null); + } catch (JSONFoundExecption e) { + System.out.println(e.getMessage()); + if (e.getCode().equals("200")) { + return e.getJsonObject(); + } + } + return null; + } +``` +``` + /* + * + * @param level: JSON level depth + * @param pLevel: path level depth + * @param replace: JSONObject + * */ + private static JSONObject getJsonObjectWithPath(Reader reader, JSONPointer path, JSONObject replace) throws JSONFoundExecption { + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(reader); + String[] paths = path.toString().split("/"); + paths = Arrays.copyOfRange(paths, 1, paths.length); + + while (x.more()) { + x.skipPast("<"); + if (x.more()) { + parseWithPath(x, jo, null, XMLParserConfiguration.ORIGINAL, paths, 0, 0, replace); + } + } + return jo; + } +``` +``` + private static boolean parseWithPath(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, String[] paths, int level, int pLevel, JSONObject replacement) + throws JSONException, JSONFoundExecption{ + if (token == BANG) { + } else if (c == '[') { + } else if (token == QUEST) { + } else if (token == SLASH) { + } else if (token instanceof Character) { + } else { + + //add by Sunsheng Su + if(level == pLevel){ + if(paths[pLevel].equals(tagName)){ + if(pLevel < paths.length-1){ + pLevel++; + } + + //for toJSONObject with replacement + if(replacement != null && pLevel == level){ //paths.length -1 + x.skipPast(""); + //from professor Cristina test demo and responses to Justin, replacement has the last key of paths + context.put(tagName, replacement.get(tagName)); + return false; + } + } + else if(replacement == null){ + x.skipPast(""); + return false; + } + } + + for (;;){ + if (token == null) { + if (token instanceof String) { + } else if (token == SLASH) { + } else if (token == GT) { + for (;;) { + if (token == null) { + } else if (token instanceof String) { + } else if (token == LT) { + if (parseWithPath(x, jsonObject, tagName, config, paths, level+1, pLevel, replacement)) { + if (config.getForceList().contains(tagName)) { + } else { + } + + /*add by Sunsheng Su + pLevel == path.length-1 (no need) + pLevel not equal to level unless pLevel reach the last one + this block code only executed when tagName has been found totally, including children + */ + if(replacement == null && pLevel == paths.length-1 && level == pLevel-1){ + //for normal path, , like /a/b/c, this is b level and to get tagName c + if (paths[pLevel].split("[^0-9]").length != 1){ + throw new JSONFoundExecption( jsonObject.opt(paths[pLevel])); + } + //for path with index, like /a/b/c/1, this is c level, and accumulate until 1. + else{ + int index = Integer.parseInt(paths[pLevel]); + if(index == 0){ + throw new JSONFoundExecption( jsonObject.opt(config.getcDataTagName())); + } + else if(context.get(paths[pLevel-1]) instanceof JSONArray){ + if(context.getJSONArray(paths[pLevel-1]).length() == index+1) + throw new JSONFoundExecption( jsonObject.opt(config.getcDataTagName())); + } + } + } + return false; + } + } + } + } + } + } + } +``` + + +

static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

+ +``` + /** + * milestone 2 + * Read an XML file into a JSON object, replace a sub-object on a certain key path + * with another JSON object that you construct, + * + * @param reader + * The source string. + * @param path Configuration options for the parser. + * @param replacement Configuration options for the parser. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException Thrown if there is an errors while parsing the string + */ + public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) { + + JSONObject jo = null; + try { + jo = getJsonObjectWithPath(reader, path, replacement); + } catch (JSONFoundExecption e) { + e.printStackTrace(); + } + return jo; + } +``` + +

Part 2: Test methods

+

We don't need to have a JSON docuemnt to work. This project also admits conversions from other type of files.

+

Secondly, we can also convert from JSON to those type of files.

+ +

Test toJSONObject(Reader reader, JSONPointer path)

+ +``` + /* + * static JSONObject toJSONObject(Reader reader, JSONPointer path) + */ + //not using library + @Test + public void testToJSONWithReaderAndPointer1() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointer(false); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointer1 Run time:" + (endTime - startTime) + "ms"); + } + //using library + @Test + public void testToJSONWithReaderAndPointer2() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointer(true); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointer2 Run time:" + (endTime - startTime) + "ms"); + } + + private void _testToJSONWithReaderAndPointer(boolean isInLibrary) throws IOException { + Object actual; + if (isInLibrary) { + //do query inside library + actual = XML.toJSONObject(xmlReader, path); + write2File(actual, "inLib"); + } else { + //do query outside library + actual = path.queryFrom(XML.toJSONObject(xmlReader)); + write2File(actual, "outLib"); + } +// System.out.println(actual == null ? actual : actual.toString()); + } +``` + + +

Test toJSONObject(Reader reader, JSONPointer path, JSONObject replacement)

+ +``` + /* + * static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) + */ + //not using library + @Test + public void testToJSONWithReaderAndPointerWithReplace1() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointerWithReplace(false); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointerWithReplace1 Run time:" + (endTime - startTime) + "ms"); + } + //using library + @Test + public void testToJSONWithReaderAndPointerWithReplace2() throws IOException { + long startTime = System.currentTimeMillis(); + _testToJSONWithReaderAndPointerWithReplace(true); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONWithReaderAndPointerWithReplace2 Run time:" + (endTime - startTime) + "ms"); + } + + private void _testToJSONWithReaderAndPointerWithReplace(boolean isInLibrary) throws IOException { + Object actual; + if (isInLibrary) { + //do query inside library + actual = XML.toJSONObject(xmlReader, path, replacement); + write2File(actual, "replace_inLib"); + } else { + //do query outside library + String[] strs = path.toString().split("/"); + String keyName = strs[strs.length - 1]; + String parentPath = ""; + for(int i=1; i Date: Tue, 8 Feb 2022 14:57:49 -0800 Subject: [PATCH 09/24] adjust jdk setting to 1.8 --- pom.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fac7a2ea6..4cdc0926d 100644 --- a/pom.xml +++ b/pom.xml @@ -118,8 +118,10 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + + + 1.8 + 1.8 From b466293da4f5d2465461a76f323f688e33630d82 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 8 Feb 2022 14:58:05 -0800 Subject: [PATCH 10/24] add milestone 3 readme --- README_M3.md | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 README_M3.md diff --git a/README_M3.md b/README_M3.md new file mode 100644 index 000000000..b3b7bb3df --- /dev/null +++ b/README_M3.md @@ -0,0 +1,173 @@ +

Milestone 3

+ +Overload function "toJSONObject" and "parse". + +``` +public static JSONObject toJSONObject(Reader reader, Function keyTransformer); + +private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, Function keyTransformer); +``` + + +

Maven

+The project is based on Maven. +Since Milestone 3 uses Function as a parameter, it needs to adjust the jdk to 1.8. + +```xml + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + + + 1.8 + 1.8 + + +``` + +

Test Units (Junit)

+ +

src/test/java/org/json/junit/XMLMyTestM3.java:

+ +``` +//replace key in client code +public void testToJSONKeyReplaceInClient(); + +//replace key in the library +public void testToJSONKeyReplaceWithKeyTransformer() throws IOException { + ... + Function func = cc-> { + return "swe262_"+cc; + }; + ... +} + +//key reverse in the library +public void testToJSONKeyReverseWithKeyTransformer() throws IOException { + ... + Function func = cc-> { + StringBuilder sb = new StringBuilder((String) cc); + sb.reverse(); + return sb.toString(); + }; + ... +} +``` + + +

Key replace in client VS. doing it inside the library

+ +| function \ file size | 8K | 250MB | 490MB | +| --- | --- | --- | --- | +| testToJSONKeyReplaceInClient | 18ms | 43,045ms | 103,838ms | +| testToJSONKeyReplaceWithKeyTransformer | 78ms | 33,132ms | 89,854ms | + +Function "testToJSONKeyReplaceInClient" replaces key in client code. +Function "testToJSONKeyReplaceWithKeyTransformer" replaces key in library, not in client code. + +From testing result, we can see that for a small file, doing key replace in client code runs faster than doing it inside library. +But for big files, doing key replace inside library runs much faster than doing it in client code. + +I guess that replacing the key in the library by passing a function of KeyTransformer has additional calling consuming time. +For small files, this calling consuming time is significant. But for big files, it can be trivial. + + +

Implement

+ +To execute the task of key transformation, put the function of "keyTransformer" in three places. + +

1. deals with global tagName for most cases: put keyTransformer inside callback "parse" (tagName meet with pair end tagName).

+ +``` + if (parse(x, jsonObject, tagName, config, keyTransformer)) { + tagName = (String) keyTransformer.apply(tagName); + ... + } +``` + +

2. to deal with special xml format, like below

+ +```xml +September 12, 2019 +``` + +```json +{ + "swe262_last_update_posted": { + "swe262_type": "Actual", + "swe262_content": "September 12, 2019" + } +} +``` + +

2.1 When First token is a String, second token is also a String, and third token is a "=".

+ +Like below xml, first token is "last_update_posted", second token is "type", third token is "=" + +```xml +September 12, 2019 +``` + +If it comes across this case, it needs to put a keyTransformer here. + +``` +if (token instanceof String) { + string = (String) token; + //put keyTransformer + string = (String) keyTransformer.apply(string); + if (token == EQ) { + if (config.isConvertNilAttributeToNull() + } else if(config.getXsiTypeMap() != null && !config.getXsiTypeMap().isEmpty() + } else if (!nilAttributeFound) { + jsonObject.accumulate(string, + config.isKeepStrings() + ? ((String) token) + : stringToValue((String) token)); + } + ... + } + ... +} +``` + +In this circumstance that it would have added "type" tag into "last_update_posted", like below: + +```json +{ + "swe262_last_update_posted": { + "swe262_type": "Actual" + } +} +``` + +

2.2 When it is going to add content of "September 12, 2019" directly to JsonObject "last_update_posted".

+ +Here is to deal with this kind of token tagName "content". + +``` +//add "if" predicate logic +if(jsonObject.length()>0){ + //put keyTransformer + String configTagName = (String) keyTransformer.apply(config.getcDataTagName()); + jsonObject.accumulate(configTagName, + config.isKeepStrings() ? string : stringToValue(string)); +} +else { + jsonObject.accumulate(config.getcDataTagName(), + config.isKeepStrings() ? string : stringToValue(string)); +} +``` + +By putting keyTransformer in these two places, it can successfully deal with this case and turn it into correct output. + +```json +{ + "swe262_last_update_posted": { + "swe262_type": "Actual", + "swe262_content": "September 12, 2019" + } +} +``` + From 3cc1201ce6e95224b86b961a33ba5e6eda384ca9 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 8 Feb 2022 14:58:49 -0800 Subject: [PATCH 11/24] milestone 3 xml.java --- src/main/java/org/json/XML.java | 303 +++++++++++++++++++++++++++++++- 1 file changed, 299 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 4fe77fef2..884890f3a 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -34,6 +34,8 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; /** @@ -493,7 +495,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP * depth level of xml/JSONObject * @param pLevel * depth level of paths - * @param replacement + * @param replacement for Milestone 2 * JSONObject that is going to replace the path's object * @return true if the close tag is processed. * @throws JSONException @@ -618,8 +620,8 @@ else if(replacement == null){ } // attribute = value if (token instanceof String) { - //why two consecutive String, then = - //maybe is loop cause, last loop + //for this xml + //September 12, 2019 string = (String) token; token = x.nextToken(); if (token == EQ) { @@ -1068,7 +1070,7 @@ public static Object toJSONObject(Reader reader, JSONPointer path) { * * @param level: JSON level depth * @param pLevel: path level depth - * @param replace: JSONObject + * @param replace: JSONObject for Milestone 2 * */ private static JSONObject getJsonObjectWithPath(Reader reader, JSONPointer path, JSONObject replace) throws JSONFoundExecption { JSONObject jo = new JSONObject(); @@ -1257,4 +1259,297 @@ public static String toString(final Object object, final String tagName, final X + ">" + string + ""; } + + + /** + * Milestone 3 + * + * reload original parse function and add a new parameter "Function keyTransformer" + * + * Scan the content following the named tag, attaching it to the context. + * + * @param x + * The XMLTokener containing the source string. + * @param context + * The JSONObject that will include the new material. + * @param name + * The tag name. + * @param keyTransformer + * key to new key + * @return true if the close tag is processed. + * @throws JSONException + */ + private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, Function keyTransformer) + throws JSONException { + char c; + int i; + JSONObject jsonObject = null; + String string; + String tagName; + Object token; + XMLXsiTypeConverter xmlXsiTypeConverter; + + // Test for and skip past these forms: + // + // + // + // + // Report errors for these forms: + // <> + // <= + // << + + token = x.nextToken(); + + // + x.skipPast("-->"); + return false; + } + x.back(); + } else if (c == '[') { + token = x.nextToken(); + if ("CDATA".equals(token)) { + if (x.next() == '[') { + string = x.nextCDATA(); + if (string.length() > 0) { + context.accumulate(config.getcDataTagName(), string); + } + return false; + } + } + throw x.syntaxError("Expected 'CDATA['"); + } + i = 1; + do { + token = x.nextMeta(); + if (token == null) { + throw x.syntaxError("Missing '>' after ' 0); + return false; + } else if (token == QUEST) { + + // "); + return false; + } else if (token == SLASH) { + + // Close tag September 12, 2019 + //add type to Jsonobject + //"last_update_posted": { + // "type": "Actual", + // "content": "September 12, 2019" + // }, + string = (String) token; + string = (String) keyTransformer.apply(string); + + token = x.nextToken(); + if (token == EQ) { + token = x.nextToken(); + if (!(token instanceof String)) { + throw x.syntaxError("Missing value"); + } + + if (config.isConvertNilAttributeToNull() + && NULL_ATTR.equals(string) + && Boolean.parseBoolean((String) token)) { + nilAttributeFound = true; + } else if(config.getXsiTypeMap() != null && !config.getXsiTypeMap().isEmpty() + && TYPE_ATTR.equals(string)) { + xmlXsiTypeConverter = config.getXsiTypeMap().get(token); + } else if (!nilAttributeFound) { + jsonObject.accumulate(string, + config.isKeepStrings() + ? ((String) token) + : stringToValue((String) token)); + } + token = null; + } else { + jsonObject.accumulate(string, ""); + } + + + } else if (token == SLASH) { + // Empty tag <.../> + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped tag"); + } + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (nilAttributeFound) { + context.append(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + //in loop + context.append(tagName, jsonObject); + } else { + context.put(tagName, new JSONArray()); + } + } else { + if (nilAttributeFound) { + context.accumulate(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.accumulate(tagName, jsonObject); + } else { + context.accumulate(tagName, ""); + } + } + return false; + + } else if (token == GT) { + // Content, between <...> and + // after a String, if it is >, then analysis inner content + for (;;) { + //loop goal: first time temporally save String, second time put into parent JSONObject + //if has nested element, do recursion + token = x.nextContent(); + if (token == null) { + if (tagName != null) { + throw x.syntaxError("Unclosed tag " + tagName); + } + return false; + } else if (token instanceof String) { + string = (String) token; + if (string.length() > 0) { + if(xmlXsiTypeConverter != null) { + jsonObject.accumulate(config.getcDataTagName(), + stringToValue(string, xmlXsiTypeConverter)); + } else { + if(jsonObject.length()>0){ + //For Milestone 3 + //here to deal with real content tagName, not configTagName + //September 12, 2019 + //"last_update_posted": { + // "type": "Actual", + // "content": "September 12, 2019" + // }, + String configTagName = (String) keyTransformer.apply(config.getcDataTagName()); + jsonObject.accumulate(configTagName, + config.isKeepStrings() ? string : stringToValue(string)); + } + else { + jsonObject.accumulate(config.getcDataTagName(), + config.isKeepStrings() ? string : stringToValue(string)); + } + } + } + + } else if (token == LT) { + // Nested element + // new < + //when subStruct come across opposite one, subStruct's recursion return true to subStruct + //when subStruct has been accumulated, return false to parentis done + if (parse(x, jsonObject, tagName, config, keyTransformer)) { + //For Milestone 3 + //deal with global tagName for most cases + tagName = (String) keyTransformer.apply(tagName); + + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (jsonObject.length() == 0) { + context.put(tagName, new JSONArray()); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + context.append(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.append(tagName, jsonObject); + } + } else { + if (jsonObject.length() == 0) { + //let values of same tagName, save as JSONArray + context.accumulate(tagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + //enter into subStruct and then return true, come here when subStruct is value + //config.getcDataTagName():"content" is temporally saving key + context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + //when subStruct has nested element like
111Ave
+ context.accumulate(tagName, jsonObject); + } + } + + return false; + } + } + } + } else { + throw x.syntaxError("Misshaped tag"); + } + } + } + } + + /* + * Milestone 3 + * Read an XML file into a JSON object, and add the prefix "swe262_" to all of its keys. + * + * YOURTYPEHERE should be a function (or "functional" in Java) + * that takes as input a String denoting a key and returns another String that is the transformation of the key + * + * functions provided by the client code, so they can be quite powerful + * and include all sorts of string matching and transformation logic. + * + * The goal here is that you do the transformation during the parsing of the XML file, not in another pass afterwards. + * "foo" --> "swe262_foo" + * "foo" --> "oof" + * + * https://canvas.eee.uci.edu/courses/42906/pages/project + * */ + public static JSONObject toJSONObject(Reader reader, Function keyTransformer){ + keyTransformer.apply("b"); + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(reader); + while (x.more()) { + x.skipPast("<"); + if (x.more()) { + parse(x, jo, null, XMLParserConfiguration.ORIGINAL, keyTransformer); + } + } + + return jo; + } } From d0202ae38c100f1d91a674fb4a9bcb6b3eabdb7c Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 8 Feb 2022 14:59:28 -0800 Subject: [PATCH 12/24] milestone 3 test file --- src/test/java/org/json/junit/XMLMyTestM3.java | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 src/test/java/org/json/junit/XMLMyTestM3.java diff --git a/src/test/java/org/json/junit/XMLMyTestM3.java b/src/test/java/org/json/junit/XMLMyTestM3.java new file mode 100644 index 000000000..c3df24294 --- /dev/null +++ b/src/test/java/org/json/junit/XMLMyTestM3.java @@ -0,0 +1,161 @@ +package org.json.junit; + +import org.json.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.*; +import java.util.*; +import java.util.function.Function; + +/** + * @program: JSON-java for Milestone 3 + * @description: Read an XML file into a JSON object, and add the prefix "swe262_" to all of its keys. + * Unit tests for inside the library vs. doing it in client code + * @author: Mr. Su + * @create: 2022-01-25 11:26 + **/ + +public class XMLMyTestM3 { + Reader xmlReader; + String xmlFileName; + InputStream xmlStream = null; + @Before + public void load() { + xmlFileName = "Issue537.xml"; +// xmlFileName = "enwiki-250MB.xml"; +// xmlFileName = "enwiki-490MB.xml"; + xmlStream = XMLMyTestM3.class.getClassLoader().getResourceAsStream(xmlFileName); + this.xmlReader = new InputStreamReader(xmlStream); + } + + @After + public void prepare() throws IOException { + xmlStream.close(); + } + + /* + * static JSONObject toJSONObject(Reader reader, JSONPointer path) + * Two ways output file is different in key sequence. + * because they calculate hashcode in different hash size + * and the key will insert into different + */ + //replace key in client code + @Test + public void testToJSONKeyReplaceInClient() throws IOException { + long startTime = System.currentTimeMillis(); + //replace key outside library, in client code + JSONObject jsonObject; + jsonObject = XML.toJSONObject(xmlReader); + refactorKeyName(jsonObject, "swe262_"); + write2File(jsonObject, "M3_outLib"); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONKeyReplaceInClient Run time:" + (endTime - startTime) + "ms"); + } + + //replace key in the library + @Test + public void testToJSONKeyReplaceWithKeyTransformer() throws IOException { + long startTime = System.currentTimeMillis(); + Function func = cc-> { + return "swe262_"+cc; + }; + _testToJSONKeyReplaceWithKeyTransformer(func); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONKeyReplaceWithKeyTransformer Run time:" + (endTime - startTime) + "ms"); + } + + //reverse key in the library + @Test + public void testToJSONKeyReverseWithKeyTransformer() throws IOException { + long startTime = System.currentTimeMillis(); + Function func = cc-> { + StringBuilder sb = new StringBuilder((String) cc); + sb.reverse(); + return sb.toString(); + }; + _testToJSONKeyReplaceWithKeyTransformer(func); + long endTime = System.currentTimeMillis(); //get end time + System.out.println("testToJSONKeyReplaceWithKeyTransformer Run time:" + (endTime - startTime) + "ms"); + } + +// @Test + public void _testToJSONKeyReplaceWithKeyTransformer(Function func) throws IOException { + try { + //replace key inside library + JSONObject jsonObject; + jsonObject = XML.toJSONObject(xmlReader, func); + write2File(jsonObject, "M3_inLib"); + } catch (JSONException e) { + System.out.println(e); + } + } + + private static void refactorKeyName(JSONObject jsonObject, String prefix) { + Set sets = new HashSet(jsonObject.keySet()); + for(String key : sets){ + Object o = jsonObject.get(key); + refactorByType(prefix, o); + jsonObject.remove(key); + jsonObject.put(prefix+key, o); + } + } + + //support both jsonObject and jsonArray + private static void refactorByType(String prefix, Object object) { + if (object instanceof JSONObject) { + refactorKeyName((JSONObject) object, prefix); + } else if (object instanceof JSONArray) { + JSONArray jsonArray = (JSONArray)object; + for(Object o : jsonArray) { + refactorByType( prefix, o); + } + } + } + + private void write2File(JSONObject json, String ex) throws IOException { + String xmlRealPath = this.getClass().getResource("/" + xmlFileName).getPath(); + FileWriter out = new FileWriter(xmlRealPath.replace("xml", "json").replace(".json", "_" + ex + "_sub.json")); + BufferedWriter bw = new BufferedWriter(out); + bw.write(json == null ? "" : json.toString(4)); + bw.close(); + } + + + @Test + public void testHashMap1(){ + HashMap map = new HashMap(); + map.put("verification_date", "verification_date"); + map.put("last_update_submitted", "last_update_submitted"); + for(String key: map.keySet()){ + String value = map.get(key); + map.remove(key); + key = "swe262_" + key; + map.put(key, value); + System.out.println(key + ":" + map.get(key)); + } + } + + @Test + public void testHashMap2(){ + HashMap map = new HashMap(); + map.put("swe262_verification_date", "verification_date"); + map.put("swe262_last_update_submitted", "last_update_submitted"); + + for(String key: map.keySet()){ + System.out.println(key + ":" + map.get(key)); + } + } + + @Test + public void testHashMap3(){ + HashMap map = new HashMap(); + map.put("verification_date", "verification_date"); + map.put("last_update_submitted", "last_update_submitted"); + + for(String key: map.keySet()){ + System.out.println(key + ":" + map.get(key)); + } + } +} From 92562f6777f073ea641ea99f5a116218a454b54e Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 8 Feb 2022 15:04:14 -0800 Subject: [PATCH 13/24] readme edit --- README_M3.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README_M3.md b/README_M3.md index b3b7bb3df..0ea3c515e 100644 --- a/README_M3.md +++ b/README_M3.md @@ -1,6 +1,9 @@

Milestone 3

-Overload function "toJSONObject" and "parse". +Read an XML file into a JSON object, and add the prefix "swe262_" to all of its keys. +Do it by adding an overloaded static method to the XML class with the signature + +

Overload function "toJSONObject" and "parse".

``` public static JSONObject toJSONObject(Reader reader, Function keyTransformer); From ec8f2ce9b42e9f78b8965e39c49c8c5ee4f0c82c Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 8 Feb 2022 15:08:33 -0800 Subject: [PATCH 14/24] readme edit --- README_M3.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README_M3.md b/README_M3.md index 0ea3c515e..cccf1eda0 100644 --- a/README_M3.md +++ b/README_M3.md @@ -66,14 +66,13 @@ public void testToJSONKeyReverseWithKeyTransformer() throws IOException { | --- | --- | --- | --- | | testToJSONKeyReplaceInClient | 18ms | 43,045ms | 103,838ms | | testToJSONKeyReplaceWithKeyTransformer | 78ms | 33,132ms | 89,854ms | +("testToJSONKeyReplaceInClient" does it in client code. +"testToJSONKeyReplaceWithKeyTransformer" does it in library.) -Function "testToJSONKeyReplaceInClient" replaces key in client code. -Function "testToJSONKeyReplaceWithKeyTransformer" replaces key in library, not in client code. - -From testing result, we can see that for a small file, doing key replace in client code runs faster than doing it inside library. +We can see that for a small file, doing key replace in client code runs faster than doing it inside library. But for big files, doing key replace inside library runs much faster than doing it in client code. -I guess that replacing the key in the library by passing a function of KeyTransformer has additional calling consuming time. +I guess that passing a function of KeyTransformer and calling it may have additional calling consuming time. For small files, this calling consuming time is significant. But for big files, it can be trivial. @@ -84,10 +83,10 @@ To execute the task of key transformation, put the function of "keyTransformer"

1. deals with global tagName for most cases: put keyTransformer inside callback "parse" (tagName meet with pair end tagName).

``` - if (parse(x, jsonObject, tagName, config, keyTransformer)) { - tagName = (String) keyTransformer.apply(tagName); - ... - } +if (parse(x, jsonObject, tagName, config, keyTransformer)) { + tagName = (String) keyTransformer.apply(tagName); + ... +} ```

2. to deal with special xml format, like below

From 4d992ba8ed93b71bbe4a84053688ecce10791f57 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 8 Feb 2022 15:08:56 -0800 Subject: [PATCH 15/24] add some comments --- README_M3.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README_M3.md b/README_M3.md index cccf1eda0..ba4ed747f 100644 --- a/README_M3.md +++ b/README_M3.md @@ -66,6 +66,7 @@ public void testToJSONKeyReverseWithKeyTransformer() throws IOException { | --- | --- | --- | --- | | testToJSONKeyReplaceInClient | 18ms | 43,045ms | 103,838ms | | testToJSONKeyReplaceWithKeyTransformer | 78ms | 33,132ms | 89,854ms | + ("testToJSONKeyReplaceInClient" does it in client code. "testToJSONKeyReplaceWithKeyTransformer" does it in library.) From 9a22d963ae7dddfed9cb2cde4617f1287098434d Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 8 Feb 2022 15:15:28 -0800 Subject: [PATCH 16/24] add some comments --- README_M3.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README_M3.md b/README_M3.md index ba4ed747f..6b023a1b8 100644 --- a/README_M3.md +++ b/README_M3.md @@ -32,7 +32,9 @@ Since Milestone 3 uses Function as a parameter, it needs to adjust the jdk to 1.

Test Units (Junit)

-

src/test/java/org/json/junit/XMLMyTestM3.java:

+

Test two kind of key transformer: key replace and key reverse. And do a key replace in client code VS. inside library

+ +

Test file position: src/test/java/org/json/junit/XMLMyTestM3.java.

``` //replace key in client code From 6da773f102991a2054e016384654c155128046e5 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 22 Feb 2022 11:12:08 -0800 Subject: [PATCH 17/24] Add streaming methods to the library that allow the client code to chain operations on JSON nodes. --- README.md | 2 + README_M4.md | 179 ++++++++++++++++++ src/main/java/org/json/JSONObject.java | 126 ++++++++++-- .../java/org/json/junit/M4StreamTest.java | 134 +++++++++++++ 4 files changed, 427 insertions(+), 14 deletions(-) create mode 100644 README_M4.md create mode 100644 src/test/java/org/json/junit/M4StreamTest.java diff --git a/README.md b/README.md index 127542b8f..c095762ca 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ [Milestone 2](README_M2.md) [Milestone 3](README_M3.md) + +[Milestone 4](README_M4.md) diff --git a/README_M4.md b/README_M4.md new file mode 100644 index 000000000..5a639872b --- /dev/null +++ b/README_M4.md @@ -0,0 +1,179 @@ +

Milestone 4

+ +Add streaming methods to the library that allow the client code to chain operations on JSON nodes. + +Streaming methods in: + +Test Units in: + +Turn the JSONObject to the stream in type of Map.Entry. +In this way, it keeps the original key of JSONObject and easily be filtered and sorted by key. + +For JSONArray in JSONObject, I split the array and add index of array as key. Then add them into stream. + +

Two ways of transforming JSONObject to stream

+ +

In my opinion, First way is more abstract but the code is more elegant. +Second way is more comprehensive and helpful in understand stream.

+ +

1. Recursive Stream

+ +``` +public Stream> stream(); +``` + +

2. Spliterator of Stream

+ +``` +public Stream> stream(); +public Spliterator> spliterator(); +static class JSONObjectSpliterator implements Spliterator>{ + public int characteristics(); + public long estimateSize(); + public boolean tryAdvance(Consumer> action); +} +``` + +

Maven

+The project is based on Maven. +Since Milestone 4 uses Stream, it needs to adjust the jdk to 1.8. + +```xml + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + + + 1.8 + 1.8 + + +``` + +

Test Units (Junit)

+ +

Test file position: src/test/java/org/json/junit/M4StreamTest.java.

+ + +Test streams features: + +| methods | Params | +| --- | --- | +| forEach | System.out::println | +| filter | e.getKey() or e.getValue() | +| map | e.getKey().toUpperCase() or Map.Entry::getValue | +| collect | Collectors.toMap or Collectors.toList() | +| findFirst | | +| sorted | Comparator.comparing(Map.Entry::getKey) | + + +``` +//JSONObject stream(): print key and value +@Test +public void testJSONObjectStreamForEach(); + + +//JSONObject stream(): filter and transform key to upper case +@Test +public void testJSONObjectStreamKeyToUpperCase(); + +//JSONObject stream() to Map +@Test +public void testJSONObjectStreamToMap(); + +//JSONObject stream(): +//(1) filter and get the first match value +//(2) filter value and get the first match key +@Test +public void testJSONStreamMatch(); + +//JSONObject stream(): filter key and turn values to list +@Test +public void testJSONStreamMatchToList() +``` + +

Implement

+ +

Source file position: src/main/java/org/json/JSONObject.java

+ + +There is two ways to transform JSONObject into stream. + +

1. Recursive Stream

+ +``` +public Stream> stream(){ + Stream> resultingStream = null; + for(Map.Entry entry: map.entrySet()){ + if(resultingStream == null){ + resultingStream = Stream.of(entry); + } + else{ + resultingStream = Stream.concat(resultingStream, Stream.of(entry)); + } + Object object = entry.getValue(); + if(object instanceof JSONObject){ + //recursive call + resultingStream = Stream.concat(resultingStream, ((JSONObject) object).stream()); + } + else if(object instanceof JSONArray){ + //transform a JSONArray to a JSONObject, then recursive call + JSONArray jsonArray = (JSONArray)(object); + JSONObject json = new JSONObject(jsonArray.length()); + for (int i = 0; i < jsonArray.length(); i++) { + json.put(i + "", jsonArray.opt(i)); + } + resultingStream = Stream.concat(resultingStream, json.stream()); + } + + } + return resultingStream; +} +``` + +

2. Spliterator

+ +``` +static class JSONObjectSpliterator implements Spliterator> { + private final JSONObject root; + private JSONObject tree; + + public int characteristics() {...} + public long estimateSize() {...} + + @Override + public boolean tryAdvance(Consumer> action) { + JSONObject current = tree; + + for(Map.Entry entry : tree.entrySet()){ + action.accept(entry); + Object value = entry.getValue(); + if(value instanceof JSONObject){ + tree = (JSONObject) value; + tryAdvance(action); + } + else if(value instanceof JSONArray){ + //transform a JSONArray to a JSONObject, then recursive call + JSONArray jsonArray = (JSONArray)(value); + JSONObject json = new JSONObject(jsonArray.length()); + for (int i = 0; i < jsonArray.length(); i++) { + json.put(i + "", jsonArray.opt(i)); + } + tree = json; + tryAdvance(action); + } + } + + //Returns: false if no remaining elements existed upon entry to this method, else true. + tree = current; + + if (tree == root) + return false; + else + return true; + } +} + +``` diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 99a075069..f9848367b 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -36,18 +36,12 @@ of this software and associated documentation files (the "Software"), to deal 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; +import java.util.stream.StreamSupport; /** * A JSONObject is an unordered collection of name/value pairs. Its external @@ -1169,7 +1163,7 @@ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { return objectToBigDecimal(val, defaultValue, true); } - + /** * @param val value to convert * @param defaultValue default value to return is the conversion doesn't work or is null. @@ -1552,12 +1546,12 @@ && isValidMethodName(method.getName())) { final Object result = method.invoke(bean); if (result != null) { // check cyclic dependency and throw error if needed - // the wrap and populateMap combination method is + // the wrap and populateMap combination method is // itself DFS recursive if (objectsRecord.contains(result)) { throw recursivelyDefinedObjectException(key); } - + objectsRecord.add(result); this.map.put(key, wrap(result, objectsRecord)); @@ -1566,7 +1560,7 @@ && isValidMethodName(method.getName())) { // we don't use the result anywhere outside of wrap // if it's a resource we should be sure to close it - // after calling toString + // after calling toString if (result instanceof Closeable) { try { ((Closeable) result).close(); @@ -2722,4 +2716,108 @@ private static JSONException recursivelyDefinedObjectException(String key) { "JavaBean object contains recursively defined member variable of key " + quote(key) ); } + + /* + * + * + * */ + public Stream> stream(){ + Stream> resultingStream = null; + for(Map.Entry entry: map.entrySet()){ + //first, add new stuff to stream + if(resultingStream == null){ + resultingStream = Stream.of(entry); + } + else{ + resultingStream = Stream.concat(resultingStream, Stream.of(entry)); + } + //then, check whether to recursion + Object object = entry.getValue(); + if(object instanceof JSONObject){ + //recursive call + resultingStream = Stream.concat(resultingStream, ((JSONObject) object).stream()); + } + else if(object instanceof JSONArray){ + //transform a JSONArray to a JSONObject, then recursive call + JSONArray jsonArray = (JSONArray)(object); + JSONObject json = new JSONObject(jsonArray.length()); + for (int i = 0; i < jsonArray.length(); i++) { + json.put(i + "", jsonArray.opt(i)); + } + resultingStream = Stream.concat(resultingStream, json.stream()); + } + } + return resultingStream; + } + + + /* + + public Stream> stream() { + return StreamSupport.stream(this.spliterator(), false); + } + + public Spliterator> spliterator() { + return new JSONObjectSpliterator(this); + } + + static class JSONObjectSpliterator implements Spliterator> { + private final JSONObject root; + private JSONObject tree; + + JSONObjectSpliterator(JSONObject t) { + root = tree = t; + } + + @Override + public int characteristics() { + return Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL; + } + + @Override + public long estimateSize() { + return Long.MAX_VALUE; + } + + //Returns: false if no remaining elements existed upon entry to this method, else true. + @Override + public boolean tryAdvance(Consumer> action) { + JSONObject current = tree; + + for(Map.Entry entry : tree.entrySet()){ + action.accept(entry); + Object value = entry.getValue(); + if(value instanceof JSONObject){ + tree = (JSONObject) value; + tryAdvance(action); + } + else if(value instanceof JSONArray){ + //transform a JSONArray to a JSONObject, then recursive call + JSONArray jsonArray = (JSONArray)(value); + JSONObject json = new JSONObject(jsonArray.length()); + for (int i = 0; i < jsonArray.length(); i++) { + json.put(i + "", jsonArray.opt(i)); + } + tree = json; + tryAdvance(action); + } + } + + tree = current; + + if (tree == root) + return false; + else + return true; + } + + @Override + public Spliterator> trySplit() { + return null; + } + } + + */ } + + diff --git a/src/test/java/org/json/junit/M4StreamTest.java b/src/test/java/org/json/junit/M4StreamTest.java new file mode 100644 index 000000000..cba2b3dc5 --- /dev/null +++ b/src/test/java/org/json/junit/M4StreamTest.java @@ -0,0 +1,134 @@ +package org.json.junit; + +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @program: JSON-java + * @description: + * @author: Mr. Su + * @create: 2022-02-18 11:15 + **/ + +public class M4StreamTest { + JSONObject jsonObject = null; + @Before + public void loadJSONObject(){ + jsonObject = new JSONObject("{\"menu\": {\n" + + " \"id\": \"file\",\n" + + " \"value\": \"File\",\n" + + " \"popup\": {\n" + + " \"menuitem\": [\n" + + " {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\n" + + " {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\n" + + " {\"value\": \"Submit\", \"onclick\": \"SubmitDoc()\"},\n" + + " {\"value\": \"Delete\", \"onclick\": \"DeleteDoc()\"},\n" + + " {\"value\": \"Revise\", \"onclick\": \"ReviseDoc()\"},\n" + + " {\"value\": \"Confirm\", \"onclick\": \"ConfirmDoc()\"},\n" + + " {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\n" + + " ]\n" + + " }\n" + + "}}"); + } + + /* + * JSONObject stream(): print key and value + * */ + @Test + public void testJSONObjectStreamForEach(){ + System.out.println("------------------------"); + System.out.println("JSONObject stream: print out key & value"); + jsonObject.stream() + .forEach(System.out::println); + } + + /* + * JSONObject stream(): filter and transform key to upper case + * */ + @Test + public void testJSONObjectStreamKeyToUpperCase(){ + System.out.println("------------------------"); + System.out.println("JSONObject stream: print out key & value"); + jsonObject.stream() + .filter(e -> e.getValue() instanceof String) + .map(e->{ + return new AbstractMap.SimpleEntry<>(e.getKey().toUpperCase(), e.getValue()); + }) + .forEach(e->{ + System.out.println(e.getKey() + " : " + e.getValue()); + }); + } + + /* + * JSONObject stream() to Map + * */ + @Test + public void testJSONObjectStreamToMap(){ + System.out.println("------------------------"); + System.out.println("JSONObject stream: print out key & value"); + Map map = jsonObject.stream() + .collect(Collectors.toMap(e -> e.getKey().toUpperCase(), e -> e.getValue(), (x1, x2) -> x1)); + } + + /* + * JSONObject stream(): + * (1) filter key and get the first match value + * (2) filter value and get the first match key + * */ + @Test + public void testJSONStreamMatch(){ + System.out.println("------------------------"); + System.out.println("JSONObject stream: Retrieving a Match by key \"onclick\""); + //match key + Optional firstValue = jsonObject.stream() + .filter(e -> "onclick".equals(e.getKey())) + .map(Map.Entry::getValue) + .findFirst(); + System.out.println(firstValue.get()); + + System.out.println("------------------------"); + System.out.println("JSONObject stream: Retrieving a Match by value of \"CreateNewDoc()\""); + //match value + Optional firstKey = jsonObject.stream() + .filter(e -> "CreateNewDoc()".equals(e.getValue())) + .map(Map.Entry::getKey) + .findFirst(); + System.out.println(firstKey.get()); + } + + /* + * JSONObject stream(): filter key and turn values to list + * */ + @Test + public void testJSONStreamMatchToList(){ + + System.out.println("------------------------"); + System.out.println("JSONObject stream: Retrieving Multiple Results"); + //Retrieving Multiple Results + List buttons = jsonObject.stream() + .filter(e->"onclick".equals(e.getKey())) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + for(Object button : buttons){ + System.out.println(button); + } + } + + /* + * JSONObject stream(): filter and sort by key + * */ + @Test + public void testJSONStreamSortByKey(){ + System.out.println("------------------------"); + System.out.println("JSONObject stream: sort"); + //sort + jsonObject.stream() + .filter(e->e.getValue() instanceof String) + .sorted(Comparator.comparing(Map.Entry::getKey)) + .forEach(System.out::println); + } +} From 2502ff9ad48f78598c7b6a96816e9f7b254566a3 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 22 Feb 2022 13:07:20 -0800 Subject: [PATCH 18/24] edit some info in README_M4.md --- README_M4.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/README_M4.md b/README_M4.md index 5a639872b..af79c5518 100644 --- a/README_M4.md +++ b/README_M4.md @@ -2,20 +2,21 @@ Add streaming methods to the library that allow the client code to chain operations on JSON nodes. -Streaming methods in: +Streaming methods: src/main/java/org/json/JSONObject.java -Test Units in: +(https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/main/java/org/json/JSONObject.java) + +Test Units Cases: src/test/java/org/json/junit/M4StreamTest.java + +(https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/test/java/org/json/junit/M4StreamTest.java) Turn the JSONObject to the stream in type of Map.Entry. -In this way, it keeps the original key of JSONObject and easily be filtered and sorted by key. +In this way, it keeps the original key of JSONObject and easily be filtered, sorted and applied other actions. For JSONArray in JSONObject, I split the array and add index of array as key. Then add them into stream.

Two ways of transforming JSONObject to stream

-

In my opinion, First way is more abstract but the code is more elegant. -Second way is more comprehensive and helpful in understand stream.

-

1. Recursive Stream

``` @@ -34,6 +35,9 @@ static class JSONObjectSpliterator implements SpliteratorIn my opinion, the first way is more abstract but the code is more elegant. +The second way is more comprehensive and helpful in understand stream.

+

Maven

The project is based on Maven. Since Milestone 4 uses Stream, it needs to adjust the jdk to 1.8. @@ -54,10 +58,11 @@ Since Milestone 4 uses Stream, it needs to adjust the jdk to 1.8.

Test Units (Junit)

-

Test file position: src/test/java/org/json/junit/M4StreamTest.java.

- +Test Units Cases: src/test/java/org/json/junit/M4StreamTest.java + +(https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/test/java/org/json/junit/M4StreamTest.java) -Test streams features: +

Test streams features:

| methods | Params | | --- | --- | @@ -68,6 +73,7 @@ Test streams features: | findFirst | | | sorted | Comparator.comparing(Map.Entry::getKey) | +

Test Unites Cases:

``` //JSONObject stream(): print key and value @@ -96,10 +102,11 @@ public void testJSONStreamMatchToList()

Implement

-

Source file position: src/main/java/org/json/JSONObject.java

- +Streaming methods: src/main/java/org/json/JSONObject.java + +(https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/main/java/org/json/JSONObject.java) -There is two ways to transform JSONObject into stream. +There are two ways to transform JSONObject into stream.

1. Recursive Stream

From 74d7d7452e725712f0c87ed5ebb11f9c059ea491 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 22 Feb 2022 13:16:43 -0800 Subject: [PATCH 19/24] edit some info in README_M4.md --- README_M4.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/README_M4.md b/README_M4.md index af79c5518..04d491d36 100644 --- a/README_M4.md +++ b/README_M4.md @@ -10,22 +10,22 @@ Test Units Cases: src/test/java/org/json/junit/M4StreamTest.java (https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/test/java/org/json/junit/M4StreamTest.java) -Turn the JSONObject to the stream in type of Map.Entry. -In this way, it keeps the original key of JSONObject and easily be filtered, sorted and applied other actions. +Turn the JSONObject to the stream in the type of Map.Entry. +In this way, it keeps the original key of JSONObject and easily be filtered, sorted, and applied to other actions. -For JSONArray in JSONObject, I split the array and add index of array as key. Then add them into stream. +For JSONArray in JSONObject, I add an index to each object in JSONArray as the key and add them into the stream.

Two ways of transforming JSONObject to stream

1. Recursive Stream

-``` +```java public Stream> stream(); ```

2. Spliterator of Stream

-``` +```java public Stream> stream(); public Spliterator> spliterator(); static class JSONObjectSpliterator implements Spliterator>{ @@ -36,11 +36,11 @@ static class JSONObjectSpliterator implements SpliteratorIn my opinion, the first way is more abstract but the code is more elegant. -The second way is more comprehensive and helpful in understand stream.

+The second way is more comprehensive and helpful in understanding stream.

Maven

The project is based on Maven. -Since Milestone 4 uses Stream, it needs to adjust the jdk to 1.8. +Since Milestone 4 uses Stream, it needs to adjust the JDK to 1.8. ```xml @@ -75,7 +75,7 @@ Test Units Cases: src/test/java/org/json/junit/M4StreamTest.java

Test Unites Cases:

-``` +```java //JSONObject stream(): print key and value @Test public void testJSONObjectStreamForEach(); @@ -110,16 +110,21 @@ There are two ways to transform JSONObject into stream.

1. Recursive Stream

-``` +```java +/* +* Recursive Stream +* */ public Stream> stream(){ Stream> resultingStream = null; for(Map.Entry entry: map.entrySet()){ + //first, add new stuff to stream if(resultingStream == null){ resultingStream = Stream.of(entry); } else{ resultingStream = Stream.concat(resultingStream, Stream.of(entry)); } + //then, check whether to recursion Object object = entry.getValue(); if(object instanceof JSONObject){ //recursive call @@ -134,7 +139,6 @@ public Stream> stream(){ } resultingStream = Stream.concat(resultingStream, json.stream()); } - } return resultingStream; } @@ -142,7 +146,7 @@ public Stream> stream(){

2. Spliterator

-``` +```java static class JSONObjectSpliterator implements Spliterator> { private final JSONObject root; private JSONObject tree; From 44900df91a08ba02136aee6c8855402185ceadbb Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 22 Feb 2022 13:24:16 -0800 Subject: [PATCH 20/24] add some comments --- src/main/java/org/json/JSONObject.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index f9848367b..ba435c54e 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -2718,8 +2718,7 @@ private static JSONException recursivelyDefinedObjectException(String key) { } /* - * - * + * 1. Recursive Stream * */ public Stream> stream(){ Stream> resultingStream = null; @@ -2751,8 +2750,12 @@ else if(object instanceof JSONArray){ } + /* + * 2. Spliterator of Stream + * */ /* + //Spliterator of Stream public Stream> stream() { return StreamSupport.stream(this.spliterator(), false); } @@ -2761,6 +2764,7 @@ public Spliterator> spliterator() { return new JSONObjectSpliterator(this); } + //Spliterator of Stream static class JSONObjectSpliterator implements Spliterator> { private final JSONObject root; private JSONObject tree; From 4859e55229ea145c67f598748ea1d5870b7ac4d2 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Tue, 22 Feb 2022 13:24:40 -0800 Subject: [PATCH 21/24] add some comments --- src/test/java/org/json/junit/M4StreamTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/json/junit/M4StreamTest.java b/src/test/java/org/json/junit/M4StreamTest.java index cba2b3dc5..755195629 100644 --- a/src/test/java/org/json/junit/M4StreamTest.java +++ b/src/test/java/org/json/junit/M4StreamTest.java @@ -52,7 +52,7 @@ public void testJSONObjectStreamForEach(){ @Test public void testJSONObjectStreamKeyToUpperCase(){ System.out.println("------------------------"); - System.out.println("JSONObject stream: print out key & value"); + System.out.println("JSONObject stream: filter and transform key to upper case"); jsonObject.stream() .filter(e -> e.getValue() instanceof String) .map(e->{ @@ -69,9 +69,11 @@ public void testJSONObjectStreamKeyToUpperCase(){ @Test public void testJSONObjectStreamToMap(){ System.out.println("------------------------"); - System.out.println("JSONObject stream: print out key & value"); + System.out.println("JSONObject stream: ToMap"); Map map = jsonObject.stream() .collect(Collectors.toMap(e -> e.getKey().toUpperCase(), e -> e.getValue(), (x1, x2) -> x1)); + map.entrySet().stream() + .forEach(System.out::println); } /* @@ -82,7 +84,7 @@ public void testJSONObjectStreamToMap(){ @Test public void testJSONStreamMatch(){ System.out.println("------------------------"); - System.out.println("JSONObject stream: Retrieving a Match by key \"onclick\""); + System.out.println("JSONObject stream: Retrieving the first Match by key \"onclick\""); //match key Optional firstValue = jsonObject.stream() .filter(e -> "onclick".equals(e.getKey())) @@ -91,7 +93,7 @@ public void testJSONStreamMatch(){ System.out.println(firstValue.get()); System.out.println("------------------------"); - System.out.println("JSONObject stream: Retrieving a Match by value of \"CreateNewDoc()\""); + System.out.println("JSONObject stream: Retrieving the first Match by value of \"CreateNewDoc()\""); //match value Optional firstKey = jsonObject.stream() .filter(e -> "CreateNewDoc()".equals(e.getValue())) @@ -107,7 +109,7 @@ public void testJSONStreamMatch(){ public void testJSONStreamMatchToList(){ System.out.println("------------------------"); - System.out.println("JSONObject stream: Retrieving Multiple Results"); + System.out.println("JSONObject stream: filter key and turn values to list"); //Retrieving Multiple Results List buttons = jsonObject.stream() .filter(e->"onclick".equals(e.getKey())) @@ -124,7 +126,7 @@ public void testJSONStreamMatchToList(){ @Test public void testJSONStreamSortByKey(){ System.out.println("------------------------"); - System.out.println("JSONObject stream: sort"); + System.out.println("JSONObject stream: filter and sort by key"); //sort jsonObject.stream() .filter(e->e.getValue() instanceof String) From b2f55ed19722a992a9eb03a1e4c6c31fa099bf6b Mon Sep 17 00:00:00 2001 From: susunsheng Date: Thu, 10 Mar 2022 23:25:12 -0800 Subject: [PATCH 22/24] Milestone 5 --- README_M5.md | 128 +++++++++++++++++++++++ src/main/java/org/json/CustomFuture.java | 26 +++++ src/main/java/org/json/XML.java | 52 ++++++++- 3 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 README_M5.md create mode 100644 src/main/java/org/json/CustomFuture.java diff --git a/README_M5.md b/README_M5.md new file mode 100644 index 000000000..14065e684 --- /dev/null +++ b/README_M5.md @@ -0,0 +1,128 @@ +

Milestone 5

+ +Add asynchronous methods to the library that allow the client code to proceed. + +Methods: src/main/java/org/json/XML.java + +(https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/main/java/org/json/XML.java) + +CustomFuture: src/main/java/org/json/CustomFuture.java + +(https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/main/java/org/json/CustomFuture.java) + +Test Units Cases: src/test/java/org/json/junit/XMLMyTestM5.java + +(https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/test/java/org/json/junit/XMLMyTestM5.java) + +

Two ways of asynchronous implementation

+ +

1. Custom Future

+ +```java +public static CustomFuture toJSONObject(Reader reader, BiFunction fun, Function fail); +``` + +

2. Java Concurrent CompletableFuture

+ +```java +public static CompletableFuture toJSONObject(Reader reader, BiFunction fun, Function fail); +``` + +

Test Units (Junit)

+ +Test Units Cases: src/test/java/org/json/junit/XMLMyTestM5.java + +(https://github.com/JoshuaSunsheng/JSON-java/blob/master/src/test/java/org/json/junit/XMLMyTestM5.java) + +

Test functions:

+ +| test methods | description | +| --- | --- | +| testAsyncToJSON | read xml into JSONObject and store in file | +| testAsyncToJSONError | simulate failure case | + +

Implement

+ +There are two ways. + +

1. Custom Future

+ +```java +public class XML{ + /* + * Milestone 5 + * Add asynchronous methods to the library that allow the client code to proceed + * example: XML.toJSONObject(aReader, (JSONObject jo) -> {jo.write(aWriter);}, (Exception e) -> { something went wrong }); + * + * */ + static ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);//LinkedBlockingQueue + public static CustomFuture toJSONObject(Reader reader, BiFunction fun, Function fail) throws ExecutionException, InterruptedException { + Function futureFun = (a) -> { + Object msg = null; + System.out.println("Async: " + Thread.currentThread().getName() + " is transforming xml to JSON in the background."); + try{ + JSONObject jsonObject = toJSONObject(reader, XMLParserConfiguration.ORIGINAL); + System.out.println("Async: xml to toJSONObject done"); + msg = fun.apply(jsonObject, "M5_inner"); + }catch (Exception e){ + System.out.println("Async: Exception happens!"); + msg = fail.apply(e); + }catch (Error e){ + System.out.println("Async: Error happens!"); + msg = fail.apply(e); + } + return msg; + }; + CustomFuture customFuture = new CustomFuture(futureFun); + executor.submit(customFuture); + return customFuture; + } +} + +public class CustomFuture implements Runnable { + boolean finished = false; + T result; + Function function; + + public CustomFuture(Function function) { + this.function = function; + } + + public T get() throws InterruptedException { + while (true) { + Thread.currentThread().sleep(50); + if (finished) break; + } + return result; + } + + @Override + public void run() { + result = function.apply(""); + finished = true; + } +} +``` + +

2. Java Concurrent CompletableFuture

+ +```java +public class XML{ + public static CompletableFuture toJSONObject(Reader reader, BiFunction fun, Function fail) throws ExecutionException, InterruptedException { + CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> { + String msg = null; + System.out.println("Async: " + Thread.currentThread().getName() + " is transforming xml to JSON in the background."); + try { + JSONObject jsonObject = toJSONObject(reader, XMLParserConfiguration.ORIGINAL); + System.out.println("Async: xml to toJSONObject done"); + msg = fun.apply(jsonObject, "M5_inner"); + } catch (Throwable e) { + System.out.println("Async: Something wrong happens!"); + msg = (String) fail.apply(e); + } + return msg; + }); + return completableFuture; + } +} +``` diff --git a/src/main/java/org/json/CustomFuture.java b/src/main/java/org/json/CustomFuture.java new file mode 100644 index 000000000..76cd7d00c --- /dev/null +++ b/src/main/java/org/json/CustomFuture.java @@ -0,0 +1,26 @@ +package org.json; +import java.util.function.Function; + +public class CustomFuture implements Runnable { + boolean finished = false; + T result; + Function function; + + public CustomFuture(Function function) { + this.function = function; + } + + public T get() throws InterruptedException { + while (true) { + Thread.currentThread().sleep(50); + if (finished) break; + } + return result; + } + + @Override + public void run() { + result = function.apply(""); + finished = true; + } +} diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 884890f3a..dd01ca3dc 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -28,13 +28,12 @@ of this software and associated documentation files (the "Software"), to deal import java.io.Reader; import java.io.StringReader; -import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.Iterator; -import java.util.List; -import java.util.function.Consumer; +import java.util.concurrent.*; +import java.util.function.BiFunction; import java.util.function.Function; @@ -1552,4 +1551,51 @@ public static JSONObject toJSONObject(Reader reader, Function keyTransformer){ return jo; } + + /* + * Milestone 5 + * Add asynchronous methods to the library that allow the client code to proceed + * example: XML.toJSONObject(aReader, (JSONObject jo) -> {jo.write(aWriter);}, (Exception e) -> { something went wrong }); + * + * */ + static ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);//LinkedBlockingQueue + public static CustomFuture toJSONObject(Reader reader, BiFunction fun, Function fail) throws ExecutionException, InterruptedException { + Function futureFun = (a) -> { + Object msg = null; + System.out.println("Async: " + Thread.currentThread().getName() + " is transforming xml to JSON in the background."); + try{ + JSONObject jsonObject = toJSONObject(reader, XMLParserConfiguration.ORIGINAL); + System.out.println("Async: xml to toJSONObject done"); + msg = fun.apply(jsonObject, "M5_inner"); + }catch (Exception e){ + System.out.println("Async: Exception happens!"); + msg = fail.apply(e); + }catch (Error e){ + System.out.println("Async: Error happens!"); + msg = fail.apply(e); + } + return msg; + }; + CustomFuture customFuture = new CustomFuture(futureFun); + executor.submit(customFuture); + return customFuture; + } + +// public static CompletableFuture toJSONObject(Reader reader, BiFunction fun, Function fail) throws ExecutionException, InterruptedException { +// CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> { +// String msg = null; +// System.out.println("Async: " + Thread.currentThread().getName() + " is transforming xml to JSON in the background."); +// try { +// JSONObject jsonObject = toJSONObject(reader, XMLParserConfiguration.ORIGINAL); +// System.out.println("Async: xml to toJSONObject done"); +// msg = fun.apply(jsonObject, "M5_inner"); +// } catch (Throwable e) { +// System.out.println("Async: Something wrong happens!"); +// msg = (String) fail.apply(e); +// } +// return msg; +// }); +// return completableFuture; +// } + } From 4b6ac6d96beb4f9875430d4fd0e4adc5659d11e0 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Thu, 10 Mar 2022 23:26:05 -0800 Subject: [PATCH 23/24] add M5 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c095762ca..e912be602 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,5 @@ [Milestone 3](README_M3.md) [Milestone 4](README_M4.md) + +[Milestone 5](README_M5.md) From 83956ded30f86db783deb830e0bfdb29500b0de0 Mon Sep 17 00:00:00 2001 From: susunsheng Date: Thu, 10 Mar 2022 23:27:29 -0800 Subject: [PATCH 24/24] XMLMyTestM5 --- src/test/java/org/json/junit/XMLMyTestM5.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/test/java/org/json/junit/XMLMyTestM5.java diff --git a/src/test/java/org/json/junit/XMLMyTestM5.java b/src/test/java/org/json/junit/XMLMyTestM5.java new file mode 100644 index 000000000..6ed1c0ed6 --- /dev/null +++ b/src/test/java/org/json/junit/XMLMyTestM5.java @@ -0,0 +1,96 @@ +package org.json.junit; + +import org.json.CustomFuture; +import org.json.JSONObject; +import org.json.XML; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @program: JSON-java + * @description: Add asynchronous methods to the library that allow the client code to proceed + * @author: Mr. Su + * @create: 2022-02-28 + **/ + +public class XMLMyTestM5 { + Reader xmlReader; + String xmlFileName; + InputStream xmlStream = null; + long startTime; +// CompletableFuture future; + CustomFuture future; + + @Before + public void load() { + startTime = System.currentTimeMillis(); + +// xmlFileName = "Issue537.xml"; + xmlFileName = "enwiki-250MB.xml"; +// xmlFileName = "enwiki-490MB.xml"; + xmlStream = XMLMyTestM3.class.getClassLoader().getResourceAsStream(xmlFileName); + this.xmlReader = new InputStreamReader(xmlStream); + } + + @After + public void end() { + long endTime = System.currentTimeMillis(); //get end time + System.out.println("Main: testAsyncToJSON Run time:" + (endTime - startTime) + "ms"); + } + + //success case + @Test + public void testAsyncToJSON() throws IOException, ExecutionException, InterruptedException { + BiFunction funSuccess = (jsonObject, str) -> { + System.out.println("Async: funSuccess writing into disk"); + try { + write2File(jsonObject, str); + return "Success"; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + future = XML.toJSONObject(xmlReader, funSuccess, funFail); + System.out.println("Main: Approach the end of main code"); + System.out.println("Main: The whole application ends with " + future.get()); + } + + //Error case + @Test + public void testAsyncToJSONError() throws IOException, ExecutionException, InterruptedException { + BiFunction funSuccess = (jsonObject, str) -> { + //simulate runtime exception + if(true) throw new RuntimeException("test error in funFail "); + return ""; + }; + future = XML.toJSONObject(xmlReader, funSuccess, funFail); + System.out.println("Main: Approach the end of main code"); + System.out.println("Main: The whole application ends with " + future.get()); + } + //Exception funciton + Function funFail = e -> { + System.out.println("Async: Catch failure, now funFail printing stackTrace"); + e.printStackTrace(); + return "Error"; + }; + + //Auxiliary function + private void write2File(JSONObject json, String ex) throws IOException { + String xmlRealPath = this.getClass().getResource("/" + xmlFileName).getPath(); + FileWriter out = new FileWriter(xmlRealPath.replace("xml", "json").replace(".json", "_" + ex + "_sub.json")); + BufferedWriter bw = new BufferedWriter(out); + bw.write(json == null ? "" : json.toString(4)); + bw.close(); + } +}