From 89f16ad0af9f7c1097a4336c8fda02afd4b87563 Mon Sep 17 00:00:00 2001 From: Scott Paffrath <33729384+spaffrath@users.noreply.github.com> Date: Fri, 5 Aug 2022 11:00:33 -0700 Subject: [PATCH 01/48] Issue 682 JSONString similarity --- src/main/java/org/json/JSONArray.java | 4 ++++ src/main/java/org/json/JSONObject.java | 4 ++++ .../java/org/json/junit/JSONArrayTest.java | 24 +++++++++++++++++++ .../java/org/json/junit/JSONObjectTest.java | 22 +++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/src/main/java/org/json/JSONArray.java b/src/main/java/org/json/JSONArray.java index 2b33e1d..7f09114 100644 --- a/src/main/java/org/json/JSONArray.java +++ b/src/main/java/org/json/JSONArray.java @@ -1386,6 +1386,10 @@ public boolean similar(Object other) { if (!JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther)) { return false; } + } else if (valueThis instanceof JSONString && valueOther instanceof JSONString) { + if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) { + return false; + } } else if (!valueThis.equals(valueOther)) { return false; } diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index ea549e3..859b3e5 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -2142,6 +2142,10 @@ public boolean similar(Object other) { if (!isNumberSimilar((Number)valueThis, (Number)valueOther)) { return false; } + } else if (valueThis instanceof JSONString && valueOther instanceof JSONString) { + if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) { + return false; + } } else if (!valueThis.equals(valueOther)) { return false; } diff --git a/src/test/java/org/json/junit/JSONArrayTest.java b/src/test/java/org/json/junit/JSONArrayTest.java index 987891e..959954b 100644 --- a/src/test/java/org/json/junit/JSONArrayTest.java +++ b/src/test/java/org/json/junit/JSONArrayTest.java @@ -26,6 +26,7 @@ of this software and associated documentation files (the "Software"), to deal import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -49,7 +50,9 @@ of this software and associated documentation files (the "Software"), to deal import org.json.JSONException; import org.json.JSONObject; import org.json.JSONPointerException; +import org.json.JSONString; import org.json.JSONTokener; +import org.json.junit.data.MyJsonString; import org.junit.Test; import com.jayway.jsonpath.Configuration; @@ -1298,4 +1301,25 @@ public void issue654StackOverflowInputWellFormed() { assertNotNull(json_input); fail("Excepected Exception."); } + + @Test + public void testIssue682SimilarityOfJSONString() { + JSONArray ja1 = new JSONArray() + .put(new MyJsonString()) + .put(2); + JSONArray ja2 = new JSONArray() + .put(new MyJsonString()) + .put(2); + assertTrue(ja1.similar(ja2)); + + JSONArray ja3 = new JSONArray() + .put(new JSONString() { + @Override + public String toJSONString() { + return "\"different value\""; + } + }) + .put(2); + assertFalse(ja1.similar(ja3)); + } } diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 2ba58bf..d244f2f 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -57,6 +57,7 @@ of this software and associated documentation files (the "Software"), to deal import org.json.JSONException; import org.json.JSONObject; import org.json.JSONPointerException; +import org.json.JSONString; import org.json.JSONTokener; import org.json.XML; import org.json.junit.data.BrokenToString; @@ -3398,4 +3399,25 @@ public void issue654StackOverflowInputWellFormed() { assertNotNull(json_input); fail("Excepected Exception."); } + + @Test + public void testIssue682SimilarityOfJSONString() { + JSONObject jo1 = new JSONObject() + .put("a", new MyJsonString()) + .put("b", 2); + JSONObject jo2 = new JSONObject() + .put("a", new MyJsonString()) + .put("b", 2); + assertTrue(jo1.similar(jo2)); + + JSONObject jo3 = new JSONObject() + .put("a", new JSONString() { + @Override + public String toJSONString() { + return "\"different value\""; + } + }) + .put("b", 2); + assertFalse(jo1.similar(jo3)); + } } From 3eecd67a3b4f84c7f2c498613de9dbdec0b227ad Mon Sep 17 00:00:00 2001 From: Joshua Schwartz Date: Tue, 23 Aug 2022 15:00:01 -0500 Subject: [PATCH 02/48] Fix typo --- Examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples.md b/Examples.md index 3f45e78..8f28461 100644 --- a/Examples.md +++ b/Examples.md @@ -218,7 +218,7 @@ import java.util.Properties; } ```

Part 2: Conversion methods

-

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

+

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

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

Extra: Conversion to JSONArray

From 6daabb43ab2d32974f2a8ea79d713f67f4c22d30 Mon Sep 17 00:00:00 2001 From: stleary Date: Tue, 23 Aug 2022 20:00:25 -0500 Subject: [PATCH 03/48] update-copyright - Replace copyright and license restrictions with Public Domain --- LICENSE | 23 +--------------- README.md | 2 -- pom.xml | 27 ++----------------- src/main/java/org/json/CDL.java | 22 +-------------- src/main/java/org/json/Cookie.java | 22 +-------------- src/main/java/org/json/CookieList.java | 22 +-------------- src/main/java/org/json/HTTP.java | 22 +-------------- src/main/java/org/json/HTTPTokener.java | 22 +-------------- src/main/java/org/json/JSONArray.java | 22 +-------------- src/main/java/org/json/JSONException.java | 22 +-------------- src/main/java/org/json/JSONML.java | 22 +-------------- src/main/java/org/json/JSONObject.java | 27 +++---------------- src/main/java/org/json/JSONPointer.java | 22 +-------------- .../java/org/json/JSONPointerException.java | 22 +-------------- .../java/org/json/JSONPropertyIgnore.java | 22 +-------------- src/main/java/org/json/JSONPropertyName.java | 22 +-------------- src/main/java/org/json/JSONString.java | 22 +-------------- src/main/java/org/json/JSONStringer.java | 22 +-------------- src/main/java/org/json/JSONTokener.java | 22 +-------------- src/main/java/org/json/JSONWriter.java | 22 +-------------- src/main/java/org/json/Property.java | 22 +-------------- src/main/java/org/json/XML.java | 22 +-------------- .../java/org/json/XMLParserConfiguration.java | 22 +-------------- src/main/java/org/json/XMLTokener.java | 22 +-------------- .../java/org/json/XMLXsiTypeConverter.java | 22 +-------------- src/test/java/org/json/junit/CDLTest.java | 22 +-------------- .../java/org/json/junit/CookieListTest.java | 22 +-------------- src/test/java/org/json/junit/CookieTest.java | 22 +-------------- src/test/java/org/json/junit/EnumTest.java | 22 +-------------- src/test/java/org/json/junit/HTTPTest.java | 22 +-------------- .../java/org/json/junit/JSONArrayTest.java | 22 +-------------- src/test/java/org/json/junit/JSONMLTest.java | 22 +-------------- .../org/json/junit/JSONObjectLocaleTest.java | 22 +-------------- .../java/org/json/junit/JSONObjectTest.java | 22 +-------------- .../java/org/json/junit/JSONPointerTest.java | 22 +-------------- .../java/org/json/junit/JSONStringTest.java | 22 +-------------- .../java/org/json/junit/JSONStringerTest.java | 22 +-------------- .../java/org/json/junit/JSONTokenerTest.java | 22 +-------------- .../java/org/json/junit/PropertyTest.java | 22 +-------------- src/test/java/org/json/junit/Util.java | 22 +-------------- .../org/json/junit/XMLConfigurationTest.java | 22 +-------------- src/test/java/org/json/junit/XMLTest.java | 22 +-------------- 42 files changed, 44 insertions(+), 871 deletions(-) diff --git a/LICENSE b/LICENSE index 6cfb9b2..2ef9799 100644 --- a/LICENSE +++ b/LICENSE @@ -1,23 +1,2 @@ - -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. diff --git a/README.md b/README.md index 69869cd..9d1ead9 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ Project goals include: 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) diff --git a/pom.xml b/pom.xml index e4a3503..1a93b34 100644 --- a/pom.xml +++ b/pom.xml @@ -18,10 +18,6 @@ This is a reference implementation. There is a large number of JSON packages in Java. Perhaps someday the Java community will standardize on one. Until then, choose carefully. - - The license includes this restriction: "The software shall be used for good, - not evil." If your conscience cannot live with that, then choose a different - package. https://github.com/douglascrockford/JSON-java @@ -39,28 +35,9 @@ - The JSON License - http://json.org/license.html + Public Domain + https://github.com/stleary/JSON-java/blob/master/LICENSE repo - Copyright (c) 2002 JSON.org - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - associated documentation files (the "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the - following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial - portions of the Software. - - The Software shall be used for Good, not Evil. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN - NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/src/main/java/org/json/CDL.java b/src/main/java/org/json/CDL.java index f12cfc0..848831d 100644 --- a/src/main/java/org/json/CDL.java +++ b/src/main/java/org/json/CDL.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/Cookie.java b/src/main/java/org/json/Cookie.java index 1c5fb78..7a7e028 100644 --- a/src/main/java/org/json/Cookie.java +++ b/src/main/java/org/json/Cookie.java @@ -3,27 +3,7 @@ import java.util.Locale; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/CookieList.java b/src/main/java/org/json/CookieList.java index 83b2630..8ad8b58 100644 --- a/src/main/java/org/json/CookieList.java +++ b/src/main/java/org/json/CookieList.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/HTTP.java b/src/main/java/org/json/HTTP.java index cc01167..6fee6ba 100644 --- a/src/main/java/org/json/HTTP.java +++ b/src/main/java/org/json/HTTP.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import java.util.Locale; diff --git a/src/main/java/org/json/HTTPTokener.java b/src/main/java/org/json/HTTPTokener.java index 16c7081..48cad31 100644 --- a/src/main/java/org/json/HTTPTokener.java +++ b/src/main/java/org/json/HTTPTokener.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/JSONArray.java b/src/main/java/org/json/JSONArray.java index 2b33e1d..18e57e6 100644 --- a/src/main/java/org/json/JSONArray.java +++ b/src/main/java/org/json/JSONArray.java @@ -1,27 +1,7 @@ package org.json; /* - Copyright (c) 2002 JSON.org - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - The Software shall be used for Good, not Evil. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. +Public Domain. */ import java.io.IOException; diff --git a/src/main/java/org/json/JSONException.java b/src/main/java/org/json/JSONException.java index ab7ff77..02c29f3 100644 --- a/src/main/java/org/json/JSONException.java +++ b/src/main/java/org/json/JSONException.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/JSONML.java b/src/main/java/org/json/JSONML.java index aafdf72..2f9b840 100644 --- a/src/main/java/org/json/JSONML.java +++ b/src/main/java/org/json/JSONML.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2008 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 782f8a4..9f6ee5f 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -1,31 +1,10 @@ package org.json; -import java.io.Closeable; - /* - Copyright (c) 2002 JSON.org - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - The Software shall be used for Good, not Evil. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - */ +Public Domain. +*/ +import java.io.Closeable; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; diff --git a/src/main/java/org/json/JSONPointer.java b/src/main/java/org/json/JSONPointer.java index f1f7f33..963fdec 100644 --- a/src/main/java/org/json/JSONPointer.java +++ b/src/main/java/org/json/JSONPointer.java @@ -10,27 +10,7 @@ import java.util.List; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/JSONPointerException.java b/src/main/java/org/json/JSONPointerException.java index 0ce1aeb..a0e128c 100644 --- a/src/main/java/org/json/JSONPointerException.java +++ b/src/main/java/org/json/JSONPointerException.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/JSONPropertyIgnore.java b/src/main/java/org/json/JSONPropertyIgnore.java index 682de74..7c5fa53 100644 --- a/src/main/java/org/json/JSONPropertyIgnore.java +++ b/src/main/java/org/json/JSONPropertyIgnore.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2018 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static java.lang.annotation.ElementType.METHOD; diff --git a/src/main/java/org/json/JSONPropertyName.java b/src/main/java/org/json/JSONPropertyName.java index a1bcd58..a66f4ad 100644 --- a/src/main/java/org/json/JSONPropertyName.java +++ b/src/main/java/org/json/JSONPropertyName.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2018 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static java.lang.annotation.ElementType.METHOD; diff --git a/src/main/java/org/json/JSONString.java b/src/main/java/org/json/JSONString.java index bcd9a81..cd8d184 100644 --- a/src/main/java/org/json/JSONString.java +++ b/src/main/java/org/json/JSONString.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/JSONStringer.java b/src/main/java/org/json/JSONStringer.java index d2a4dfb..2f6cf9e 100644 --- a/src/main/java/org/json/JSONStringer.java +++ b/src/main/java/org/json/JSONStringer.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2006 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import java.io.StringWriter; diff --git a/src/main/java/org/json/JSONTokener.java b/src/main/java/org/json/JSONTokener.java index 7f0c86a..8c98c77 100644 --- a/src/main/java/org/json/JSONTokener.java +++ b/src/main/java/org/json/JSONTokener.java @@ -8,27 +8,7 @@ import java.io.StringReader; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/JSONWriter.java b/src/main/java/org/json/JSONWriter.java index dafb1b2..11f4a5c 100644 --- a/src/main/java/org/json/JSONWriter.java +++ b/src/main/java/org/json/JSONWriter.java @@ -5,27 +5,7 @@ import java.util.Map; /* -Copyright (c) 2006 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/main/java/org/json/Property.java b/src/main/java/org/json/Property.java index 7caeebb..83694c0 100644 --- a/src/main/java/org/json/Property.java +++ b/src/main/java/org/json/Property.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import java.util.Enumeration; diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 33838a1..69782cb 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2015 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import java.io.Reader; diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index a1fd63e..9f00710 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -1,26 +1,6 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import java.util.Collections; diff --git a/src/main/java/org/json/XMLTokener.java b/src/main/java/org/json/XMLTokener.java index 3bbd382..957498c 100644 --- a/src/main/java/org/json/XMLTokener.java +++ b/src/main/java/org/json/XMLTokener.java @@ -1,27 +1,7 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import java.io.Reader; diff --git a/src/main/java/org/json/XMLXsiTypeConverter.java b/src/main/java/org/json/XMLXsiTypeConverter.java index 0f8a8c3..0011eff 100644 --- a/src/main/java/org/json/XMLXsiTypeConverter.java +++ b/src/main/java/org/json/XMLXsiTypeConverter.java @@ -1,26 +1,6 @@ package org.json; /* -Copyright (c) 2002 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ /** diff --git a/src/test/java/org/json/junit/CDLTest.java b/src/test/java/org/json/junit/CDLTest.java index b8bdede..f3364fb 100644 --- a/src/test/java/org/json/junit/CDLTest.java +++ b/src/test/java/org/json/junit/CDLTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/CookieListTest.java b/src/test/java/org/json/junit/CookieListTest.java index c3f647f..0af9640 100644 --- a/src/test/java/org/json/junit/CookieListTest.java +++ b/src/test/java/org/json/junit/CookieListTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/CookieTest.java b/src/test/java/org/json/junit/CookieTest.java index 7e7b62b..edd8a7e 100644 --- a/src/test/java/org/json/junit/CookieTest.java +++ b/src/test/java/org/json/junit/CookieTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/EnumTest.java b/src/test/java/org/json/junit/EnumTest.java index 6867123..1496a63 100644 --- a/src/test/java/org/json/junit/EnumTest.java +++ b/src/test/java/org/json/junit/EnumTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/json/junit/HTTPTest.java b/src/test/java/org/json/junit/HTTPTest.java index 8182b60..703d5ad 100644 --- a/src/test/java/org/json/junit/HTTPTest.java +++ b/src/test/java/org/json/junit/HTTPTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/JSONArrayTest.java b/src/test/java/org/json/junit/JSONArrayTest.java index 946f407..e98259c 100644 --- a/src/test/java/org/json/junit/JSONArrayTest.java +++ b/src/test/java/org/json/junit/JSONArrayTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/json/junit/JSONMLTest.java b/src/test/java/org/json/junit/JSONMLTest.java index 390cbd8..34bc9f0 100644 --- a/src/test/java/org/json/junit/JSONMLTest.java +++ b/src/test/java/org/json/junit/JSONMLTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/JSONObjectLocaleTest.java b/src/test/java/org/json/junit/JSONObjectLocaleTest.java index 5112bf5..1cdaf74 100755 --- a/src/test/java/org/json/junit/JSONObjectLocaleTest.java +++ b/src/test/java/org/json/junit/JSONObjectLocaleTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 3681148..32f4a42 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/json/junit/JSONPointerTest.java b/src/test/java/org/json/junit/JSONPointerTest.java index 4ea7434..d88803e 100644 --- a/src/test/java/org/json/junit/JSONPointerTest.java +++ b/src/test/java/org/json/junit/JSONPointerTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/json/junit/JSONStringTest.java b/src/test/java/org/json/junit/JSONStringTest.java index a199611..b4fee3e 100644 --- a/src/test/java/org/json/junit/JSONStringTest.java +++ b/src/test/java/org/json/junit/JSONStringTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/JSONStringerTest.java b/src/test/java/org/json/junit/JSONStringerTest.java index a99db3b..0ecb9d6 100644 --- a/src/test/java/org/json/junit/JSONStringerTest.java +++ b/src/test/java/org/json/junit/JSONStringerTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/JSONTokenerTest.java b/src/test/java/org/json/junit/JSONTokenerTest.java index e8e0f98..da716b8 100644 --- a/src/test/java/org/json/junit/JSONTokenerTest.java +++ b/src/test/java/org/json/junit/JSONTokenerTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/json/junit/PropertyTest.java b/src/test/java/org/json/junit/PropertyTest.java index e1a9b8d..eee482f 100644 --- a/src/test/java/org/json/junit/PropertyTest.java +++ b/src/test/java/org/json/junit/PropertyTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import java.util.*; diff --git a/src/test/java/org/json/junit/Util.java b/src/test/java/org/json/junit/Util.java index a4d0e22..b676045 100644 --- a/src/test/java/org/json/junit/Util.java +++ b/src/test/java/org/json/junit/Util.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.*; diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java index 2ab06be..f9d24a6 100755 --- a/src/test/java/org/json/junit/XMLConfigurationTest.java +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 0155472..906924d 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1,27 +1,7 @@ package org.json.junit; /* -Copyright (c) 2020 JSON.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Public Domain. */ import static org.junit.Assert.assertEquals; From a30d71fdca1a2bf3c1d1d15038cb904d01ad6264 Mon Sep 17 00:00:00 2001 From: Sean Leary Date: Sat, 24 Sep 2022 16:11:12 -0500 Subject: [PATCH 04/48] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d1ead9..5181b8b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 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/20220320/json-20220320.jar)** +**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20220924/json-20220924.jar)** # Overview From a6bdd081eb673509e73b712bd0936e0e52409aea Mon Sep 17 00:00:00 2001 From: Sean Leary Date: Sat, 24 Sep 2022 16:13:11 -0500 Subject: [PATCH 05/48] Update RELEASES.md --- docs/RELEASES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/RELEASES.md b/docs/RELEASES.md index 149525d..0d49331 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -5,6 +5,8 @@ and artifactId "json". For example: [https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav](https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav) ~~~ +20220924 New License - public domain, and some minor updates + 20220320 Wrap StackOverflow with JSONException 20211205 Recent commits and some bug fixes for similar() From 8439039da75eb539efb21a04442cfa8330740bda Mon Sep 17 00:00:00 2001 From: Sean Leary Date: Sat, 24 Sep 2022 16:25:18 -0500 Subject: [PATCH 06/48] Update pom.xml For the 20220924 release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1a93b34..4ef85a8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.json json - 20220320 + 20220924 bundle JSON in Java From 1915aab7c4bcaefb973e99889e4126320b1e2263 Mon Sep 17 00:00:00 2001 From: hendrixjoseph Date: Tue, 4 Oct 2022 14:32:41 -0400 Subject: [PATCH 07/48] create unit tests for various number formats --- .../org/json/junit/JSONObjectNumberTest.java | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/test/java/org/json/junit/JSONObjectNumberTest.java diff --git a/src/test/java/org/json/junit/JSONObjectNumberTest.java b/src/test/java/org/json/junit/JSONObjectNumberTest.java new file mode 100644 index 0000000..f6e13c6 --- /dev/null +++ b/src/test/java/org/json/junit/JSONObjectNumberTest.java @@ -0,0 +1,126 @@ +package org.json.junit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; + +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class JSONObjectNumberTest { + private final String objectString; + private Integer value = 50; + + @Parameters(name = "{index}: {0}") + public static Collection data() { + return Arrays.asList(new Object[][]{ + {"{value:50}", 1}, + {"{value:50.0}", 1}, + {"{value:5e1}", 1}, + {"{value:5E1}", 1}, + {"{value:5e1}", 1}, + {"{value:'50'}", 1}, + {"{value:-50}", -1}, + {"{value:-50.0}", -1}, + {"{value:-5e1}", -1}, + {"{value:-5E1}", -1}, + {"{value:-5e1}", -1}, + {"{value:'-50'}", -1} + // JSON does not support octal or hex numbers; + // see https://stackoverflow.com/a/52671839/6323312 + // "{value:062}", // octal 50 + // "{value:0x32}" // hex 50 + }); + } + + public JSONObjectNumberTest(String objectString, int resultIsNegative) { + this.objectString = objectString; + this.value *= resultIsNegative; + } + + private JSONObject object; + + @Before + public void setJsonObject() { + object = new JSONObject(objectString); + } + + @Test + public void testGetNumber() { + assertEquals(value.intValue(), object.getNumber("value").intValue()); + } + + @Test + public void testGetBigDecimal() { + assertTrue(BigDecimal.valueOf(value).compareTo(object.getBigDecimal("value")) == 0); + } + + @Test + public void testGetBigInteger() { + assertEquals(BigInteger.valueOf(value), object.getBigInteger("value")); + } + + @Test + public void testGetFloat() { + assertEquals(value.floatValue(), object.getFloat("value"), 0.0f); + } + + @Test + public void testGetDouble() { + assertEquals(value.doubleValue(), object.getDouble("value"), 0.0d); + } + + @Test + public void testGetInt() { + assertEquals(value.intValue(), object.getInt("value")); + } + + @Test + public void testGetLong() { + assertEquals(value.longValue(), object.getLong("value")); + } + + @Test + public void testOptNumber() { + assertEquals(value.intValue(), object.optNumber("value").intValue()); + } + + @Test + public void testOptBigDecimal() { + assertTrue(BigDecimal.valueOf(value).compareTo(object.optBigDecimal("value", null)) == 0); + } + + @Test + public void testOptBigInteger() { + assertEquals(BigInteger.valueOf(value), object.optBigInteger("value", null)); + } + + @Test + public void testOptFloat() { + assertEquals(value.floatValue(), object.optFloat("value"), 0.0f); + } + + @Test + public void testOptDouble() { + assertEquals(value.doubleValue(), object.optDouble("value"), 0.0d); + } + + @Test + public void testOptInt() { + assertEquals(value.intValue(), object.optInt("value")); + } + + @Test + public void testOptLong() { + assertEquals(value.longValue(), object.optLong("value")); + } +} From 61801c623e8fe51827b1e03a5ffebcebbb708994 Mon Sep 17 00:00:00 2001 From: TheCommandBlock <95651685+InACommandBlock@users.noreply.github.com> Date: Thu, 6 Oct 2022 00:48:34 +0200 Subject: [PATCH 08/48] Minor Adjustments Example.md Added syntax highlighting, standardised indentation --- Examples.md | 560 ++++++++++++++++++++++++++-------------------------- 1 file changed, 280 insertions(+), 280 deletions(-) diff --git a/Examples.md b/Examples.md index 8f28461..2671d1d 100644 --- a/Examples.md +++ b/Examples.md @@ -1,7 +1,7 @@

Examples

Imports used in the examples:

-``` +```java import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -14,208 +14,208 @@ import java.util.Properties;

Using JSONArray

-``` - private static void JSONExampleArray1() { - //We create a JSONObject from a String containing an array using JSONArray - //Firstly, we declare an Array in a String - - String arrayStr = - "["+"true,"+"false,"+ "\"true\","+ "\"false\","+"\"hello\","+"23.45e-4,"+ - "\"23.45\","+"42,"+"\"43\","+"["+"\"world\""+"],"+ - "{"+ - "\"key1\":\"value1\","+ - "\"key2\":\"value2\","+ - "\"key3\":\"value3\","+ - "\"key4\":\"value4\""+ - "},"+ - "0,"+"\"-1\""+ - "]"; - - //Then, we initializate the JSONArray thanks to its constructor - - JSONArray array = new JSONArray(arrayStr); - System.out.println("Values array: "+ array); - - //We convert that array into a JSONObject, but first, we need the labels, so we need another JSONArray with the labels. - //Here we will use an auxiliary function to get one for the example. - - JSONArray list = listNumberArray(array.length()); - System.out.println("Label Array: "+ list.toString()); - //Now, we construct the JSONObject using both the value array and the label array. - JSONObject object = array.toJSONObject(list); - System.out.println("Final JSONOBject: " + object); - } +```java +private static void JSONExampleArray1() { + //We create a JSONObject from a String containing an array using JSONArray + //Firstly, we declare an Array in a String + + String arrayStr = + "["+"true,"+"false,"+ "\"true\","+ "\"false\","+"\"hello\","+"23.45e-4,"+ + "\"23.45\","+"42,"+"\"43\","+"["+"\"world\""+"],"+ + "{"+ + "\"key1\":\"value1\","+ + "\"key2\":\"value2\","+ + "\"key3\":\"value3\","+ + "\"key4\":\"value4\""+ + "},"+ + "0,"+"\"-1\""+ + "]"; + + //Then, we initializate the JSONArray thanks to its constructor + + JSONArray array = new JSONArray(arrayStr); + System.out.println("Values array: "+ array); + + //We convert that array into a JSONObject, but first, we need the labels, so we need another JSONArray with the labels. + //Here we will use an auxiliary function to get one for the example. + + JSONArray list = listNumberArray(array.length()); + System.out.println("Label Array: "+ list.toString()); + //Now, we construct the JSONObject using both the value array and the label array. + JSONObject object = array.toJSONObject(list); + System.out.println("Final JSONOBject: " + object); +} - //This method creates an JSONArray of labels in which those are generated by their positions +//This method creates an JSONArray of labels in which those are generated by their positions - private static JSONArray listNumberArray(int max){ - JSONArray res = new JSONArray(); - for (int i=0; iUsing JSONStringer -``` - private static void JSONExampleStringer() { +```java +private static void JSONExampleStringer() { - //We initializate the JSONStringer + //We initializate the JSONStringer - JSONStringer jsonStringer = new JSONStringer(); + JSONStringer jsonStringer = new JSONStringer(); - //Now we start the process of adding elements with .object() + //Now we start the process of adding elements with .object() - jsonStringer.object(); + jsonStringer.object(); - //We can now add elements as keys and values with .values () and .key() + //We can now add elements as keys and values with .values () and .key() - jsonStringer.key("trueValue").value(true); - jsonStringer.key("falseValue").value(false); - jsonStringer.key("nullValue").value(null); - jsonStringer.key("stringValue").value("hello world!"); - jsonStringer.key("complexStringValue").value("h\be\tllo w\u1234orld!"); - jsonStringer.key("intValue").value(42); - jsonStringer.key("doubleValue").value(-23.45e67); + jsonStringer.key("trueValue").value(true); + jsonStringer.key("falseValue").value(false); + jsonStringer.key("nullValue").value(null); + jsonStringer.key("stringValue").value("hello world!"); + jsonStringer.key("complexStringValue").value("h\be\tllo w\u1234orld!"); + jsonStringer.key("intValue").value(42); + jsonStringer.key("doubleValue").value(-23.45e67); - //We end this prcedure with .ednObject + //We end this prcedure with .ednObject - jsonStringer.endObject(); + jsonStringer.endObject(); - //Once we have a JSONStringer, we convert it to JSONObject generating a String and using JSONObject's contructor. + //Once we have a JSONStringer, we convert it to JSONObject generating a String and using JSONObject's contructor. - String str = jsonStringer.toString(); - JSONObject jsonObject = new JSONObject(str); - - System.out.println("Final JSONOBject: " + jsonObject); - } + String str = jsonStringer.toString(); + JSONObject jsonObject = new JSONObject(str); + + System.out.println("Final JSONOBject: " + jsonObject); +} ```

Using JSONObject

-``` - private static void JSONExampleObject1() { +```java +private static void JSONExampleObject1() { - //We can create a JSONObject from a String with the class builder + //We can create a JSONObject from a String with the class builder - String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}"; - JSONObject example = new JSONObject(string); - System.out.println("Final JSONObject: " + example); - - } -``` + String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}"; + JSONObject example = new JSONObject(string); + System.out.println("Final JSONObject: " + example); + +} ``` - private static void JSONExampleObject2() { +```java +private static void JSONExampleObject2() { - //We can also create a JSONObject directly without messing around with any of the other functions. + //We can also create a JSONObject directly without messing around with any of the other functions. - JSONObject example = new JSONObject(); + JSONObject example = new JSONObject(); - //Now we add the keys and values in a similar way as the Stringer method - example.put("key", "value"); + //Now we add the keys and values in a similar way as the Stringer method + example.put("key", "value"); - //As you can see, the first entry is the key and the second would be its associeted value. + //As you can see, the first entry is the key and the second would be its associeted value. - example.put("key2", 5); - example.put("key3", -23.45e67); - example.put("trueValue", true); + example.put("key2", 5); + example.put("key3", -23.45e67); + example.put("trueValue", true); - //We can't add null values thougth + //We can't add null values thougth - //example.put("nullValue", null); //This is not possible - - System.out.println("Final JSONOBject: " + example); - } -``` + //example.put("nullValue", null); //This is not possible + + System.out.println("Final JSONOBject: " + example); +} ``` - private static void JSONExampleObject3() { +```java +private static void JSONExampleObject3() { - //We can also create a JSONObject with a Java Map - //YoU will need a Map whose keys are Strings. The values can be whatever you want + //We can also create a JSONObject with a Java Map + //YoU will need a Map whose keys are Strings. The values can be whatever you want - Map map = new HashMap(); - - map.put("key1", 1.0); - map.put("key2", -23.45e67); - - //We create the JSONObject with the map with its class builder + Map map = new HashMap(); - JSONObject example = new JSONObject(map); - System.out.println("Final JSONOBject: " + example); - } + map.put("key1", 1.0); + map.put("key2", -23.45e67); + + //We create the JSONObject with the map with its class builder + + JSONObject example = new JSONObject(map); + System.out.println("Final JSONOBject: " + example); +} ```

Using JSONWriter

-``` - private static void JSONExamplWriter() { - - //This method works in a very similar way to Object and Stringer in the construction of the JSON. - //The difference is that it needs a Java object called "Appendable" like StringBuilder - - StringBuilder write = new StringBuilder(); - JSONWriter jsonWriter = new JSONWriter(write); - - //We behave now the same way as Stringer - - jsonWriter.object(); - - jsonWriter.key("trueValue").value(true); - jsonWriter.key("falseValue").value(false); - jsonWriter.key("nullValue").value(null); - jsonWriter.key("stringValue").value("hello world!"); - jsonWriter.key("complexStringValue").value("h\be\tllo w\u1234orld!"); - jsonWriter.key("intValue").value(42); - jsonWriter.key("doubleValue").value(-23.45e67); - - jsonWriter.endObject(); - - //The resoult should be in the "write" object - - System.out.println("JSON: " + write.toString()); - - //The difference is that we don't get a JSONObject in this one. - - - } +```java +private static void JSONExamplWriter() { + + //This method works in a very similar way to Object and Stringer in the construction of the JSON. + //The difference is that it needs a Java object called "Appendable" like StringBuilder + + StringBuilder write = new StringBuilder(); + JSONWriter jsonWriter = new JSONWriter(write); + + //We behave now the same way as Stringer + + jsonWriter.object(); + + jsonWriter.key("trueValue").value(true); + jsonWriter.key("falseValue").value(false); + jsonWriter.key("nullValue").value(null); + jsonWriter.key("stringValue").value("hello world!"); + jsonWriter.key("complexStringValue").value("h\be\tllo w\u1234orld!"); + jsonWriter.key("intValue").value(42); + jsonWriter.key("doubleValue").value(-23.45e67); + + jsonWriter.endObject(); + + //The resoult should be in the "write" object + + System.out.println("JSON: " + write.toString()); + + //The difference is that we don't get a JSONObject in this one. + + +} ``` -``` - private static void JSONExampleTokener() { +```java +private static void JSONExampleTokener() { - //A partir de una String podemos crear un JSONTokener, que lo podemos usar alternativamente para JSONArray,JSONObject + //A partir de una String podemos crear un JSONTokener, que lo podemos usar alternativamente para JSONArray,JSONObject - String string = "this is not a valid JSON string"; - JSONTokener token = new JSONTokener(string); - - //Now you can use the token in JSONObject and Array the same way as a String + String string = "this is not a valid JSON string"; + JSONTokener token = new JSONTokener(string); - JSONObject object = new JSONObject(token); - JSONArray array = new JSONArray(token); - - } + //Now you can use the token in JSONObject and Array the same way as a String + + JSONObject object = new JSONObject(token); + JSONArray array = new JSONArray(token); + +} ```

Part 2: Conversion methods

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

@@ -223,144 +223,144 @@ import java.util.Properties;

Extra: Conversion to JSONArray

-``` - private static void JSONObjectToArray() { - //We start with a JSONObject - - String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}"; - - JSONObject example = new JSONObject(string); - - //We need a list of key strings like the reverse operation - - JSONArray keyStrings = listNumberArray(example.length()); - - //Then we convert to the Array using both elelements - - JSONArray array = example.toJSONArray(keyStrings); - - System.out.println("Final JSONArray: " + array); - } +```java +private static void JSONObjectToArray() { + //We start with a JSONObject + + String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}"; + + JSONObject example = new JSONObject(string); + + //We need a list of key strings like the reverse operation + + JSONArray keyStrings = listNumberArray(example.length()); + + //Then we convert to the Array using both elelements + + JSONArray array = example.toJSONArray(keyStrings); + + System.out.println("Final JSONArray: " + array); +} ```

XML Conversions

-``` - private static void XMLToExampleConversion() { +```java +private static void XMLToExampleConversion() { - //We start with a JSONObject - - String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}"; - JSONObject example = new JSONObject(string); + //We start with a JSONObject - //We obtain a String with XML format with toString() + String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}"; + JSONObject example = new JSONObject(string); - String output = XML.toString(example); - System.out.println("Final XML: " + output); - } + //We obtain a String with XML format with toString() + + String output = XML.toString(example); + System.out.println("Final XML: " + output); +} ``` -``` - private static void XMLFromExampleConversion() { +```java +private static void XMLFromExampleConversion() { - //We start with a string with the XML format + //We start with a string with the XML format - String string = "<0>value<1>5<2>-2.345E+68<3>true"; + String string = "<0>value<1>5<2>-2.345E+68<3>true"; - //We obtain a JSONObject with toJSONOBject() + //We obtain a JSONObject with toJSONOBject() - JSONObject output = XML.toJSONObject(string); - - System.out.println("Final JSONObject: " + output); - } + JSONObject output = XML.toJSONObject(string); + + System.out.println("Final JSONObject: " + output); +} ```

Cookie Conversions

-``` - private static void CookieToExampleConversion() { +```java +private static void CookieToExampleConversion() { - //We start with a JSONObject - //The JSONOBject needs to entries that gives the cookie a name and gives the field "name" a name too. - //The Cokkie format doesn't support booleans + //We start with a JSONObject + //The JSONOBject needs to entries that gives the cookie a name and gives the field "name" a name too. + //The Cokkie format doesn't support booleans - String string = "{\"name\":\"Cookie-Name\",\"value\":\"name\",\"1\":5,\"2\":-2.345E68,\"3\":'true'}"; - JSONObject example = new JSONObject(string); - - //We obtain a String with Cookie format with toString() + String string = "{\"name\":\"Cookie-Name\",\"value\":\"name\",\"1\":5,\"2\":-2.345E68,\"3\":'true'}"; + JSONObject example = new JSONObject(string); - String output = Cookie.toString(example); - System.out.println("Final Cookie: " + output); - } -``` + //We obtain a String with Cookie format with toString() + + String output = Cookie.toString(example); + System.out.println("Final Cookie: " + output); +} ``` - private static void CookieFromExampleConversion() { +```java +private static void CookieFromExampleConversion() { - //We start with a string with the Cookie format + //We start with a string with the Cookie format - String string = "Cookie-Name=name;1=5;2=-2.345E%2b68;3=true"; + String string = "Cookie-Name=name;1=5;2=-2.345E%2b68;3=true"; - //We obtain a JSONObject with toJSONOBject() + //We obtain a JSONObject with toJSONOBject() - JSONObject output = Cookie.toJSONObject(string); - System.out.println("Final JSONObject: " + output); - } + JSONObject output = Cookie.toJSONObject(string); + System.out.println("Final JSONObject: " + output); +} ```

HTTP Conversions

-``` - private static void HTTPToExampleConversion() { +```java +private static void HTTPToExampleConversion() { - //We start with a JSONObject - //The JSONObject must have the minimun header for a HTTP request or header + //We start with a JSONObject + //The JSONObject must have the minimun header for a HTTP request or header - String string = "{\"Method\":\"POST\",\"Request-URI\":'/',\"HTTP-Version\":'HTTP/1.1',\"Value1\":true,\"Value2\":2,\"Value3\":-2.345E68}"; + String string = "{\"Method\":\"POST\",\"Request-URI\":'/',\"HTTP-Version\":'HTTP/1.1',\"Value1\":true,\"Value2\":2,\"Value3\":-2.345E68}"; - JSONObject example = new JSONObject(string); + JSONObject example = new JSONObject(string); - //We obtain a String with HTTP format with toString() + //We obtain a String with HTTP format with toString() - String output = HTTP.toString(example); - System.out.println("Final HTTP: " + output); - } + String output = HTTP.toString(example); + System.out.println("Final HTTP: " + output); +} ``` -``` - private static void HTTPFromExampleConversion() { +```java +private static void HTTPFromExampleConversion() { - //We start with a string with the HTTP format + //We start with a string with the HTTP format - String string = "Final HTTP: POST '/' HTTP/1.1 Value3: -2.345E+68 Value1: true Value2: 2"; + String string = "Final HTTP: POST '/' HTTP/1.1 Value3: -2.345E+68 Value1: true Value2: 2"; - //We obtain a JSONObject with toJSONOBject() + //We obtain a JSONObject with toJSONOBject() - JSONObject output = HTTP.toJSONObject(string); - System.out.println("Final JSONObject: " + output); - } + JSONObject output = HTTP.toJSONObject(string); + System.out.println("Final JSONObject: " + output); +} ```

CDL Conversions

-``` +```java private static void CDLToExampleConversion() { - //We start with some JSONObjects with the same values in the keys but different values in the "values" + //We start with some JSONObjects with the same values in the keys but different values in the "values" + + String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}"; + JSONObject example = new JSONObject(string); - String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}"; - JSONObject example = new JSONObject(string); - - String string2 = "{\"0\":\"value2\",\"1\":6,\"2\":-8.345E68,\"3\":false}"; - JSONObject example2 = new JSONObject(string2); - - //We need now a JSONArray with those JSONObjects + String string2 = "{\"0\":\"value2\",\"1\":6,\"2\":-8.345E68,\"3\":false}"; + JSONObject example2 = new JSONObject(string2); - JSONArray array = new JSONArray(); - array.put(example); - array.put(example2); + //We need now a JSONArray with those JSONObjects - //We obtain a String with XML format with toString() + JSONArray array = new JSONArray(); + array.put(example); + array.put(example2); - String output = CDL.toString(array); - System.out.println("Final CDL: \r\n" + output); - } -``` + //We obtain a String with XML format with toString() + + String output = CDL.toString(array); + System.out.println("Final CDL: \r\n" + output); +} ``` +```java private static void CDLFromExampleConversion() { //We start wtih a String with the CDL format @@ -368,7 +368,7 @@ private static void CDLFromExampleConversion() { String string = "0,1,2,3\n" + "value,5,-2.345E+68,true\n" + "value2,6,-8.345E+68,false"; - + //We obtain a JSONArray with toJSONOBject() JSONArray output = CDL.toJSONArray(string); @@ -377,8 +377,8 @@ private static void CDLFromExampleConversion() { ```

Properties Conversions

-``` - private static Properties PropertyToExampleConversion() { +```java +private static Properties PropertyToExampleConversion() { //We start with a JSONObject @@ -391,43 +391,43 @@ private static void CDLFromExampleConversion() { System.out.println("Final Properties: " + output); return output; - } +} ``` -``` - private static void PropertyFromExampleConversion() { +```java +private static void PropertyFromExampleConversion() { - //We start with a Properties object + //We start with a Properties object - Properties input = PropertyToExampleConversion(); + Properties input = PropertyToExampleConversion(); - //We obtain a JSONObject with toJSONOBject() + //We obtain a JSONObject with toJSONOBject() - JSONObject output = Property.toJSONObject(input); - System.out.println("Final JSONObject: " + output); - } + JSONObject output = Property.toJSONObject(input); + System.out.println("Final JSONObject: " + output); +} ```

List of all examples methods

-``` - public static void main(String[] args) { - //JSONObjectToArray(); - //JSONExampleArray1(); - //JSONExampleArray2(); - //JSONExampleStringer(); - //JSONExampleObject1(); - //JSONExampleObject2(); - //JSONExampleObject3(); - //JSONExamplWriter(); - //XMLToExampleConversion(); - //XMLFromExampleConversion(); - //CookieToExampleConversion(); - //CookieFromExampleConversion(); - //HTTPToExampleConversion(); - //HTTPFromExampleConversion(); - //CDLToExampleConversion(); - //CDLFromExampleConversion(); - //PropertyToExampleConversion(); - //PropertyFromExampleConversion(); - } +```java +public static void main(String[] args) { + //JSONObjectToArray(); + //JSONExampleArray1(); + //JSONExampleArray2(); + //JSONExampleStringer(); + //JSONExampleObject1(); + //JSONExampleObject2(); + //JSONExampleObject3(); + //JSONExamplWriter(); + //XMLToExampleConversion(); + //XMLFromExampleConversion(); + //CookieToExampleConversion(); + //CookieFromExampleConversion(); + //HTTPToExampleConversion(); + //HTTPFromExampleConversion(); + //CDLToExampleConversion(); + //CDLFromExampleConversion(); + //PropertyToExampleConversion(); + //PropertyFromExampleConversion(); +} ``` From 12411b7981e47a3541d1ec27694ca2bc0507a7ba Mon Sep 17 00:00:00 2001 From: TheCommandBlock <95651685+InACommandBlock@users.noreply.github.com> Date: Thu, 6 Oct 2022 03:18:03 +0200 Subject: [PATCH 09/48] Update Examples.md Co-authored-by: JAYSE <104235500+JayseMayne@users.noreply.github.com> --- Examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples.md b/Examples.md index 2671d1d..fb010d5 100644 --- a/Examples.md +++ b/Examples.md @@ -406,7 +406,7 @@ private static void PropertyFromExampleConversion() { System.out.println("Final JSONObject: " + output); } ``` -

List of all examples methods

+

List of all examples methods

```java public static void main(String[] args) { From b7f708b22299501305c2bd8577eb7b5bbdbbb8f4 Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 6 Oct 2022 12:01:13 +0100 Subject: [PATCH 10/48] Altered XML toString to allow indentation param --- src/main/java/org/json/XML.java | 95 +++++++++++++++++++++-- src/test/java/org/json/junit/XMLTest.java | 78 +++++++++++++++++++ 2 files changed, 166 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 69782cb..28a292f 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -752,6 +752,11 @@ public static String toString(final Object object, final String tagName) { */ public static String toString(final Object object, final String tagName, final XMLParserConfiguration config) throws JSONException { + return toString(object, tagName, config, 0, 0); + } + + private static String toString(final Object object, final String tagName, final XMLParserConfiguration config, int indentFactor, int indent) + throws JSONException { StringBuilder sb = new StringBuilder(); JSONArray ja; JSONObject jo; @@ -761,9 +766,14 @@ public static String toString(final Object object, final String tagName, final X // Emit if (tagName != null) { + sb.append(indent(indent)); sb.append('<'); sb.append(tagName); sb.append('>'); + if(indentFactor > 0){ + sb.append("\n"); + indent += indentFactor; + } } // Loop thru the keys. @@ -806,31 +816,39 @@ public static String toString(final Object object, final String tagName, final X sb.append('<'); sb.append(key); sb.append('>'); - sb.append(toString(val, null, config)); + sb.append(toString(val, null, config, indentFactor, indent)); sb.append("'); } else { - sb.append(toString(val, key, config)); + sb.append(toString(val, key, config, indentFactor, indent)); } } } else if ("".equals(value)) { + sb.append(indent(indent)); sb.append('<'); sb.append(key); sb.append("/>"); + if(indentFactor > 0){ + sb.append("\n"); + } // Emit a new tag } else { - sb.append(toString(value, key, config)); + sb.append(toString(value, key, config, indentFactor, indent)); } } if (tagName != null) { // Emit the close tag + sb.append(indent(indent - indentFactor)); sb.append("'); + if(indentFactor > 0){ + sb.append("\n"); + } } return sb.toString(); @@ -849,15 +867,78 @@ public static String toString(final Object object, final String tagName, final X // XML does not have good support for arrays. If an array // appears in a place where XML is lacking, synthesize an // element. - sb.append(toString(val, tagName == null ? "array" : tagName, config)); + sb.append(toString(val, tagName == null ? "array" : tagName, config, indentFactor, indent)); } return sb.toString(); } + string = (object == null) ? "null" : escape(object.toString()); - return (tagName == null) ? "\"" + string + "\"" - : (string.length() == 0) ? "<" + tagName + "/>" : "<" + tagName - + ">" + string + ""; + if(tagName == null){ + return indent(indent) + "\"" + string + "\"" + ((indentFactor > 0) ? "\n" : ""); + } else if(string.length() == 0){ + return indent(indent) + "<" + tagName + "/>" + ((indentFactor > 0) ? "\n" : ""); + } else { + return indent(indent) + "<" + tagName + + ">" + string + "" + ((indentFactor > 0) ? "\n" : ""); + } + } + + /** + * Convert a JSONObject into a well-formed, pretty printed element-normal XML string. + * + * @param object + * A JSONObject. + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return A string. + * @throws JSONException Thrown if there is an error parsing the string + */ + public static String toString(Object object, int indentFactor){ + return toString(object, null, XMLParserConfiguration.ORIGINAL, indentFactor); + } + + /** + * Convert a JSONObject into a well-formed, pretty printed element-normal XML string. + * + * @param object + * A JSONObject. + * @param tagName + * The optional name of the enclosing tag. + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return A string. + * @throws JSONException Thrown if there is an error parsing the string + */ + public static String toString(final Object object, final String tagName, int indentFactor) { + return toString(object, tagName, XMLParserConfiguration.ORIGINAL, indentFactor); + } + + /** + * Convert a JSONObject into a well-formed, pretty printed element-normal XML string. + * + * @param object + * A JSONObject. + * @param tagName + * The optional name of the enclosing tag. + * @param config + * Configuration that can control output to XML. + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @return A string. + * @throws JSONException Thrown if there is an error parsing the string + */ + public static String toString(final Object object, final String tagName, final XMLParserConfiguration config, int indentFactor) + throws JSONException { + return toString(object, tagName, config, indentFactor, 0); + } + + private static final String indent(int indent) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < indent; i++) { + sb.append(' '); + } + return sb.toString(); } } diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 906924d..9ba2279 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1049,4 +1049,82 @@ public void testXSITypeMapNotModifiable() { fail("Expected to be unable to modify the config"); } catch (Exception ignored) { } } + + @Test + public void testXmlToStringWithIndent(){ + String str = "{\n" + + " \"success\": true,\n" + + " \"error\": null,\n" + + " \"response\": [\n" + + " {\n" + + " \"timestamp\": 1664917200,\n" + + " \"dateTimeISO\": \"2022-10-05T00:00:00+03:00\",\n" + + " \"loc\": {\n" + + " \"lat\": 39.91987,\n" + + " \"long\": 32.85427\n" + + " },\n" + + " \"place\": {\n" + + " \"name\": \"ankara\",\n" + + " \"state\": \"an\",\n" + + " \"country\": \"tr\"\n" + + " },\n" + + " \"profile\": {\n" + + " \"tz\": \"Europe/Istanbul\"\n" + + " },\n" + + " \"sun\": {\n" + + " \"rise\": 1664941721,\n" + + " \"riseISO\": \"2022-10-05T06:48:41+03:00\",\n" + + " \"set\": 1664983521,\n" + + " \"setISO\": \"2022-10-05T18:25:21+03:00\",\n" + + " \"transit\": 1664962621,\n" + + " \"transitISO\": \"2022-10-05T12:37:01+03:00\",\n" + + " \"midnightSun\": false,\n" + + " \"polarNight\": false,\n" + + " \"twilight\": {\n" + + " \"civilBegin\": 1664940106,\n" + + " \"civilBeginISO\": \"2022-10-05T06:21:46+03:00\",\n" + + " \"civilEnd\": 1664985136,\n" + + " \"civilEndISO\": \"2022-10-05T18:52:16+03:00\",\n" + + " \"nauticalBegin\": 1664938227,\n" + + " \"nauticalBeginISO\": \"2022-10-05T05:50:27+03:00\",\n" + + " \"nauticalEnd\": 1664987015,\n" + + " \"nauticalEndISO\": \"2022-10-05T19:23:35+03:00\",\n" + + " \"astronomicalBegin\": 1664936337,\n" + + " \"astronomicalBeginISO\": \"2022-10-05T05:18:57+03:00\",\n" + + " \"astronomicalEnd\": 1664988905,\n" + + " \"astronomicalEndISO\": \"2022-10-05T19:55:05+03:00\"\n" + + " }\n" + + " },\n" + + " \"moon\": {\n" + + " \"rise\": 1664976480,\n" + + " \"riseISO\": \"2022-10-05T16:28:00+03:00\",\n" + + " \"set\": 1664921520,\n" + + " \"setISO\": \"2022-10-05T01:12:00+03:00\",\n" + + " \"transit\": 1664994240,\n" + + " \"transitISO\": \"2022-10-05T21:24:00+03:00\",\n" + + " \"underfoot\": 1664949360,\n" + + " \"underfootISO\": \"2022-10-05T08:56:00+03:00\",\n" + + " \"phase\": {\n" + + " \"phase\": 0.3186,\n" + + " \"name\": \"waxing gibbous\",\n" + + " \"illum\": 71,\n" + + " \"age\": 9.41,\n" + + " \"angle\": 0.55\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + "}" ; + JSONObject jsonObject = new JSONObject(str); + String xmlString = XML.toString(jsonObject, "Outer", 1); + System.out.println(xmlString); + System.out.println(XML.toIndentedXmlString(xmlString, 2, true)); + + + } + + } + + + From fa457a4113d34fe768ebdc6ee39cdcd122ba1596 Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 6 Oct 2022 12:01:26 +0100 Subject: [PATCH 11/48] Test cases for XML toString indentation --- src/test/java/org/json/junit/XMLTest.java | 1505 ++++++++++++++++++++- 1 file changed, 1501 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 9ba2279..8d25b7e 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1051,7 +1051,7 @@ public void testXSITypeMapNotModifiable() { } @Test - public void testXmlToStringWithIndent(){ + public void testIndentComplicatedJsonObject(){ String str = "{\n" + " \"success\": true,\n" + " \"error\": null,\n" + @@ -1116,14 +1116,1511 @@ public void testXmlToStringWithIndent(){ " ]\n" + "}" ; JSONObject jsonObject = new JSONObject(str); - String xmlString = XML.toString(jsonObject, "Outer", 1); - System.out.println(xmlString); - System.out.println(XML.toIndentedXmlString(xmlString, 2, true)); + String actualIndentedXmlString = XML.toString(jsonObject, 1); + String expected = "true\n" + + "\n" + + " 2022-10-05T00:00:00+03:00\n" + + " \n" + + " 39.91987\n" + + " 32.85427\n" + + " \n" + + " \n" + + " \n" + + " 0.3186\n" + + " waxing gibbous\n" + + " 0.55\n" + + " 71\n" + + " 9.41\n" + + " \n" + + " 2022-10-05T01:12:00+03:00\n" + + " 1664949360\n" + + " 1664921520\n" + + " 1664994240\n" + + " 2022-10-05T21:24:00+03:00\n" + + " 2022-10-05T16:28:00+03:00\n" + + " 1664976480\n" + + " 2022-10-05T08:56:00+03:00\n" + + " \n" + + " \n" + + " Europe/Istanbul\n" + + " \n" + + " \n" + + " tr\n" + + " ankara\n" + + " an\n" + + " \n" + + " \n" + + " 2022-10-05T18:25:21+03:00\n" + + " false\n" + + " 1664983521\n" + + " 1664962621\n" + + " false\n" + + " 2022-10-05T12:37:01+03:00\n" + + " 2022-10-05T06:48:41+03:00\n" + + " 1664941721\n" + + " \n" + + " 1664985136\n" + + " 1664936337\n" + + " 1664988905\n" + + " 2022-10-05T05:18:57+03:00\n" + + " 1664940106\n" + + " 2022-10-05T19:23:35+03:00\n" + + " 2022-10-05T19:55:05+03:00\n" + + " 1664938227\n" + + " 1664987015\n" + + " 2022-10-05T05:50:27+03:00\n" + + " 2022-10-05T06:21:46+03:00\n" + + " 2022-10-05T18:52:16+03:00\n" + + " \n" + + " \n" + + " 1664917200\n" + + "\n" + + "null\n"; + assertEquals(actualIndentedXmlString, expected); } + @Test + public void testIndentSimpleJsonObject(){ + String str = "{ \"employee\": { \n" + + " \"name\": \"sonoo\", \n" + + " \"salary\": 56000, \n" + + " \"married\": true \n" + + " }}"; + JSONObject jsonObject = new JSONObject(str); + String actual = XML.toString(jsonObject, "Test", 2); + String expected = "\n" + + " \n" + + " sonoo\n" + + " 56000\n" + + " true\n" + + " \n" + + "\n"; + assertEquals(actual, expected); + } + @Test + public void testIndentSimpleJsonArray(){ + String str = "[ \n" + + " {\"name\":\"Ram\", \"email\":\"Ram@gmail.com\"}, \n" + + " {\"name\":\"Bob\", \"email\":\"bob32@gmail.com\"} \n" + + "] "; + JSONArray jsonObject = new JSONArray(str); + String actual = XML.toString(jsonObject, 2); + String expected = "\n" + + " Ram\n" + + " Ram@gmail.com\n" + + "\n" + + "\n" + + " Bob\n" + + " bob32@gmail.com\n" + + "\n"; + assertEquals(actual, expected); + + + } + @Test + public void testIndentComplicatedJsonObjectWithArrayAndWithConfig(){ + String str = "{\n" + + " \"success\": true,\n" + + " \"error\": null,\n" + + " \"response\": [\n" + + " {\n" + + " \"loc\": {\n" + + " \"long\": 31.25,\n" + + " \"lat\": 30.063\n" + + " },\n" + + " \"interval\": \"day\",\n" + + " \"place\": {\n" + + " \"name\": \"cairo\",\n" + + " \"state\": \"qh\",\n" + + " \"country\": \"eg\"\n" + + " },\n" + + " \"periods\": [\n" + + " {\n" + + " \"timestamp\": 1665032400,\n" + + " \"validTime\": \"2022-10-06T07:00:00+02:00\",\n" + + " \"dateTimeISO\": \"2022-10-06T07:00:00+02:00\",\n" + + " \"maxTempC\": 32,\n" + + " \"maxTempF\": 90,\n" + + " \"minTempC\": 19,\n" + + " \"minTempF\": 66,\n" + + " \"avgTempC\": 25,\n" + + " \"avgTempF\": 78,\n" + + " \"tempC\": null,\n" + + " \"tempF\": null,\n" + + " \"maxFeelslikeC\": 32,\n" + + " \"maxFeelslikeF\": 89,\n" + + " \"minFeelslikeC\": 21,\n" + + " \"minFeelslikeF\": 70,\n" + + " \"avgFeelslikeC\": 26,\n" + + " \"avgFeelslikeF\": 80,\n" + + " \"feelslikeC\": 21,\n" + + " \"feelslikeF\": 70,\n" + + " \"maxDewpointC\": 17,\n" + + " \"maxDewpointF\": 63,\n" + + " \"minDewpointC\": 11,\n" + + " \"minDewpointF\": 52,\n" + + " \"avgDewpointC\": 14,\n" + + " \"avgDewpointF\": 58,\n" + + " \"dewpointC\": 17,\n" + + " \"dewpointF\": 63,\n" + + " \"maxHumidity\": 77,\n" + + " \"minHumidity\": 29,\n" + + " \"humidity\": 77,\n" + + " \"pop\": 0,\n" + + " \"precipMM\": 0,\n" + + " \"precipIN\": 0,\n" + + " \"iceaccum\": null,\n" + + " \"iceaccumMM\": null,\n" + + " \"iceaccumIN\": null,\n" + + " \"snowCM\": 0,\n" + + " \"snowIN\": 0,\n" + + " \"pressureMB\": 1015,\n" + + " \"pressureIN\": 29.97,\n" + + " \"windDir\": \"N\",\n" + + " \"windDirDEG\": 353,\n" + + " \"windSpeedKTS\": 5,\n" + + " \"windSpeedKPH\": 9,\n" + + " \"windSpeedMPH\": 6,\n" + + " \"windGustKTS\": 21,\n" + + " \"windGustKPH\": 40,\n" + + " \"windGustMPH\": 25,\n" + + " \"windDirMax\": \"NNW\",\n" + + " \"windDirMaxDEG\": 342,\n" + + " \"windSpeedMaxKTS\": 9,\n" + + " \"windSpeedMaxKPH\": 16,\n" + + " \"windSpeedMaxMPH\": 10,\n" + + " \"windDirMin\": \"N\",\n" + + " \"windDirMinDEG\": 353,\n" + + " \"windSpeedMinKTS\": 1,\n" + + " \"windSpeedMinKPH\": 2,\n" + + " \"windSpeedMinMPH\": 1,\n" + + " \"windDir80m\": \"N\",\n" + + " \"windDir80mDEG\": 11,\n" + + " \"windSpeed80mKTS\": 12,\n" + + " \"windSpeed80mKPH\": 22,\n" + + " \"windSpeed80mMPH\": 13,\n" + + " \"windGust80mKTS\": 22,\n" + + " \"windGust80mKPH\": 41,\n" + + " \"windGust80mMPH\": 25,\n" + + " \"windDirMax80m\": \"NNW\",\n" + + " \"windDirMax80mDEG\": 343,\n" + + " \"windSpeedMax80mKTS\": 22,\n" + + " \"windSpeedMax80mKPH\": 41,\n" + + " \"windSpeedMax80mMPH\": 25,\n" + + " \"windDirMin80m\": \"E\",\n" + + " \"windDirMin80mDEG\": 95,\n" + + " \"windSpeedMin80mKTS\": 8,\n" + + " \"windSpeedMin80mKPH\": 15,\n" + + " \"windSpeedMin80mMPH\": 10,\n" + + " \"sky\": 22,\n" + + " \"cloudsCoded\": \"FW\",\n" + + " \"weather\": \"Mostly Sunny\",\n" + + " \"weatherCoded\": [],\n" + + " \"weatherPrimary\": \"Mostly Sunny\",\n" + + " \"weatherPrimaryCoded\": \"::FW\",\n" + + " \"icon\": \"fair.png\",\n" + + " \"visibilityKM\": 24.135,\n" + + " \"visibilityMI\": 15,\n" + + " \"uvi\": 6,\n" + + " \"solradWM2\": 5608,\n" + + " \"solradMinWM2\": 0,\n" + + " \"solradMaxWM2\": 778,\n" + + " \"isDay\": true,\n" + + " \"maxCoverage\": \"\",\n" + + " \"sunrise\": 1665028274,\n" + + " \"sunset\": 1665070502,\n" + + " \"sunriseISO\": \"2022-10-06T05:51:14+02:00\",\n" + + " \"sunsetISO\": \"2022-10-06T17:35:02+02:00\"\n" + + " },\n" + + " {\n" + + " \"timestamp\": 1665118800,\n" + + " \"validTime\": \"2022-10-07T07:00:00+02:00\",\n" + + " \"dateTimeISO\": \"2022-10-07T07:00:00+02:00\",\n" + + " \"maxTempC\": 30,\n" + + " \"maxTempF\": 86,\n" + + " \"minTempC\": 19,\n" + + " \"minTempF\": 66,\n" + + " \"avgTempC\": 24,\n" + + " \"avgTempF\": 76,\n" + + " \"tempC\": null,\n" + + " \"tempF\": null,\n" + + " \"maxFeelslikeC\": 29,\n" + + " \"maxFeelslikeF\": 85,\n" + + " \"minFeelslikeC\": 19,\n" + + " \"minFeelslikeF\": 67,\n" + + " \"avgFeelslikeC\": 24,\n" + + " \"avgFeelslikeF\": 76,\n" + + " \"feelslikeC\": 19,\n" + + " \"feelslikeF\": 67,\n" + + " \"maxDewpointC\": 15,\n" + + " \"maxDewpointF\": 60,\n" + + " \"minDewpointC\": 10,\n" + + " \"minDewpointF\": 50,\n" + + " \"avgDewpointC\": 12,\n" + + " \"avgDewpointF\": 54,\n" + + " \"dewpointC\": 15,\n" + + " \"dewpointF\": 60,\n" + + " \"maxHumidity\": 77,\n" + + " \"minHumidity\": 30,\n" + + " \"humidity\": 77,\n" + + " \"pop\": 0,\n" + + " \"precipMM\": 0,\n" + + " \"precipIN\": 0,\n" + + " \"iceaccum\": null,\n" + + " \"iceaccumMM\": null,\n" + + " \"iceaccumIN\": null,\n" + + " \"snowCM\": 0,\n" + + " \"snowIN\": 0,\n" + + " \"pressureMB\": 1014,\n" + + " \"pressureIN\": 29.95,\n" + + " \"windDir\": \"NW\",\n" + + " \"windDirDEG\": 325,\n" + + " \"windSpeedKTS\": 1,\n" + + " \"windSpeedKPH\": 2,\n" + + " \"windSpeedMPH\": 1,\n" + + " \"windGustKTS\": 16,\n" + + " \"windGustKPH\": 29,\n" + + " \"windGustMPH\": 18,\n" + + " \"windDirMax\": \"WNW\",\n" + + " \"windDirMaxDEG\": 298,\n" + + " \"windSpeedMaxKTS\": 7,\n" + + " \"windSpeedMaxKPH\": 13,\n" + + " \"windSpeedMaxMPH\": 8,\n" + + " \"windDirMin\": \"NW\",\n" + + " \"windDirMinDEG\": 325,\n" + + " \"windSpeedMinKTS\": 1,\n" + + " \"windSpeedMinKPH\": 2,\n" + + " \"windSpeedMinMPH\": 1,\n" + + " \"windDir80m\": \"NNW\",\n" + + " \"windDir80mDEG\": 347,\n" + + " \"windSpeed80mKTS\": 6,\n" + + " \"windSpeed80mKPH\": 10,\n" + + " \"windSpeed80mMPH\": 6,\n" + + " \"windGust80mKTS\": 20,\n" + + " \"windGust80mKPH\": 37,\n" + + " \"windGust80mMPH\": 23,\n" + + " \"windDirMax80m\": \"NW\",\n" + + " \"windDirMax80mDEG\": 316,\n" + + " \"windSpeedMax80mKTS\": 20,\n" + + " \"windSpeedMax80mKPH\": 37,\n" + + " \"windSpeedMax80mMPH\": 23,\n" + + " \"windDirMin80m\": \"NNW\",\n" + + " \"windDirMin80mDEG\": 347,\n" + + " \"windSpeedMin80mKTS\": 6,\n" + + " \"windSpeedMin80mKPH\": 10,\n" + + " \"windSpeedMin80mMPH\": 6,\n" + + " \"sky\": 30,\n" + + " \"cloudsCoded\": \"FW\",\n" + + " \"weather\": \"Mostly Sunny\",\n" + + " \"weatherCoded\": [],\n" + + " \"weatherPrimary\": \"Mostly Sunny\",\n" + + " \"weatherPrimaryCoded\": \"::FW\",\n" + + " \"icon\": \"fair.png\",\n" + + " \"visibilityKM\": 24.135,\n" + + " \"visibilityMI\": 15,\n" + + " \"uvi\": 6,\n" + + " \"solradWM2\": 5486,\n" + + " \"solradMinWM2\": 0,\n" + + " \"solradMaxWM2\": 742,\n" + + " \"isDay\": true,\n" + + " \"maxCoverage\": \"\",\n" + + " \"sunrise\": 1665114710,\n" + + " \"sunset\": 1665156831,\n" + + " \"sunriseISO\": \"2022-10-07T05:51:50+02:00\",\n" + + " \"sunsetISO\": \"2022-10-07T17:33:51+02:00\"\n" + + " },\n" + + " {\n" + + " \"timestamp\": 1665205200,\n" + + " \"validTime\": \"2022-10-08T07:00:00+02:00\",\n" + + " \"dateTimeISO\": \"2022-10-08T07:00:00+02:00\",\n" + + " \"maxTempC\": 30,\n" + + " \"maxTempF\": 87,\n" + + " \"minTempC\": 19,\n" + + " \"minTempF\": 66,\n" + + " \"avgTempC\": 25,\n" + + " \"avgTempF\": 76,\n" + + " \"tempC\": null,\n" + + " \"tempF\": null,\n" + + " \"maxFeelslikeC\": 30,\n" + + " \"maxFeelslikeF\": 86,\n" + + " \"minFeelslikeC\": 19,\n" + + " \"minFeelslikeF\": 67,\n" + + " \"avgFeelslikeC\": 25,\n" + + " \"avgFeelslikeF\": 76,\n" + + " \"feelslikeC\": 19,\n" + + " \"feelslikeF\": 67,\n" + + " \"maxDewpointC\": 15,\n" + + " \"maxDewpointF\": 59,\n" + + " \"minDewpointC\": 11,\n" + + " \"minDewpointF\": 52,\n" + + " \"avgDewpointC\": 13,\n" + + " \"avgDewpointF\": 56,\n" + + " \"dewpointC\": 15,\n" + + " \"dewpointF\": 59,\n" + + " \"maxHumidity\": 76,\n" + + " \"minHumidity\": 32,\n" + + " \"humidity\": 76,\n" + + " \"pop\": 0,\n" + + " \"precipMM\": 0,\n" + + " \"precipIN\": 0,\n" + + " \"iceaccum\": null,\n" + + " \"iceaccumMM\": null,\n" + + " \"iceaccumIN\": null,\n" + + " \"snowCM\": 0,\n" + + " \"snowIN\": 0,\n" + + " \"pressureMB\": 1014,\n" + + " \"pressureIN\": 29.94,\n" + + " \"windDir\": \"NNE\",\n" + + " \"windDirDEG\": 21,\n" + + " \"windSpeedKTS\": 1,\n" + + " \"windSpeedKPH\": 2,\n" + + " \"windSpeedMPH\": 1,\n" + + " \"windGustKTS\": 17,\n" + + " \"windGustKPH\": 32,\n" + + " \"windGustMPH\": 20,\n" + + " \"windDirMax\": \"WNW\",\n" + + " \"windDirMaxDEG\": 301,\n" + + " \"windSpeedMaxKTS\": 7,\n" + + " \"windSpeedMaxKPH\": 13,\n" + + " \"windSpeedMaxMPH\": 8,\n" + + " \"windDirMin\": \"NNE\",\n" + + " \"windDirMinDEG\": 21,\n" + + " \"windSpeedMinKTS\": 1,\n" + + " \"windSpeedMinKPH\": 2,\n" + + " \"windSpeedMinMPH\": 1,\n" + + " \"windDir80m\": \"NW\",\n" + + " \"windDir80mDEG\": 309,\n" + + " \"windSpeed80mKTS\": 5,\n" + + " \"windSpeed80mKPH\": 9,\n" + + " \"windSpeed80mMPH\": 5,\n" + + " \"windGust80mKTS\": 17,\n" + + " \"windGust80mKPH\": 31,\n" + + " \"windGust80mMPH\": 19,\n" + + " \"windDirMax80m\": \"NW\",\n" + + " \"windDirMax80mDEG\": 322,\n" + + " \"windSpeedMax80mKTS\": 17,\n" + + " \"windSpeedMax80mKPH\": 31,\n" + + " \"windSpeedMax80mMPH\": 19,\n" + + " \"windDirMin80m\": \"NW\",\n" + + " \"windDirMin80mDEG\": 309,\n" + + " \"windSpeedMin80mKTS\": 5,\n" + + " \"windSpeedMin80mKPH\": 9,\n" + + " \"windSpeedMin80mMPH\": 5,\n" + + " \"sky\": 47,\n" + + " \"cloudsCoded\": \"SC\",\n" + + " \"weather\": \"Partly Cloudy\",\n" + + " \"weatherCoded\": [],\n" + + " \"weatherPrimary\": \"Partly Cloudy\",\n" + + " \"weatherPrimaryCoded\": \"::SC\",\n" + + " \"icon\": \"pcloudy.png\",\n" + + " \"visibilityKM\": 24.135,\n" + + " \"visibilityMI\": 15,\n" + + " \"uvi\": 7,\n" + + " \"solradWM2\": 4785,\n" + + " \"solradMinWM2\": 0,\n" + + " \"solradMaxWM2\": 682,\n" + + " \"isDay\": true,\n" + + " \"maxCoverage\": \"\",\n" + + " \"sunrise\": 1665201146,\n" + + " \"sunset\": 1665243161,\n" + + " \"sunriseISO\": \"2022-10-08T05:52:26+02:00\",\n" + + " \"sunsetISO\": \"2022-10-08T17:32:41+02:00\"\n" + + " },\n" + + " {\n" + + " \"timestamp\": 1665291600,\n" + + " \"validTime\": \"2022-10-09T07:00:00+02:00\",\n" + + " \"dateTimeISO\": \"2022-10-09T07:00:00+02:00\",\n" + + " \"maxTempC\": 31,\n" + + " \"maxTempF\": 87,\n" + + " \"minTempC\": 19,\n" + + " \"minTempF\": 67,\n" + + " \"avgTempC\": 25,\n" + + " \"avgTempF\": 77,\n" + + " \"tempC\": null,\n" + + " \"tempF\": null,\n" + + " \"maxFeelslikeC\": 30,\n" + + " \"maxFeelslikeF\": 86,\n" + + " \"minFeelslikeC\": 20,\n" + + " \"minFeelslikeF\": 67,\n" + + " \"avgFeelslikeC\": 25,\n" + + " \"avgFeelslikeF\": 77,\n" + + " \"feelslikeC\": 20,\n" + + " \"feelslikeF\": 67,\n" + + " \"maxDewpointC\": 17,\n" + + " \"maxDewpointF\": 63,\n" + + " \"minDewpointC\": 11,\n" + + " \"minDewpointF\": 52,\n" + + " \"avgDewpointC\": 14,\n" + + " \"avgDewpointF\": 57,\n" + + " \"dewpointC\": 17,\n" + + " \"dewpointF\": 63,\n" + + " \"maxHumidity\": 86,\n" + + " \"minHumidity\": 31,\n" + + " \"humidity\": 86,\n" + + " \"pop\": 0,\n" + + " \"precipMM\": 0,\n" + + " \"precipIN\": 0,\n" + + " \"iceaccum\": null,\n" + + " \"iceaccumMM\": null,\n" + + " \"iceaccumIN\": null,\n" + + " \"snowCM\": 0,\n" + + " \"snowIN\": 0,\n" + + " \"pressureMB\": 1016,\n" + + " \"pressureIN\": 29.99,\n" + + " \"windDir\": \"N\",\n" + + " \"windDirDEG\": 356,\n" + + " \"windSpeedKTS\": 2,\n" + + " \"windSpeedKPH\": 4,\n" + + " \"windSpeedMPH\": 2,\n" + + " \"windGustKTS\": 19,\n" + + " \"windGustKPH\": 36,\n" + + " \"windGustMPH\": 22,\n" + + " \"windDirMax\": \"NNW\",\n" + + " \"windDirMaxDEG\": 343,\n" + + " \"windSpeedMaxKTS\": 8,\n" + + " \"windSpeedMaxKPH\": 14,\n" + + " \"windSpeedMaxMPH\": 9,\n" + + " \"windDirMin\": \"N\",\n" + + " \"windDirMinDEG\": 356,\n" + + " \"windSpeedMinKTS\": 2,\n" + + " \"windSpeedMinKPH\": 4,\n" + + " \"windSpeedMinMPH\": 2,\n" + + " \"windDir80m\": \"NW\",\n" + + " \"windDir80mDEG\": 316,\n" + + " \"windSpeed80mKTS\": 5,\n" + + " \"windSpeed80mKPH\": 9,\n" + + " \"windSpeed80mMPH\": 6,\n" + + " \"windGust80mKTS\": 20,\n" + + " \"windGust80mKPH\": 36,\n" + + " \"windGust80mMPH\": 23,\n" + + " \"windDirMax80m\": \"N\",\n" + + " \"windDirMax80mDEG\": 354,\n" + + " \"windSpeedMax80mKTS\": 20,\n" + + " \"windSpeedMax80mKPH\": 36,\n" + + " \"windSpeedMax80mMPH\": 23,\n" + + " \"windDirMin80m\": \"NW\",\n" + + " \"windDirMin80mDEG\": 316,\n" + + " \"windSpeedMin80mKTS\": 5,\n" + + " \"windSpeedMin80mKPH\": 9,\n" + + " \"windSpeedMin80mMPH\": 6,\n" + + " \"sky\": 47,\n" + + " \"cloudsCoded\": \"SC\",\n" + + " \"weather\": \"Partly Cloudy\",\n" + + " \"weatherCoded\": [],\n" + + " \"weatherPrimary\": \"Partly Cloudy\",\n" + + " \"weatherPrimaryCoded\": \"::SC\",\n" + + " \"icon\": \"pcloudy.png\",\n" + + " \"visibilityKM\": 24.135,\n" + + " \"visibilityMI\": 15,\n" + + " \"uvi\": 7,\n" + + " \"solradWM2\": 4768,\n" + + " \"solradMinWM2\": 0,\n" + + " \"solradMaxWM2\": 726,\n" + + " \"isDay\": true,\n" + + " \"maxCoverage\": \"\",\n" + + " \"sunrise\": 1665287583,\n" + + " \"sunset\": 1665329491,\n" + + " \"sunriseISO\": \"2022-10-09T05:53:03+02:00\",\n" + + " \"sunsetISO\": \"2022-10-09T17:31:31+02:00\"\n" + + " },\n" + + " {\n" + + " \"timestamp\": 1665378000,\n" + + " \"validTime\": \"2022-10-10T07:00:00+02:00\",\n" + + " \"dateTimeISO\": \"2022-10-10T07:00:00+02:00\",\n" + + " \"maxTempC\": 31,\n" + + " \"maxTempF\": 87,\n" + + " \"minTempC\": 21,\n" + + " \"minTempF\": 70,\n" + + " \"avgTempC\": 26,\n" + + " \"avgTempF\": 78,\n" + + " \"tempC\": null,\n" + + " \"tempF\": null,\n" + + " \"maxFeelslikeC\": 30,\n" + + " \"maxFeelslikeF\": 86,\n" + + " \"minFeelslikeC\": 21,\n" + + " \"minFeelslikeF\": 69,\n" + + " \"avgFeelslikeC\": 25,\n" + + " \"avgFeelslikeF\": 78,\n" + + " \"feelslikeC\": 21,\n" + + " \"feelslikeF\": 69,\n" + + " \"maxDewpointC\": 16,\n" + + " \"maxDewpointF\": 61,\n" + + " \"minDewpointC\": 13,\n" + + " \"minDewpointF\": 55,\n" + + " \"avgDewpointC\": 14,\n" + + " \"avgDewpointF\": 58,\n" + + " \"dewpointC\": 16,\n" + + " \"dewpointF\": 61,\n" + + " \"maxHumidity\": 75,\n" + + " \"minHumidity\": 35,\n" + + " \"humidity\": 75,\n" + + " \"pop\": 0,\n" + + " \"precipMM\": 0,\n" + + " \"precipIN\": 0,\n" + + " \"iceaccum\": null,\n" + + " \"iceaccumMM\": null,\n" + + " \"iceaccumIN\": null,\n" + + " \"snowCM\": 0,\n" + + " \"snowIN\": 0,\n" + + " \"pressureMB\": 1017,\n" + + " \"pressureIN\": 30.03,\n" + + " \"windDir\": \"N\",\n" + + " \"windDirDEG\": 358,\n" + + " \"windSpeedKTS\": 2,\n" + + " \"windSpeedKPH\": 4,\n" + + " \"windSpeedMPH\": 2,\n" + + " \"windGustKTS\": 16,\n" + + " \"windGustKPH\": 30,\n" + + " \"windGustMPH\": 19,\n" + + " \"windDirMax\": \"N\",\n" + + " \"windDirMaxDEG\": 10,\n" + + " \"windSpeedMaxKTS\": 8,\n" + + " \"windSpeedMaxKPH\": 15,\n" + + " \"windSpeedMaxMPH\": 9,\n" + + " \"windDirMin\": \"N\",\n" + + " \"windDirMinDEG\": 358,\n" + + " \"windSpeedMinKTS\": 2,\n" + + " \"windSpeedMinKPH\": 4,\n" + + " \"windSpeedMinMPH\": 2,\n" + + " \"windDir80m\": \"N\",\n" + + " \"windDir80mDEG\": 8,\n" + + " \"windSpeed80mKTS\": 7,\n" + + " \"windSpeed80mKPH\": 13,\n" + + " \"windSpeed80mMPH\": 8,\n" + + " \"windGust80mKTS\": 19,\n" + + " \"windGust80mKPH\": 36,\n" + + " \"windGust80mMPH\": 22,\n" + + " \"windDirMax80m\": \"N\",\n" + + " \"windDirMax80mDEG\": 10,\n" + + " \"windSpeedMax80mKTS\": 19,\n" + + " \"windSpeedMax80mKPH\": 36,\n" + + " \"windSpeedMax80mMPH\": 22,\n" + + " \"windDirMin80m\": \"E\",\n" + + " \"windDirMin80mDEG\": 91,\n" + + " \"windSpeedMin80mKTS\": 7,\n" + + " \"windSpeedMin80mKPH\": 13,\n" + + " \"windSpeedMin80mMPH\": 8,\n" + + " \"sky\": 64,\n" + + " \"cloudsCoded\": \"SC\",\n" + + " \"weather\": \"Partly Cloudy\",\n" + + " \"weatherCoded\": [],\n" + + " \"weatherPrimary\": \"Partly Cloudy\",\n" + + " \"weatherPrimaryCoded\": \"::SC\",\n" + + " \"icon\": \"pcloudy.png\",\n" + + " \"visibilityKM\": 24.135,\n" + + " \"visibilityMI\": 15,\n" + + " \"uvi\": 6,\n" + + " \"solradWM2\": 4494,\n" + + " \"solradMinWM2\": 0,\n" + + " \"solradMaxWM2\": 597,\n" + + " \"isDay\": true,\n" + + " \"maxCoverage\": \"\",\n" + + " \"sunrise\": 1665374020,\n" + + " \"sunset\": 1665415821,\n" + + " \"sunriseISO\": \"2022-10-10T05:53:40+02:00\",\n" + + " \"sunsetISO\": \"2022-10-10T17:30:21+02:00\"\n" + + " },\n" + + " {\n" + + " \"timestamp\": 1665464400,\n" + + " \"validTime\": \"2022-10-11T07:00:00+02:00\",\n" + + " \"dateTimeISO\": \"2022-10-11T07:00:00+02:00\",\n" + + " \"maxTempC\": 31,\n" + + " \"maxTempF\": 87,\n" + + " \"minTempC\": 21,\n" + + " \"minTempF\": 70,\n" + + " \"avgTempC\": 26,\n" + + " \"avgTempF\": 78,\n" + + " \"tempC\": null,\n" + + " \"tempF\": null,\n" + + " \"maxFeelslikeC\": 31,\n" + + " \"maxFeelslikeF\": 87,\n" + + " \"minFeelslikeC\": 22,\n" + + " \"minFeelslikeF\": 72,\n" + + " \"avgFeelslikeC\": 26,\n" + + " \"avgFeelslikeF\": 79,\n" + + " \"feelslikeC\": 22,\n" + + " \"feelslikeF\": 72,\n" + + " \"maxDewpointC\": 17,\n" + + " \"maxDewpointF\": 62,\n" + + " \"minDewpointC\": 11,\n" + + " \"minDewpointF\": 51,\n" + + " \"avgDewpointC\": 13,\n" + + " \"avgDewpointF\": 55,\n" + + " \"dewpointC\": 17,\n" + + " \"dewpointF\": 62,\n" + + " \"maxHumidity\": 71,\n" + + " \"minHumidity\": 30,\n" + + " \"humidity\": 71,\n" + + " \"pop\": 0,\n" + + " \"precipMM\": 0,\n" + + " \"precipIN\": 0,\n" + + " \"iceaccum\": null,\n" + + " \"iceaccumMM\": null,\n" + + " \"iceaccumIN\": null,\n" + + " \"snowCM\": 0,\n" + + " \"snowIN\": 0,\n" + + " \"pressureMB\": 1015,\n" + + " \"pressureIN\": 29.98,\n" + + " \"windDir\": \"NNE\",\n" + + " \"windDirDEG\": 13,\n" + + " \"windSpeedKTS\": 8,\n" + + " \"windSpeedKPH\": 15,\n" + + " \"windSpeedMPH\": 9,\n" + + " \"windGustKTS\": 15,\n" + + " \"windGustKPH\": 28,\n" + + " \"windGustMPH\": 17,\n" + + " \"windDirMax\": \"NNE\",\n" + + " \"windDirMaxDEG\": 28,\n" + + " \"windSpeedMaxKTS\": 15,\n" + + " \"windSpeedMaxKPH\": 28,\n" + + " \"windSpeedMaxMPH\": 18,\n" + + " \"windDirMin\": \"NNE\",\n" + + " \"windDirMinDEG\": 14,\n" + + " \"windSpeedMinKTS\": 7,\n" + + " \"windSpeedMinKPH\": 14,\n" + + " \"windSpeedMinMPH\": 8,\n" + + " \"windDir80m\": \"NNE\",\n" + + " \"windDir80mDEG\": 16,\n" + + " \"windSpeed80mKTS\": 10,\n" + + " \"windSpeed80mKPH\": 19,\n" + + " \"windSpeed80mMPH\": 12,\n" + + " \"windGust80mKTS\": 17,\n" + + " \"windGust80mKPH\": 31,\n" + + " \"windGust80mMPH\": 19,\n" + + " \"windDirMax80m\": \"NNE\",\n" + + " \"windDirMax80mDEG\": 28,\n" + + " \"windSpeedMax80mKTS\": 17,\n" + + " \"windSpeedMax80mKPH\": 31,\n" + + " \"windSpeedMax80mMPH\": 19,\n" + + " \"windDirMin80m\": \"NNE\",\n" + + " \"windDirMin80mDEG\": 13,\n" + + " \"windSpeedMin80mKTS\": 9,\n" + + " \"windSpeedMin80mKPH\": 18,\n" + + " \"windSpeedMin80mMPH\": 11,\n" + + " \"sky\": 0,\n" + + " \"cloudsCoded\": \"CL\",\n" + + " \"weather\": \"Sunny\",\n" + + " \"weatherCoded\": [],\n" + + " \"weatherPrimary\": \"Sunny\",\n" + + " \"weatherPrimaryCoded\": \"::CL\",\n" + + " \"icon\": \"sunny.png\",\n" + + " \"visibilityKM\": 24.135,\n" + + " \"visibilityMI\": 15,\n" + + " \"uvi\": null,\n" + + " \"solradWM2\": 5450,\n" + + " \"solradMinWM2\": 0,\n" + + " \"solradMaxWM2\": 758,\n" + + " \"isDay\": true,\n" + + " \"maxCoverage\": \"\",\n" + + " \"sunrise\": 1665460458,\n" + + " \"sunset\": 1665502153,\n" + + " \"sunriseISO\": \"2022-10-11T05:54:18+02:00\",\n" + + " \"sunsetISO\": \"2022-10-11T17:29:13+02:00\"\n" + + " },\n" + + " {\n" + + " \"timestamp\": 1665550800,\n" + + " \"validTime\": \"2022-10-12T07:00:00+02:00\",\n" + + " \"dateTimeISO\": \"2022-10-12T07:00:00+02:00\",\n" + + " \"maxTempC\": 31,\n" + + " \"maxTempF\": 88,\n" + + " \"minTempC\": 21,\n" + + " \"minTempF\": 69,\n" + + " \"avgTempC\": 26,\n" + + " \"avgTempF\": 79,\n" + + " \"tempC\": null,\n" + + " \"tempF\": null,\n" + + " \"maxFeelslikeC\": 31,\n" + + " \"maxFeelslikeF\": 88,\n" + + " \"minFeelslikeC\": 22,\n" + + " \"minFeelslikeF\": 72,\n" + + " \"avgFeelslikeC\": 26,\n" + + " \"avgFeelslikeF\": 80,\n" + + " \"feelslikeC\": 22,\n" + + " \"feelslikeF\": 72,\n" + + " \"maxDewpointC\": 16,\n" + + " \"maxDewpointF\": 60,\n" + + " \"minDewpointC\": 11,\n" + + " \"minDewpointF\": 51,\n" + + " \"avgDewpointC\": 13,\n" + + " \"avgDewpointF\": 55,\n" + + " \"dewpointC\": 16,\n" + + " \"dewpointF\": 60,\n" + + " \"maxHumidity\": 68,\n" + + " \"minHumidity\": 29,\n" + + " \"humidity\": 68,\n" + + " \"pop\": 0,\n" + + " \"precipMM\": 0,\n" + + " \"precipIN\": 0,\n" + + " \"iceaccum\": null,\n" + + " \"iceaccumMM\": null,\n" + + " \"iceaccumIN\": null,\n" + + " \"snowCM\": 0,\n" + + " \"snowIN\": 0,\n" + + " \"pressureMB\": 1014,\n" + + " \"pressureIN\": 29.95,\n" + + " \"windDir\": \"NNE\",\n" + + " \"windDirDEG\": 12,\n" + + " \"windSpeedKTS\": 8,\n" + + " \"windSpeedKPH\": 15,\n" + + " \"windSpeedMPH\": 9,\n" + + " \"windGustKTS\": 15,\n" + + " \"windGustKPH\": 28,\n" + + " \"windGustMPH\": 17,\n" + + " \"windDirMax\": \"E\",\n" + + " \"windDirMaxDEG\": 96,\n" + + " \"windSpeedMaxKTS\": 14,\n" + + " \"windSpeedMaxKPH\": 26,\n" + + " \"windSpeedMaxMPH\": 16,\n" + + " \"windDirMin\": \"NNE\",\n" + + " \"windDirMinDEG\": 12,\n" + + " \"windSpeedMinKTS\": 7,\n" + + " \"windSpeedMinKPH\": 13,\n" + + " \"windSpeedMinMPH\": 8,\n" + + " \"windDir80m\": \"NNE\",\n" + + " \"windDir80mDEG\": 15,\n" + + " \"windSpeed80mKTS\": 10,\n" + + " \"windSpeed80mKPH\": 19,\n" + + " \"windSpeed80mMPH\": 12,\n" + + " \"windGust80mKTS\": 18,\n" + + " \"windGust80mKPH\": 33,\n" + + " \"windGust80mMPH\": 21,\n" + + " \"windDirMax80m\": \"E\",\n" + + " \"windDirMax80mDEG\": 96,\n" + + " \"windSpeedMax80mKTS\": 18,\n" + + " \"windSpeedMax80mKPH\": 33,\n" + + " \"windSpeedMax80mMPH\": 21,\n" + + " \"windDirMin80m\": \"NNE\",\n" + + " \"windDirMin80mDEG\": 15,\n" + + " \"windSpeedMin80mKTS\": 10,\n" + + " \"windSpeedMin80mKPH\": 18,\n" + + " \"windSpeedMin80mMPH\": 11,\n" + + " \"sky\": 27,\n" + + " \"cloudsCoded\": \"FW\",\n" + + " \"weather\": \"Mostly Sunny\",\n" + + " \"weatherCoded\": [],\n" + + " \"weatherPrimary\": \"Mostly Sunny\",\n" + + " \"weatherPrimaryCoded\": \"::FW\",\n" + + " \"icon\": \"fair.png\",\n" + + " \"visibilityKM\": 24.135,\n" + + " \"visibilityMI\": 15,\n" + + " \"uvi\": null,\n" + + " \"solradWM2\": 4740,\n" + + " \"solradMinWM2\": 0,\n" + + " \"solradMaxWM2\": 743,\n" + + " \"isDay\": true,\n" + + " \"maxCoverage\": \"\",\n" + + " \"sunrise\": 1665546895,\n" + + " \"sunset\": 1665588484,\n" + + " \"sunriseISO\": \"2022-10-12T05:54:55+02:00\",\n" + + " \"sunsetISO\": \"2022-10-12T17:28:04+02:00\"\n" + + " }\n" + + " ],\n" + + " \"profile\": {\n" + + " \"tz\": \"Africa/Cairo\",\n" + + " \"elevM\": 23,\n" + + " \"elevFT\": 75\n" + + " }\n" + + " }\n" + + " ]\n" + + "}"; + JSONObject jsonObject = new JSONObject(str); + String actual = XML.toString(jsonObject, null, XMLParserConfiguration.KEEP_STRINGS,2); + String expected = "true\n" + + "\n" + + " \n" + + " 31.25\n" + + " 30.063\n" + + " \n" + + " \n" + + " 23\n" + + " Africa/Cairo\n" + + " 75\n" + + " \n" + + " \n" + + " 2022-10-06T07:00:00+02:00\n" + + " E\n" + + " 95\n" + + " 21\n" + + " 15\n" + + " 10\n" + + " 353\n" + + " N\n" + + " 2022-10-06T05:51:14+02:00\n" + + " null\n" + + " 9\n" + + " null\n" + + " 66\n" + + " 0\n" + + " Mostly Sunny\n" + + " 2022-10-06T17:35:02+02:00\n" + + " 32\n" + + " 77\n" + + " N\n" + + " 89\n" + + " 0\n" + + " 22\n" + + " 25\n" + + " 25\n" + + " Mostly Sunny\n" + + " 41\n" + + " 58\n" + + " 41\n" + + " 22\n" + + " 14\n" + + " 0\n" + + " 22\n" + + " 353\n" + + " 16\n" + + " 8\n" + + " 70\n" + + " 2022-10-06T07:00:00+02:00\n" + + " 10\n" + + " 778\n" + + " 25\n" + + " 15\n" + + " ::FW\n" + + " 1665028274\n" + + " 78\n" + + " N\n" + + " \n" + + " fair.png\n" + + " 21\n" + + " 17\n" + + " FW\n" + + " 70\n" + + " 29\n" + + " 63\n" + + " 12\n" + + " 0\n" + + " 0\n" + + " NNW\n" + + " 13\n" + + " 22\n" + + " 11\n" + + " 32\n" + + " 1015\n" + + " 24.135\n" + + " 1665032400\n" + + " 90\n" + + " null\n" + + " 11\n" + + " 0\n" + + " 1\n" + + " 343\n" + + " 21\n" + + " 2\n" + + " 63\n" + + " 1\n" + + " 26\n" + + " 6\n" + + " NNW\n" + + " 17\n" + + " 29.97\n" + + " 80\n" + + " null\n" + + " true\n" + + " 19\n" + + " 52\n" + + " 5\n" + + " 1665070502\n" + + " 5608\n" + + " 9\n" + + " 25\n" + + " 77\n" + + " 6\n" + + " 40\n" + + " 342\n" + + " null\n" + + " \n" + + " \n" + + " 2022-10-07T07:00:00+02:00\n" + + " NNW\n" + + " 347\n" + + " 19\n" + + " 15\n" + + " 8\n" + + " 325\n" + + " NW\n" + + " 2022-10-07T05:51:50+02:00\n" + + " null\n" + + " 7\n" + + " null\n" + + " 66\n" + + " 0\n" + + " Mostly Sunny\n" + + " 2022-10-07T17:33:51+02:00\n" + + " 29\n" + + " 77\n" + + " NNW\n" + + " 85\n" + + " 0\n" + + " 30\n" + + " 23\n" + + " 23\n" + + " Mostly Sunny\n" + + " 37\n" + + " 54\n" + + " 37\n" + + " 20\n" + + " 12\n" + + " 0\n" + + " 20\n" + + " 325\n" + + " 13\n" + + " 6\n" + + " 67\n" + + " 2022-10-07T07:00:00+02:00\n" + + " 6\n" + + " 742\n" + + " 24\n" + + " 10\n" + + " ::FW\n" + + " 1665114710\n" + + " 76\n" + + " NW\n" + + " \n" + + " fair.png\n" + + " 19\n" + + " 15\n" + + " FW\n" + + " 67\n" + + " 30\n" + + " 60\n" + + " 6\n" + + " 0\n" + + " 0\n" + + " WNW\n" + + " 6\n" + + " 10\n" + + " 347\n" + + " 30\n" + + " 1014\n" + + " 24.135\n" + + " 1665118800\n" + + " 86\n" + + " null\n" + + " 10\n" + + " 0\n" + + " 1\n" + + " 316\n" + + " 16\n" + + " 2\n" + + " 60\n" + + " 1\n" + + " 24\n" + + " 6\n" + + " NW\n" + + " 15\n" + + " 29.95\n" + + " 76\n" + + " null\n" + + " true\n" + + " 19\n" + + " 50\n" + + " 1\n" + + " 1665156831\n" + + " 5486\n" + + " 2\n" + + " 18\n" + + " 77\n" + + " 1\n" + + " 29\n" + + " 298\n" + + " null\n" + + " \n" + + " \n" + + " 2022-10-08T07:00:00+02:00\n" + + " NW\n" + + " 309\n" + + " 19\n" + + " 15\n" + + " 8\n" + + " 21\n" + + " NNE\n" + + " 2022-10-08T05:52:26+02:00\n" + + " null\n" + + " 7\n" + + " null\n" + + " 66\n" + + " 0\n" + + " Partly Cloudy\n" + + " 2022-10-08T17:32:41+02:00\n" + + " 30\n" + + " 76\n" + + " NW\n" + + " 86\n" + + " 0\n" + + " 47\n" + + " 19\n" + + " 19\n" + + " Partly Cloudy\n" + + " 31\n" + + " 56\n" + + " 31\n" + + " 17\n" + + " 13\n" + + " 0\n" + + " 17\n" + + " 21\n" + + " 13\n" + + " 5\n" + + " 67\n" + + " 2022-10-08T07:00:00+02:00\n" + + " 5\n" + + " 682\n" + + " 25\n" + + " 9\n" + + " ::SC\n" + + " 1665201146\n" + + " 76\n" + + " NNE\n" + + " \n" + + " pcloudy.png\n" + + " 19\n" + + " 15\n" + + " SC\n" + + " 67\n" + + " 32\n" + + " 59\n" + + " 5\n" + + " 0\n" + + " 0\n" + + " WNW\n" + + " 5\n" + + " 9\n" + + " 309\n" + + " 30\n" + + " 1014\n" + + " 24.135\n" + + " 1665205200\n" + + " 87\n" + + " null\n" + + " 11\n" + + " 0\n" + + " 1\n" + + " 322\n" + + " 17\n" + + " 2\n" + + " 59\n" + + " 1\n" + + " 25\n" + + " 7\n" + + " NW\n" + + " 15\n" + + " 29.94\n" + + " 76\n" + + " null\n" + + " true\n" + + " 19\n" + + " 52\n" + + " 1\n" + + " 1665243161\n" + + " 4785\n" + + " 2\n" + + " 20\n" + + " 76\n" + + " 1\n" + + " 32\n" + + " 301\n" + + " null\n" + + " \n" + + " \n" + + " 2022-10-09T07:00:00+02:00\n" + + " NW\n" + + " 316\n" + + " 20\n" + + " 15\n" + + " 9\n" + + " 356\n" + + " N\n" + + " 2022-10-09T05:53:03+02:00\n" + + " null\n" + + " 8\n" + + " null\n" + + " 67\n" + + " 0\n" + + " Partly Cloudy\n" + + " 2022-10-09T17:31:31+02:00\n" + + " 30\n" + + " 86\n" + + " NW\n" + + " 86\n" + + " 0\n" + + " 47\n" + + " 23\n" + + " 23\n" + + " Partly Cloudy\n" + + " 36\n" + + " 57\n" + + " 36\n" + + " 20\n" + + " 14\n" + + " 0\n" + + " 20\n" + + " 356\n" + + " 14\n" + + " 5\n" + + " 67\n" + + " 2022-10-09T07:00:00+02:00\n" + + " 6\n" + + " 726\n" + + " 25\n" + + " 9\n" + + " ::SC\n" + + " 1665287583\n" + + " 77\n" + + " N\n" + + " \n" + + " pcloudy.png\n" + + " 20\n" + + " 17\n" + + " SC\n" + + " 67\n" + + " 31\n" + + " 63\n" + + " 5\n" + + " 0\n" + + " 0\n" + + " NNW\n" + + " 6\n" + + " 9\n" + + " 316\n" + + " 31\n" + + " 1016\n" + + " 24.135\n" + + " 1665291600\n" + + " 87\n" + + " null\n" + + " 11\n" + + " 0\n" + + " 2\n" + + " 354\n" + + " 19\n" + + " 4\n" + + " 63\n" + + " 2\n" + + " 25\n" + + " 7\n" + + " N\n" + + " 17\n" + + " 29.99\n" + + " 77\n" + + " null\n" + + " true\n" + + " 19\n" + + " 52\n" + + " 2\n" + + " 1665329491\n" + + " 4768\n" + + " 4\n" + + " 22\n" + + " 86\n" + + " 2\n" + + " 36\n" + + " 343\n" + + " null\n" + + " \n" + + " \n" + + " 2022-10-10T07:00:00+02:00\n" + + " E\n" + + " 91\n" + + " 21\n" + + " 15\n" + + " 9\n" + + " 358\n" + + " N\n" + + " 2022-10-10T05:53:40+02:00\n" + + " null\n" + + " 8\n" + + " null\n" + + " 70\n" + + " 0\n" + + " Partly Cloudy\n" + + " 2022-10-10T17:30:21+02:00\n" + + " 30\n" + + " 75\n" + + " N\n" + + " 86\n" + + " 0\n" + + " 64\n" + + " 22\n" + + " 22\n" + + " Partly Cloudy\n" + + " 36\n" + + " 58\n" + + " 36\n" + + " 19\n" + + " 14\n" + + " 0\n" + + " 19\n" + + " 358\n" + + " 15\n" + + " 7\n" + + " 69\n" + + " 2022-10-10T07:00:00+02:00\n" + + " 8\n" + + " 597\n" + + " 26\n" + + " 13\n" + + " ::SC\n" + + " 1665374020\n" + + " 78\n" + + " N\n" + + " \n" + + " pcloudy.png\n" + + " 21\n" + + " 16\n" + + " SC\n" + + " 69\n" + + " 35\n" + + " 61\n" + + " 7\n" + + " 0\n" + + " 0\n" + + " N\n" + + " 8\n" + + " 13\n" + + " 8\n" + + " 31\n" + + " 1017\n" + + " 24.135\n" + + " 1665378000\n" + + " 87\n" + + " null\n" + + " 13\n" + + " 0\n" + + " 2\n" + + " 10\n" + + " 16\n" + + " 4\n" + + " 61\n" + + " 2\n" + + " 25\n" + + " 6\n" + + " N\n" + + " 16\n" + + " 30.03\n" + + " 78\n" + + " null\n" + + " true\n" + + " 21\n" + + " 55\n" + + " 2\n" + + " 1665415821\n" + + " 4494\n" + + " 4\n" + + " 19\n" + + " 75\n" + + " 2\n" + + " 30\n" + + " 10\n" + + " null\n" + + " \n" + + " \n" + + " 2022-10-11T07:00:00+02:00\n" + + " NNE\n" + + " 13\n" + + " 22\n" + + " 15\n" + + " 18\n" + + " 13\n" + + " NNE\n" + + " 2022-10-11T05:54:18+02:00\n" + + " null\n" + + " 15\n" + + " null\n" + + " 70\n" + + " 0\n" + + " Sunny\n" + + " 2022-10-11T17:29:13+02:00\n" + + " 31\n" + + " 71\n" + + " NNE\n" + + " 87\n" + + " 0\n" + + " 0\n" + + " 19\n" + + " 19\n" + + " Sunny\n" + + " 31\n" + + " 55\n" + + " 31\n" + + " 17\n" + + " 13\n" + + " 0\n" + + " 17\n" + + " 14\n" + + " 28\n" + + " 9\n" + + " 72\n" + + " 2022-10-11T07:00:00+02:00\n" + + " 11\n" + + " 758\n" + + " 26\n" + + " 18\n" + + " ::CL\n" + + " 1665460458\n" + + " 78\n" + + " NNE\n" + + " \n" + + " sunny.png\n" + + " 22\n" + + " 17\n" + + " CL\n" + + " 72\n" + + " 30\n" + + " 62\n" + + " 10\n" + + " 0\n" + + " 0\n" + + " NNE\n" + + " 12\n" + + " 19\n" + + " 16\n" + + " 31\n" + + " 1015\n" + + " 24.135\n" + + " 1665464400\n" + + " 87\n" + + " null\n" + + " 11\n" + + " 0\n" + + " 7\n" + + " 28\n" + + " 15\n" + + " 14\n" + + " 62\n" + + " 8\n" + + " 26\n" + + " null\n" + + " NNE\n" + + " 17\n" + + " 29.98\n" + + " 79\n" + + " null\n" + + " true\n" + + " 21\n" + + " 51\n" + + " 8\n" + + " 1665502153\n" + + " 5450\n" + + " 15\n" + + " 17\n" + + " 71\n" + + " 9\n" + + " 28\n" + + " 28\n" + + " null\n" + + " \n" + + " \n" + + " 2022-10-12T07:00:00+02:00\n" + + " NNE\n" + + " 15\n" + + " 22\n" + + " 15\n" + + " 16\n" + + " 12\n" + + " NNE\n" + + " 2022-10-12T05:54:55+02:00\n" + + " null\n" + + " 14\n" + + " null\n" + + " 69\n" + + " 0\n" + + " Mostly Sunny\n" + + " 2022-10-12T17:28:04+02:00\n" + + " 31\n" + + " 68\n" + + " NNE\n" + + " 88\n" + + " 0\n" + + " 27\n" + + " 21\n" + + " 21\n" + + " Mostly Sunny\n" + + " 33\n" + + " 55\n" + + " 33\n" + + " 18\n" + + " 13\n" + + " 0\n" + + " 18\n" + + " 12\n" + + " 26\n" + + " 10\n" + + " 72\n" + + " 2022-10-12T07:00:00+02:00\n" + + " 11\n" + + " 743\n" + + " 26\n" + + " 18\n" + + " ::FW\n" + + " 1665546895\n" + + " 79\n" + + " NNE\n" + + " \n" + + " fair.png\n" + + " 22\n" + + " 16\n" + + " FW\n" + + " 72\n" + + " 29\n" + + " 60\n" + + " 10\n" + + " 0\n" + + " 0\n" + + " E\n" + + " 12\n" + + " 19\n" + + " 15\n" + + " 31\n" + + " 1014\n" + + " 24.135\n" + + " 1665550800\n" + + " 88\n" + + " null\n" + + " 11\n" + + " 0\n" + + " 7\n" + + " 96\n" + + " 15\n" + + " 13\n" + + " 60\n" + + " 8\n" + + " 26\n" + + " null\n" + + " E\n" + + " 16\n" + + " 29.95\n" + + " 80\n" + + " null\n" + + " true\n" + + " 21\n" + + " 51\n" + + " 8\n" + + " 1665588484\n" + + " 4740\n" + + " 15\n" + + " 17\n" + + " 68\n" + + " 9\n" + + " 28\n" + + " 96\n" + + " null\n" + + " \n" + + " day\n" + + " \n" + + " eg\n" + + " cairo\n" + + " qh\n" + + " \n" + + "\n" + + "null\n"; + assertEquals(actual, expected); + } } From 4a8ff28fd8af4ad7f68c12b8ed6029cbdd14dfd3 Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 7 Oct 2022 10:35:06 +0100 Subject: [PATCH 12/48] Reduced Test code length by using resources --- src/test/java/org/json/junit/XMLTest.java | 1437 +-------------------- src/test/resources/Issue593.json | 189 +++ src/test/resources/Issue593.xml | 169 +++ 3 files changed, 390 insertions(+), 1405 deletions(-) create mode 100644 src/test/resources/Issue593.json create mode 100644 src/test/resources/Issue593.xml diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 8d25b7e..4c46cf1 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -18,16 +18,11 @@ import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.json.XML; -import org.json.XMLParserConfiguration; -import org.json.XMLXsiTypeConverter; +import org.json.*; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -1222,1404 +1217,36 @@ public void testIndentSimpleJsonArray(){ @Test public void testIndentComplicatedJsonObjectWithArrayAndWithConfig(){ - String str = "{\n" + - " \"success\": true,\n" + - " \"error\": null,\n" + - " \"response\": [\n" + - " {\n" + - " \"loc\": {\n" + - " \"long\": 31.25,\n" + - " \"lat\": 30.063\n" + - " },\n" + - " \"interval\": \"day\",\n" + - " \"place\": {\n" + - " \"name\": \"cairo\",\n" + - " \"state\": \"qh\",\n" + - " \"country\": \"eg\"\n" + - " },\n" + - " \"periods\": [\n" + - " {\n" + - " \"timestamp\": 1665032400,\n" + - " \"validTime\": \"2022-10-06T07:00:00+02:00\",\n" + - " \"dateTimeISO\": \"2022-10-06T07:00:00+02:00\",\n" + - " \"maxTempC\": 32,\n" + - " \"maxTempF\": 90,\n" + - " \"minTempC\": 19,\n" + - " \"minTempF\": 66,\n" + - " \"avgTempC\": 25,\n" + - " \"avgTempF\": 78,\n" + - " \"tempC\": null,\n" + - " \"tempF\": null,\n" + - " \"maxFeelslikeC\": 32,\n" + - " \"maxFeelslikeF\": 89,\n" + - " \"minFeelslikeC\": 21,\n" + - " \"minFeelslikeF\": 70,\n" + - " \"avgFeelslikeC\": 26,\n" + - " \"avgFeelslikeF\": 80,\n" + - " \"feelslikeC\": 21,\n" + - " \"feelslikeF\": 70,\n" + - " \"maxDewpointC\": 17,\n" + - " \"maxDewpointF\": 63,\n" + - " \"minDewpointC\": 11,\n" + - " \"minDewpointF\": 52,\n" + - " \"avgDewpointC\": 14,\n" + - " \"avgDewpointF\": 58,\n" + - " \"dewpointC\": 17,\n" + - " \"dewpointF\": 63,\n" + - " \"maxHumidity\": 77,\n" + - " \"minHumidity\": 29,\n" + - " \"humidity\": 77,\n" + - " \"pop\": 0,\n" + - " \"precipMM\": 0,\n" + - " \"precipIN\": 0,\n" + - " \"iceaccum\": null,\n" + - " \"iceaccumMM\": null,\n" + - " \"iceaccumIN\": null,\n" + - " \"snowCM\": 0,\n" + - " \"snowIN\": 0,\n" + - " \"pressureMB\": 1015,\n" + - " \"pressureIN\": 29.97,\n" + - " \"windDir\": \"N\",\n" + - " \"windDirDEG\": 353,\n" + - " \"windSpeedKTS\": 5,\n" + - " \"windSpeedKPH\": 9,\n" + - " \"windSpeedMPH\": 6,\n" + - " \"windGustKTS\": 21,\n" + - " \"windGustKPH\": 40,\n" + - " \"windGustMPH\": 25,\n" + - " \"windDirMax\": \"NNW\",\n" + - " \"windDirMaxDEG\": 342,\n" + - " \"windSpeedMaxKTS\": 9,\n" + - " \"windSpeedMaxKPH\": 16,\n" + - " \"windSpeedMaxMPH\": 10,\n" + - " \"windDirMin\": \"N\",\n" + - " \"windDirMinDEG\": 353,\n" + - " \"windSpeedMinKTS\": 1,\n" + - " \"windSpeedMinKPH\": 2,\n" + - " \"windSpeedMinMPH\": 1,\n" + - " \"windDir80m\": \"N\",\n" + - " \"windDir80mDEG\": 11,\n" + - " \"windSpeed80mKTS\": 12,\n" + - " \"windSpeed80mKPH\": 22,\n" + - " \"windSpeed80mMPH\": 13,\n" + - " \"windGust80mKTS\": 22,\n" + - " \"windGust80mKPH\": 41,\n" + - " \"windGust80mMPH\": 25,\n" + - " \"windDirMax80m\": \"NNW\",\n" + - " \"windDirMax80mDEG\": 343,\n" + - " \"windSpeedMax80mKTS\": 22,\n" + - " \"windSpeedMax80mKPH\": 41,\n" + - " \"windSpeedMax80mMPH\": 25,\n" + - " \"windDirMin80m\": \"E\",\n" + - " \"windDirMin80mDEG\": 95,\n" + - " \"windSpeedMin80mKTS\": 8,\n" + - " \"windSpeedMin80mKPH\": 15,\n" + - " \"windSpeedMin80mMPH\": 10,\n" + - " \"sky\": 22,\n" + - " \"cloudsCoded\": \"FW\",\n" + - " \"weather\": \"Mostly Sunny\",\n" + - " \"weatherCoded\": [],\n" + - " \"weatherPrimary\": \"Mostly Sunny\",\n" + - " \"weatherPrimaryCoded\": \"::FW\",\n" + - " \"icon\": \"fair.png\",\n" + - " \"visibilityKM\": 24.135,\n" + - " \"visibilityMI\": 15,\n" + - " \"uvi\": 6,\n" + - " \"solradWM2\": 5608,\n" + - " \"solradMinWM2\": 0,\n" + - " \"solradMaxWM2\": 778,\n" + - " \"isDay\": true,\n" + - " \"maxCoverage\": \"\",\n" + - " \"sunrise\": 1665028274,\n" + - " \"sunset\": 1665070502,\n" + - " \"sunriseISO\": \"2022-10-06T05:51:14+02:00\",\n" + - " \"sunsetISO\": \"2022-10-06T17:35:02+02:00\"\n" + - " },\n" + - " {\n" + - " \"timestamp\": 1665118800,\n" + - " \"validTime\": \"2022-10-07T07:00:00+02:00\",\n" + - " \"dateTimeISO\": \"2022-10-07T07:00:00+02:00\",\n" + - " \"maxTempC\": 30,\n" + - " \"maxTempF\": 86,\n" + - " \"minTempC\": 19,\n" + - " \"minTempF\": 66,\n" + - " \"avgTempC\": 24,\n" + - " \"avgTempF\": 76,\n" + - " \"tempC\": null,\n" + - " \"tempF\": null,\n" + - " \"maxFeelslikeC\": 29,\n" + - " \"maxFeelslikeF\": 85,\n" + - " \"minFeelslikeC\": 19,\n" + - " \"minFeelslikeF\": 67,\n" + - " \"avgFeelslikeC\": 24,\n" + - " \"avgFeelslikeF\": 76,\n" + - " \"feelslikeC\": 19,\n" + - " \"feelslikeF\": 67,\n" + - " \"maxDewpointC\": 15,\n" + - " \"maxDewpointF\": 60,\n" + - " \"minDewpointC\": 10,\n" + - " \"minDewpointF\": 50,\n" + - " \"avgDewpointC\": 12,\n" + - " \"avgDewpointF\": 54,\n" + - " \"dewpointC\": 15,\n" + - " \"dewpointF\": 60,\n" + - " \"maxHumidity\": 77,\n" + - " \"minHumidity\": 30,\n" + - " \"humidity\": 77,\n" + - " \"pop\": 0,\n" + - " \"precipMM\": 0,\n" + - " \"precipIN\": 0,\n" + - " \"iceaccum\": null,\n" + - " \"iceaccumMM\": null,\n" + - " \"iceaccumIN\": null,\n" + - " \"snowCM\": 0,\n" + - " \"snowIN\": 0,\n" + - " \"pressureMB\": 1014,\n" + - " \"pressureIN\": 29.95,\n" + - " \"windDir\": \"NW\",\n" + - " \"windDirDEG\": 325,\n" + - " \"windSpeedKTS\": 1,\n" + - " \"windSpeedKPH\": 2,\n" + - " \"windSpeedMPH\": 1,\n" + - " \"windGustKTS\": 16,\n" + - " \"windGustKPH\": 29,\n" + - " \"windGustMPH\": 18,\n" + - " \"windDirMax\": \"WNW\",\n" + - " \"windDirMaxDEG\": 298,\n" + - " \"windSpeedMaxKTS\": 7,\n" + - " \"windSpeedMaxKPH\": 13,\n" + - " \"windSpeedMaxMPH\": 8,\n" + - " \"windDirMin\": \"NW\",\n" + - " \"windDirMinDEG\": 325,\n" + - " \"windSpeedMinKTS\": 1,\n" + - " \"windSpeedMinKPH\": 2,\n" + - " \"windSpeedMinMPH\": 1,\n" + - " \"windDir80m\": \"NNW\",\n" + - " \"windDir80mDEG\": 347,\n" + - " \"windSpeed80mKTS\": 6,\n" + - " \"windSpeed80mKPH\": 10,\n" + - " \"windSpeed80mMPH\": 6,\n" + - " \"windGust80mKTS\": 20,\n" + - " \"windGust80mKPH\": 37,\n" + - " \"windGust80mMPH\": 23,\n" + - " \"windDirMax80m\": \"NW\",\n" + - " \"windDirMax80mDEG\": 316,\n" + - " \"windSpeedMax80mKTS\": 20,\n" + - " \"windSpeedMax80mKPH\": 37,\n" + - " \"windSpeedMax80mMPH\": 23,\n" + - " \"windDirMin80m\": \"NNW\",\n" + - " \"windDirMin80mDEG\": 347,\n" + - " \"windSpeedMin80mKTS\": 6,\n" + - " \"windSpeedMin80mKPH\": 10,\n" + - " \"windSpeedMin80mMPH\": 6,\n" + - " \"sky\": 30,\n" + - " \"cloudsCoded\": \"FW\",\n" + - " \"weather\": \"Mostly Sunny\",\n" + - " \"weatherCoded\": [],\n" + - " \"weatherPrimary\": \"Mostly Sunny\",\n" + - " \"weatherPrimaryCoded\": \"::FW\",\n" + - " \"icon\": \"fair.png\",\n" + - " \"visibilityKM\": 24.135,\n" + - " \"visibilityMI\": 15,\n" + - " \"uvi\": 6,\n" + - " \"solradWM2\": 5486,\n" + - " \"solradMinWM2\": 0,\n" + - " \"solradMaxWM2\": 742,\n" + - " \"isDay\": true,\n" + - " \"maxCoverage\": \"\",\n" + - " \"sunrise\": 1665114710,\n" + - " \"sunset\": 1665156831,\n" + - " \"sunriseISO\": \"2022-10-07T05:51:50+02:00\",\n" + - " \"sunsetISO\": \"2022-10-07T17:33:51+02:00\"\n" + - " },\n" + - " {\n" + - " \"timestamp\": 1665205200,\n" + - " \"validTime\": \"2022-10-08T07:00:00+02:00\",\n" + - " \"dateTimeISO\": \"2022-10-08T07:00:00+02:00\",\n" + - " \"maxTempC\": 30,\n" + - " \"maxTempF\": 87,\n" + - " \"minTempC\": 19,\n" + - " \"minTempF\": 66,\n" + - " \"avgTempC\": 25,\n" + - " \"avgTempF\": 76,\n" + - " \"tempC\": null,\n" + - " \"tempF\": null,\n" + - " \"maxFeelslikeC\": 30,\n" + - " \"maxFeelslikeF\": 86,\n" + - " \"minFeelslikeC\": 19,\n" + - " \"minFeelslikeF\": 67,\n" + - " \"avgFeelslikeC\": 25,\n" + - " \"avgFeelslikeF\": 76,\n" + - " \"feelslikeC\": 19,\n" + - " \"feelslikeF\": 67,\n" + - " \"maxDewpointC\": 15,\n" + - " \"maxDewpointF\": 59,\n" + - " \"minDewpointC\": 11,\n" + - " \"minDewpointF\": 52,\n" + - " \"avgDewpointC\": 13,\n" + - " \"avgDewpointF\": 56,\n" + - " \"dewpointC\": 15,\n" + - " \"dewpointF\": 59,\n" + - " \"maxHumidity\": 76,\n" + - " \"minHumidity\": 32,\n" + - " \"humidity\": 76,\n" + - " \"pop\": 0,\n" + - " \"precipMM\": 0,\n" + - " \"precipIN\": 0,\n" + - " \"iceaccum\": null,\n" + - " \"iceaccumMM\": null,\n" + - " \"iceaccumIN\": null,\n" + - " \"snowCM\": 0,\n" + - " \"snowIN\": 0,\n" + - " \"pressureMB\": 1014,\n" + - " \"pressureIN\": 29.94,\n" + - " \"windDir\": \"NNE\",\n" + - " \"windDirDEG\": 21,\n" + - " \"windSpeedKTS\": 1,\n" + - " \"windSpeedKPH\": 2,\n" + - " \"windSpeedMPH\": 1,\n" + - " \"windGustKTS\": 17,\n" + - " \"windGustKPH\": 32,\n" + - " \"windGustMPH\": 20,\n" + - " \"windDirMax\": \"WNW\",\n" + - " \"windDirMaxDEG\": 301,\n" + - " \"windSpeedMaxKTS\": 7,\n" + - " \"windSpeedMaxKPH\": 13,\n" + - " \"windSpeedMaxMPH\": 8,\n" + - " \"windDirMin\": \"NNE\",\n" + - " \"windDirMinDEG\": 21,\n" + - " \"windSpeedMinKTS\": 1,\n" + - " \"windSpeedMinKPH\": 2,\n" + - " \"windSpeedMinMPH\": 1,\n" + - " \"windDir80m\": \"NW\",\n" + - " \"windDir80mDEG\": 309,\n" + - " \"windSpeed80mKTS\": 5,\n" + - " \"windSpeed80mKPH\": 9,\n" + - " \"windSpeed80mMPH\": 5,\n" + - " \"windGust80mKTS\": 17,\n" + - " \"windGust80mKPH\": 31,\n" + - " \"windGust80mMPH\": 19,\n" + - " \"windDirMax80m\": \"NW\",\n" + - " \"windDirMax80mDEG\": 322,\n" + - " \"windSpeedMax80mKTS\": 17,\n" + - " \"windSpeedMax80mKPH\": 31,\n" + - " \"windSpeedMax80mMPH\": 19,\n" + - " \"windDirMin80m\": \"NW\",\n" + - " \"windDirMin80mDEG\": 309,\n" + - " \"windSpeedMin80mKTS\": 5,\n" + - " \"windSpeedMin80mKPH\": 9,\n" + - " \"windSpeedMin80mMPH\": 5,\n" + - " \"sky\": 47,\n" + - " \"cloudsCoded\": \"SC\",\n" + - " \"weather\": \"Partly Cloudy\",\n" + - " \"weatherCoded\": [],\n" + - " \"weatherPrimary\": \"Partly Cloudy\",\n" + - " \"weatherPrimaryCoded\": \"::SC\",\n" + - " \"icon\": \"pcloudy.png\",\n" + - " \"visibilityKM\": 24.135,\n" + - " \"visibilityMI\": 15,\n" + - " \"uvi\": 7,\n" + - " \"solradWM2\": 4785,\n" + - " \"solradMinWM2\": 0,\n" + - " \"solradMaxWM2\": 682,\n" + - " \"isDay\": true,\n" + - " \"maxCoverage\": \"\",\n" + - " \"sunrise\": 1665201146,\n" + - " \"sunset\": 1665243161,\n" + - " \"sunriseISO\": \"2022-10-08T05:52:26+02:00\",\n" + - " \"sunsetISO\": \"2022-10-08T17:32:41+02:00\"\n" + - " },\n" + - " {\n" + - " \"timestamp\": 1665291600,\n" + - " \"validTime\": \"2022-10-09T07:00:00+02:00\",\n" + - " \"dateTimeISO\": \"2022-10-09T07:00:00+02:00\",\n" + - " \"maxTempC\": 31,\n" + - " \"maxTempF\": 87,\n" + - " \"minTempC\": 19,\n" + - " \"minTempF\": 67,\n" + - " \"avgTempC\": 25,\n" + - " \"avgTempF\": 77,\n" + - " \"tempC\": null,\n" + - " \"tempF\": null,\n" + - " \"maxFeelslikeC\": 30,\n" + - " \"maxFeelslikeF\": 86,\n" + - " \"minFeelslikeC\": 20,\n" + - " \"minFeelslikeF\": 67,\n" + - " \"avgFeelslikeC\": 25,\n" + - " \"avgFeelslikeF\": 77,\n" + - " \"feelslikeC\": 20,\n" + - " \"feelslikeF\": 67,\n" + - " \"maxDewpointC\": 17,\n" + - " \"maxDewpointF\": 63,\n" + - " \"minDewpointC\": 11,\n" + - " \"minDewpointF\": 52,\n" + - " \"avgDewpointC\": 14,\n" + - " \"avgDewpointF\": 57,\n" + - " \"dewpointC\": 17,\n" + - " \"dewpointF\": 63,\n" + - " \"maxHumidity\": 86,\n" + - " \"minHumidity\": 31,\n" + - " \"humidity\": 86,\n" + - " \"pop\": 0,\n" + - " \"precipMM\": 0,\n" + - " \"precipIN\": 0,\n" + - " \"iceaccum\": null,\n" + - " \"iceaccumMM\": null,\n" + - " \"iceaccumIN\": null,\n" + - " \"snowCM\": 0,\n" + - " \"snowIN\": 0,\n" + - " \"pressureMB\": 1016,\n" + - " \"pressureIN\": 29.99,\n" + - " \"windDir\": \"N\",\n" + - " \"windDirDEG\": 356,\n" + - " \"windSpeedKTS\": 2,\n" + - " \"windSpeedKPH\": 4,\n" + - " \"windSpeedMPH\": 2,\n" + - " \"windGustKTS\": 19,\n" + - " \"windGustKPH\": 36,\n" + - " \"windGustMPH\": 22,\n" + - " \"windDirMax\": \"NNW\",\n" + - " \"windDirMaxDEG\": 343,\n" + - " \"windSpeedMaxKTS\": 8,\n" + - " \"windSpeedMaxKPH\": 14,\n" + - " \"windSpeedMaxMPH\": 9,\n" + - " \"windDirMin\": \"N\",\n" + - " \"windDirMinDEG\": 356,\n" + - " \"windSpeedMinKTS\": 2,\n" + - " \"windSpeedMinKPH\": 4,\n" + - " \"windSpeedMinMPH\": 2,\n" + - " \"windDir80m\": \"NW\",\n" + - " \"windDir80mDEG\": 316,\n" + - " \"windSpeed80mKTS\": 5,\n" + - " \"windSpeed80mKPH\": 9,\n" + - " \"windSpeed80mMPH\": 6,\n" + - " \"windGust80mKTS\": 20,\n" + - " \"windGust80mKPH\": 36,\n" + - " \"windGust80mMPH\": 23,\n" + - " \"windDirMax80m\": \"N\",\n" + - " \"windDirMax80mDEG\": 354,\n" + - " \"windSpeedMax80mKTS\": 20,\n" + - " \"windSpeedMax80mKPH\": 36,\n" + - " \"windSpeedMax80mMPH\": 23,\n" + - " \"windDirMin80m\": \"NW\",\n" + - " \"windDirMin80mDEG\": 316,\n" + - " \"windSpeedMin80mKTS\": 5,\n" + - " \"windSpeedMin80mKPH\": 9,\n" + - " \"windSpeedMin80mMPH\": 6,\n" + - " \"sky\": 47,\n" + - " \"cloudsCoded\": \"SC\",\n" + - " \"weather\": \"Partly Cloudy\",\n" + - " \"weatherCoded\": [],\n" + - " \"weatherPrimary\": \"Partly Cloudy\",\n" + - " \"weatherPrimaryCoded\": \"::SC\",\n" + - " \"icon\": \"pcloudy.png\",\n" + - " \"visibilityKM\": 24.135,\n" + - " \"visibilityMI\": 15,\n" + - " \"uvi\": 7,\n" + - " \"solradWM2\": 4768,\n" + - " \"solradMinWM2\": 0,\n" + - " \"solradMaxWM2\": 726,\n" + - " \"isDay\": true,\n" + - " \"maxCoverage\": \"\",\n" + - " \"sunrise\": 1665287583,\n" + - " \"sunset\": 1665329491,\n" + - " \"sunriseISO\": \"2022-10-09T05:53:03+02:00\",\n" + - " \"sunsetISO\": \"2022-10-09T17:31:31+02:00\"\n" + - " },\n" + - " {\n" + - " \"timestamp\": 1665378000,\n" + - " \"validTime\": \"2022-10-10T07:00:00+02:00\",\n" + - " \"dateTimeISO\": \"2022-10-10T07:00:00+02:00\",\n" + - " \"maxTempC\": 31,\n" + - " \"maxTempF\": 87,\n" + - " \"minTempC\": 21,\n" + - " \"minTempF\": 70,\n" + - " \"avgTempC\": 26,\n" + - " \"avgTempF\": 78,\n" + - " \"tempC\": null,\n" + - " \"tempF\": null,\n" + - " \"maxFeelslikeC\": 30,\n" + - " \"maxFeelslikeF\": 86,\n" + - " \"minFeelslikeC\": 21,\n" + - " \"minFeelslikeF\": 69,\n" + - " \"avgFeelslikeC\": 25,\n" + - " \"avgFeelslikeF\": 78,\n" + - " \"feelslikeC\": 21,\n" + - " \"feelslikeF\": 69,\n" + - " \"maxDewpointC\": 16,\n" + - " \"maxDewpointF\": 61,\n" + - " \"minDewpointC\": 13,\n" + - " \"minDewpointF\": 55,\n" + - " \"avgDewpointC\": 14,\n" + - " \"avgDewpointF\": 58,\n" + - " \"dewpointC\": 16,\n" + - " \"dewpointF\": 61,\n" + - " \"maxHumidity\": 75,\n" + - " \"minHumidity\": 35,\n" + - " \"humidity\": 75,\n" + - " \"pop\": 0,\n" + - " \"precipMM\": 0,\n" + - " \"precipIN\": 0,\n" + - " \"iceaccum\": null,\n" + - " \"iceaccumMM\": null,\n" + - " \"iceaccumIN\": null,\n" + - " \"snowCM\": 0,\n" + - " \"snowIN\": 0,\n" + - " \"pressureMB\": 1017,\n" + - " \"pressureIN\": 30.03,\n" + - " \"windDir\": \"N\",\n" + - " \"windDirDEG\": 358,\n" + - " \"windSpeedKTS\": 2,\n" + - " \"windSpeedKPH\": 4,\n" + - " \"windSpeedMPH\": 2,\n" + - " \"windGustKTS\": 16,\n" + - " \"windGustKPH\": 30,\n" + - " \"windGustMPH\": 19,\n" + - " \"windDirMax\": \"N\",\n" + - " \"windDirMaxDEG\": 10,\n" + - " \"windSpeedMaxKTS\": 8,\n" + - " \"windSpeedMaxKPH\": 15,\n" + - " \"windSpeedMaxMPH\": 9,\n" + - " \"windDirMin\": \"N\",\n" + - " \"windDirMinDEG\": 358,\n" + - " \"windSpeedMinKTS\": 2,\n" + - " \"windSpeedMinKPH\": 4,\n" + - " \"windSpeedMinMPH\": 2,\n" + - " \"windDir80m\": \"N\",\n" + - " \"windDir80mDEG\": 8,\n" + - " \"windSpeed80mKTS\": 7,\n" + - " \"windSpeed80mKPH\": 13,\n" + - " \"windSpeed80mMPH\": 8,\n" + - " \"windGust80mKTS\": 19,\n" + - " \"windGust80mKPH\": 36,\n" + - " \"windGust80mMPH\": 22,\n" + - " \"windDirMax80m\": \"N\",\n" + - " \"windDirMax80mDEG\": 10,\n" + - " \"windSpeedMax80mKTS\": 19,\n" + - " \"windSpeedMax80mKPH\": 36,\n" + - " \"windSpeedMax80mMPH\": 22,\n" + - " \"windDirMin80m\": \"E\",\n" + - " \"windDirMin80mDEG\": 91,\n" + - " \"windSpeedMin80mKTS\": 7,\n" + - " \"windSpeedMin80mKPH\": 13,\n" + - " \"windSpeedMin80mMPH\": 8,\n" + - " \"sky\": 64,\n" + - " \"cloudsCoded\": \"SC\",\n" + - " \"weather\": \"Partly Cloudy\",\n" + - " \"weatherCoded\": [],\n" + - " \"weatherPrimary\": \"Partly Cloudy\",\n" + - " \"weatherPrimaryCoded\": \"::SC\",\n" + - " \"icon\": \"pcloudy.png\",\n" + - " \"visibilityKM\": 24.135,\n" + - " \"visibilityMI\": 15,\n" + - " \"uvi\": 6,\n" + - " \"solradWM2\": 4494,\n" + - " \"solradMinWM2\": 0,\n" + - " \"solradMaxWM2\": 597,\n" + - " \"isDay\": true,\n" + - " \"maxCoverage\": \"\",\n" + - " \"sunrise\": 1665374020,\n" + - " \"sunset\": 1665415821,\n" + - " \"sunriseISO\": \"2022-10-10T05:53:40+02:00\",\n" + - " \"sunsetISO\": \"2022-10-10T17:30:21+02:00\"\n" + - " },\n" + - " {\n" + - " \"timestamp\": 1665464400,\n" + - " \"validTime\": \"2022-10-11T07:00:00+02:00\",\n" + - " \"dateTimeISO\": \"2022-10-11T07:00:00+02:00\",\n" + - " \"maxTempC\": 31,\n" + - " \"maxTempF\": 87,\n" + - " \"minTempC\": 21,\n" + - " \"minTempF\": 70,\n" + - " \"avgTempC\": 26,\n" + - " \"avgTempF\": 78,\n" + - " \"tempC\": null,\n" + - " \"tempF\": null,\n" + - " \"maxFeelslikeC\": 31,\n" + - " \"maxFeelslikeF\": 87,\n" + - " \"minFeelslikeC\": 22,\n" + - " \"minFeelslikeF\": 72,\n" + - " \"avgFeelslikeC\": 26,\n" + - " \"avgFeelslikeF\": 79,\n" + - " \"feelslikeC\": 22,\n" + - " \"feelslikeF\": 72,\n" + - " \"maxDewpointC\": 17,\n" + - " \"maxDewpointF\": 62,\n" + - " \"minDewpointC\": 11,\n" + - " \"minDewpointF\": 51,\n" + - " \"avgDewpointC\": 13,\n" + - " \"avgDewpointF\": 55,\n" + - " \"dewpointC\": 17,\n" + - " \"dewpointF\": 62,\n" + - " \"maxHumidity\": 71,\n" + - " \"minHumidity\": 30,\n" + - " \"humidity\": 71,\n" + - " \"pop\": 0,\n" + - " \"precipMM\": 0,\n" + - " \"precipIN\": 0,\n" + - " \"iceaccum\": null,\n" + - " \"iceaccumMM\": null,\n" + - " \"iceaccumIN\": null,\n" + - " \"snowCM\": 0,\n" + - " \"snowIN\": 0,\n" + - " \"pressureMB\": 1015,\n" + - " \"pressureIN\": 29.98,\n" + - " \"windDir\": \"NNE\",\n" + - " \"windDirDEG\": 13,\n" + - " \"windSpeedKTS\": 8,\n" + - " \"windSpeedKPH\": 15,\n" + - " \"windSpeedMPH\": 9,\n" + - " \"windGustKTS\": 15,\n" + - " \"windGustKPH\": 28,\n" + - " \"windGustMPH\": 17,\n" + - " \"windDirMax\": \"NNE\",\n" + - " \"windDirMaxDEG\": 28,\n" + - " \"windSpeedMaxKTS\": 15,\n" + - " \"windSpeedMaxKPH\": 28,\n" + - " \"windSpeedMaxMPH\": 18,\n" + - " \"windDirMin\": \"NNE\",\n" + - " \"windDirMinDEG\": 14,\n" + - " \"windSpeedMinKTS\": 7,\n" + - " \"windSpeedMinKPH\": 14,\n" + - " \"windSpeedMinMPH\": 8,\n" + - " \"windDir80m\": \"NNE\",\n" + - " \"windDir80mDEG\": 16,\n" + - " \"windSpeed80mKTS\": 10,\n" + - " \"windSpeed80mKPH\": 19,\n" + - " \"windSpeed80mMPH\": 12,\n" + - " \"windGust80mKTS\": 17,\n" + - " \"windGust80mKPH\": 31,\n" + - " \"windGust80mMPH\": 19,\n" + - " \"windDirMax80m\": \"NNE\",\n" + - " \"windDirMax80mDEG\": 28,\n" + - " \"windSpeedMax80mKTS\": 17,\n" + - " \"windSpeedMax80mKPH\": 31,\n" + - " \"windSpeedMax80mMPH\": 19,\n" + - " \"windDirMin80m\": \"NNE\",\n" + - " \"windDirMin80mDEG\": 13,\n" + - " \"windSpeedMin80mKTS\": 9,\n" + - " \"windSpeedMin80mKPH\": 18,\n" + - " \"windSpeedMin80mMPH\": 11,\n" + - " \"sky\": 0,\n" + - " \"cloudsCoded\": \"CL\",\n" + - " \"weather\": \"Sunny\",\n" + - " \"weatherCoded\": [],\n" + - " \"weatherPrimary\": \"Sunny\",\n" + - " \"weatherPrimaryCoded\": \"::CL\",\n" + - " \"icon\": \"sunny.png\",\n" + - " \"visibilityKM\": 24.135,\n" + - " \"visibilityMI\": 15,\n" + - " \"uvi\": null,\n" + - " \"solradWM2\": 5450,\n" + - " \"solradMinWM2\": 0,\n" + - " \"solradMaxWM2\": 758,\n" + - " \"isDay\": true,\n" + - " \"maxCoverage\": \"\",\n" + - " \"sunrise\": 1665460458,\n" + - " \"sunset\": 1665502153,\n" + - " \"sunriseISO\": \"2022-10-11T05:54:18+02:00\",\n" + - " \"sunsetISO\": \"2022-10-11T17:29:13+02:00\"\n" + - " },\n" + - " {\n" + - " \"timestamp\": 1665550800,\n" + - " \"validTime\": \"2022-10-12T07:00:00+02:00\",\n" + - " \"dateTimeISO\": \"2022-10-12T07:00:00+02:00\",\n" + - " \"maxTempC\": 31,\n" + - " \"maxTempF\": 88,\n" + - " \"minTempC\": 21,\n" + - " \"minTempF\": 69,\n" + - " \"avgTempC\": 26,\n" + - " \"avgTempF\": 79,\n" + - " \"tempC\": null,\n" + - " \"tempF\": null,\n" + - " \"maxFeelslikeC\": 31,\n" + - " \"maxFeelslikeF\": 88,\n" + - " \"minFeelslikeC\": 22,\n" + - " \"minFeelslikeF\": 72,\n" + - " \"avgFeelslikeC\": 26,\n" + - " \"avgFeelslikeF\": 80,\n" + - " \"feelslikeC\": 22,\n" + - " \"feelslikeF\": 72,\n" + - " \"maxDewpointC\": 16,\n" + - " \"maxDewpointF\": 60,\n" + - " \"minDewpointC\": 11,\n" + - " \"minDewpointF\": 51,\n" + - " \"avgDewpointC\": 13,\n" + - " \"avgDewpointF\": 55,\n" + - " \"dewpointC\": 16,\n" + - " \"dewpointF\": 60,\n" + - " \"maxHumidity\": 68,\n" + - " \"minHumidity\": 29,\n" + - " \"humidity\": 68,\n" + - " \"pop\": 0,\n" + - " \"precipMM\": 0,\n" + - " \"precipIN\": 0,\n" + - " \"iceaccum\": null,\n" + - " \"iceaccumMM\": null,\n" + - " \"iceaccumIN\": null,\n" + - " \"snowCM\": 0,\n" + - " \"snowIN\": 0,\n" + - " \"pressureMB\": 1014,\n" + - " \"pressureIN\": 29.95,\n" + - " \"windDir\": \"NNE\",\n" + - " \"windDirDEG\": 12,\n" + - " \"windSpeedKTS\": 8,\n" + - " \"windSpeedKPH\": 15,\n" + - " \"windSpeedMPH\": 9,\n" + - " \"windGustKTS\": 15,\n" + - " \"windGustKPH\": 28,\n" + - " \"windGustMPH\": 17,\n" + - " \"windDirMax\": \"E\",\n" + - " \"windDirMaxDEG\": 96,\n" + - " \"windSpeedMaxKTS\": 14,\n" + - " \"windSpeedMaxKPH\": 26,\n" + - " \"windSpeedMaxMPH\": 16,\n" + - " \"windDirMin\": \"NNE\",\n" + - " \"windDirMinDEG\": 12,\n" + - " \"windSpeedMinKTS\": 7,\n" + - " \"windSpeedMinKPH\": 13,\n" + - " \"windSpeedMinMPH\": 8,\n" + - " \"windDir80m\": \"NNE\",\n" + - " \"windDir80mDEG\": 15,\n" + - " \"windSpeed80mKTS\": 10,\n" + - " \"windSpeed80mKPH\": 19,\n" + - " \"windSpeed80mMPH\": 12,\n" + - " \"windGust80mKTS\": 18,\n" + - " \"windGust80mKPH\": 33,\n" + - " \"windGust80mMPH\": 21,\n" + - " \"windDirMax80m\": \"E\",\n" + - " \"windDirMax80mDEG\": 96,\n" + - " \"windSpeedMax80mKTS\": 18,\n" + - " \"windSpeedMax80mKPH\": 33,\n" + - " \"windSpeedMax80mMPH\": 21,\n" + - " \"windDirMin80m\": \"NNE\",\n" + - " \"windDirMin80mDEG\": 15,\n" + - " \"windSpeedMin80mKTS\": 10,\n" + - " \"windSpeedMin80mKPH\": 18,\n" + - " \"windSpeedMin80mMPH\": 11,\n" + - " \"sky\": 27,\n" + - " \"cloudsCoded\": \"FW\",\n" + - " \"weather\": \"Mostly Sunny\",\n" + - " \"weatherCoded\": [],\n" + - " \"weatherPrimary\": \"Mostly Sunny\",\n" + - " \"weatherPrimaryCoded\": \"::FW\",\n" + - " \"icon\": \"fair.png\",\n" + - " \"visibilityKM\": 24.135,\n" + - " \"visibilityMI\": 15,\n" + - " \"uvi\": null,\n" + - " \"solradWM2\": 4740,\n" + - " \"solradMinWM2\": 0,\n" + - " \"solradMaxWM2\": 743,\n" + - " \"isDay\": true,\n" + - " \"maxCoverage\": \"\",\n" + - " \"sunrise\": 1665546895,\n" + - " \"sunset\": 1665588484,\n" + - " \"sunriseISO\": \"2022-10-12T05:54:55+02:00\",\n" + - " \"sunsetISO\": \"2022-10-12T17:28:04+02:00\"\n" + - " }\n" + - " ],\n" + - " \"profile\": {\n" + - " \"tz\": \"Africa/Cairo\",\n" + - " \"elevM\": 23,\n" + - " \"elevFT\": 75\n" + - " }\n" + - " }\n" + - " ]\n" + - "}"; - JSONObject jsonObject = new JSONObject(str); - String actual = XML.toString(jsonObject, null, XMLParserConfiguration.KEEP_STRINGS,2); - String expected = "true\n" + - "\n" + - " \n" + - " 31.25\n" + - " 30.063\n" + - " \n" + - " \n" + - " 23\n" + - " Africa/Cairo\n" + - " 75\n" + - " \n" + - " \n" + - " 2022-10-06T07:00:00+02:00\n" + - " E\n" + - " 95\n" + - " 21\n" + - " 15\n" + - " 10\n" + - " 353\n" + - " N\n" + - " 2022-10-06T05:51:14+02:00\n" + - " null\n" + - " 9\n" + - " null\n" + - " 66\n" + - " 0\n" + - " Mostly Sunny\n" + - " 2022-10-06T17:35:02+02:00\n" + - " 32\n" + - " 77\n" + - " N\n" + - " 89\n" + - " 0\n" + - " 22\n" + - " 25\n" + - " 25\n" + - " Mostly Sunny\n" + - " 41\n" + - " 58\n" + - " 41\n" + - " 22\n" + - " 14\n" + - " 0\n" + - " 22\n" + - " 353\n" + - " 16\n" + - " 8\n" + - " 70\n" + - " 2022-10-06T07:00:00+02:00\n" + - " 10\n" + - " 778\n" + - " 25\n" + - " 15\n" + - " ::FW\n" + - " 1665028274\n" + - " 78\n" + - " N\n" + - " \n" + - " fair.png\n" + - " 21\n" + - " 17\n" + - " FW\n" + - " 70\n" + - " 29\n" + - " 63\n" + - " 12\n" + - " 0\n" + - " 0\n" + - " NNW\n" + - " 13\n" + - " 22\n" + - " 11\n" + - " 32\n" + - " 1015\n" + - " 24.135\n" + - " 1665032400\n" + - " 90\n" + - " null\n" + - " 11\n" + - " 0\n" + - " 1\n" + - " 343\n" + - " 21\n" + - " 2\n" + - " 63\n" + - " 1\n" + - " 26\n" + - " 6\n" + - " NNW\n" + - " 17\n" + - " 29.97\n" + - " 80\n" + - " null\n" + - " true\n" + - " 19\n" + - " 52\n" + - " 5\n" + - " 1665070502\n" + - " 5608\n" + - " 9\n" + - " 25\n" + - " 77\n" + - " 6\n" + - " 40\n" + - " 342\n" + - " null\n" + - " \n" + - " \n" + - " 2022-10-07T07:00:00+02:00\n" + - " NNW\n" + - " 347\n" + - " 19\n" + - " 15\n" + - " 8\n" + - " 325\n" + - " NW\n" + - " 2022-10-07T05:51:50+02:00\n" + - " null\n" + - " 7\n" + - " null\n" + - " 66\n" + - " 0\n" + - " Mostly Sunny\n" + - " 2022-10-07T17:33:51+02:00\n" + - " 29\n" + - " 77\n" + - " NNW\n" + - " 85\n" + - " 0\n" + - " 30\n" + - " 23\n" + - " 23\n" + - " Mostly Sunny\n" + - " 37\n" + - " 54\n" + - " 37\n" + - " 20\n" + - " 12\n" + - " 0\n" + - " 20\n" + - " 325\n" + - " 13\n" + - " 6\n" + - " 67\n" + - " 2022-10-07T07:00:00+02:00\n" + - " 6\n" + - " 742\n" + - " 24\n" + - " 10\n" + - " ::FW\n" + - " 1665114710\n" + - " 76\n" + - " NW\n" + - " \n" + - " fair.png\n" + - " 19\n" + - " 15\n" + - " FW\n" + - " 67\n" + - " 30\n" + - " 60\n" + - " 6\n" + - " 0\n" + - " 0\n" + - " WNW\n" + - " 6\n" + - " 10\n" + - " 347\n" + - " 30\n" + - " 1014\n" + - " 24.135\n" + - " 1665118800\n" + - " 86\n" + - " null\n" + - " 10\n" + - " 0\n" + - " 1\n" + - " 316\n" + - " 16\n" + - " 2\n" + - " 60\n" + - " 1\n" + - " 24\n" + - " 6\n" + - " NW\n" + - " 15\n" + - " 29.95\n" + - " 76\n" + - " null\n" + - " true\n" + - " 19\n" + - " 50\n" + - " 1\n" + - " 1665156831\n" + - " 5486\n" + - " 2\n" + - " 18\n" + - " 77\n" + - " 1\n" + - " 29\n" + - " 298\n" + - " null\n" + - " \n" + - " \n" + - " 2022-10-08T07:00:00+02:00\n" + - " NW\n" + - " 309\n" + - " 19\n" + - " 15\n" + - " 8\n" + - " 21\n" + - " NNE\n" + - " 2022-10-08T05:52:26+02:00\n" + - " null\n" + - " 7\n" + - " null\n" + - " 66\n" + - " 0\n" + - " Partly Cloudy\n" + - " 2022-10-08T17:32:41+02:00\n" + - " 30\n" + - " 76\n" + - " NW\n" + - " 86\n" + - " 0\n" + - " 47\n" + - " 19\n" + - " 19\n" + - " Partly Cloudy\n" + - " 31\n" + - " 56\n" + - " 31\n" + - " 17\n" + - " 13\n" + - " 0\n" + - " 17\n" + - " 21\n" + - " 13\n" + - " 5\n" + - " 67\n" + - " 2022-10-08T07:00:00+02:00\n" + - " 5\n" + - " 682\n" + - " 25\n" + - " 9\n" + - " ::SC\n" + - " 1665201146\n" + - " 76\n" + - " NNE\n" + - " \n" + - " pcloudy.png\n" + - " 19\n" + - " 15\n" + - " SC\n" + - " 67\n" + - " 32\n" + - " 59\n" + - " 5\n" + - " 0\n" + - " 0\n" + - " WNW\n" + - " 5\n" + - " 9\n" + - " 309\n" + - " 30\n" + - " 1014\n" + - " 24.135\n" + - " 1665205200\n" + - " 87\n" + - " null\n" + - " 11\n" + - " 0\n" + - " 1\n" + - " 322\n" + - " 17\n" + - " 2\n" + - " 59\n" + - " 1\n" + - " 25\n" + - " 7\n" + - " NW\n" + - " 15\n" + - " 29.94\n" + - " 76\n" + - " null\n" + - " true\n" + - " 19\n" + - " 52\n" + - " 1\n" + - " 1665243161\n" + - " 4785\n" + - " 2\n" + - " 20\n" + - " 76\n" + - " 1\n" + - " 32\n" + - " 301\n" + - " null\n" + - " \n" + - " \n" + - " 2022-10-09T07:00:00+02:00\n" + - " NW\n" + - " 316\n" + - " 20\n" + - " 15\n" + - " 9\n" + - " 356\n" + - " N\n" + - " 2022-10-09T05:53:03+02:00\n" + - " null\n" + - " 8\n" + - " null\n" + - " 67\n" + - " 0\n" + - " Partly Cloudy\n" + - " 2022-10-09T17:31:31+02:00\n" + - " 30\n" + - " 86\n" + - " NW\n" + - " 86\n" + - " 0\n" + - " 47\n" + - " 23\n" + - " 23\n" + - " Partly Cloudy\n" + - " 36\n" + - " 57\n" + - " 36\n" + - " 20\n" + - " 14\n" + - " 0\n" + - " 20\n" + - " 356\n" + - " 14\n" + - " 5\n" + - " 67\n" + - " 2022-10-09T07:00:00+02:00\n" + - " 6\n" + - " 726\n" + - " 25\n" + - " 9\n" + - " ::SC\n" + - " 1665287583\n" + - " 77\n" + - " N\n" + - " \n" + - " pcloudy.png\n" + - " 20\n" + - " 17\n" + - " SC\n" + - " 67\n" + - " 31\n" + - " 63\n" + - " 5\n" + - " 0\n" + - " 0\n" + - " NNW\n" + - " 6\n" + - " 9\n" + - " 316\n" + - " 31\n" + - " 1016\n" + - " 24.135\n" + - " 1665291600\n" + - " 87\n" + - " null\n" + - " 11\n" + - " 0\n" + - " 2\n" + - " 354\n" + - " 19\n" + - " 4\n" + - " 63\n" + - " 2\n" + - " 25\n" + - " 7\n" + - " N\n" + - " 17\n" + - " 29.99\n" + - " 77\n" + - " null\n" + - " true\n" + - " 19\n" + - " 52\n" + - " 2\n" + - " 1665329491\n" + - " 4768\n" + - " 4\n" + - " 22\n" + - " 86\n" + - " 2\n" + - " 36\n" + - " 343\n" + - " null\n" + - " \n" + - " \n" + - " 2022-10-10T07:00:00+02:00\n" + - " E\n" + - " 91\n" + - " 21\n" + - " 15\n" + - " 9\n" + - " 358\n" + - " N\n" + - " 2022-10-10T05:53:40+02:00\n" + - " null\n" + - " 8\n" + - " null\n" + - " 70\n" + - " 0\n" + - " Partly Cloudy\n" + - " 2022-10-10T17:30:21+02:00\n" + - " 30\n" + - " 75\n" + - " N\n" + - " 86\n" + - " 0\n" + - " 64\n" + - " 22\n" + - " 22\n" + - " Partly Cloudy\n" + - " 36\n" + - " 58\n" + - " 36\n" + - " 19\n" + - " 14\n" + - " 0\n" + - " 19\n" + - " 358\n" + - " 15\n" + - " 7\n" + - " 69\n" + - " 2022-10-10T07:00:00+02:00\n" + - " 8\n" + - " 597\n" + - " 26\n" + - " 13\n" + - " ::SC\n" + - " 1665374020\n" + - " 78\n" + - " N\n" + - " \n" + - " pcloudy.png\n" + - " 21\n" + - " 16\n" + - " SC\n" + - " 69\n" + - " 35\n" + - " 61\n" + - " 7\n" + - " 0\n" + - " 0\n" + - " N\n" + - " 8\n" + - " 13\n" + - " 8\n" + - " 31\n" + - " 1017\n" + - " 24.135\n" + - " 1665378000\n" + - " 87\n" + - " null\n" + - " 13\n" + - " 0\n" + - " 2\n" + - " 10\n" + - " 16\n" + - " 4\n" + - " 61\n" + - " 2\n" + - " 25\n" + - " 6\n" + - " N\n" + - " 16\n" + - " 30.03\n" + - " 78\n" + - " null\n" + - " true\n" + - " 21\n" + - " 55\n" + - " 2\n" + - " 1665415821\n" + - " 4494\n" + - " 4\n" + - " 19\n" + - " 75\n" + - " 2\n" + - " 30\n" + - " 10\n" + - " null\n" + - " \n" + - " \n" + - " 2022-10-11T07:00:00+02:00\n" + - " NNE\n" + - " 13\n" + - " 22\n" + - " 15\n" + - " 18\n" + - " 13\n" + - " NNE\n" + - " 2022-10-11T05:54:18+02:00\n" + - " null\n" + - " 15\n" + - " null\n" + - " 70\n" + - " 0\n" + - " Sunny\n" + - " 2022-10-11T17:29:13+02:00\n" + - " 31\n" + - " 71\n" + - " NNE\n" + - " 87\n" + - " 0\n" + - " 0\n" + - " 19\n" + - " 19\n" + - " Sunny\n" + - " 31\n" + - " 55\n" + - " 31\n" + - " 17\n" + - " 13\n" + - " 0\n" + - " 17\n" + - " 14\n" + - " 28\n" + - " 9\n" + - " 72\n" + - " 2022-10-11T07:00:00+02:00\n" + - " 11\n" + - " 758\n" + - " 26\n" + - " 18\n" + - " ::CL\n" + - " 1665460458\n" + - " 78\n" + - " NNE\n" + - " \n" + - " sunny.png\n" + - " 22\n" + - " 17\n" + - " CL\n" + - " 72\n" + - " 30\n" + - " 62\n" + - " 10\n" + - " 0\n" + - " 0\n" + - " NNE\n" + - " 12\n" + - " 19\n" + - " 16\n" + - " 31\n" + - " 1015\n" + - " 24.135\n" + - " 1665464400\n" + - " 87\n" + - " null\n" + - " 11\n" + - " 0\n" + - " 7\n" + - " 28\n" + - " 15\n" + - " 14\n" + - " 62\n" + - " 8\n" + - " 26\n" + - " null\n" + - " NNE\n" + - " 17\n" + - " 29.98\n" + - " 79\n" + - " null\n" + - " true\n" + - " 21\n" + - " 51\n" + - " 8\n" + - " 1665502153\n" + - " 5450\n" + - " 15\n" + - " 17\n" + - " 71\n" + - " 9\n" + - " 28\n" + - " 28\n" + - " null\n" + - " \n" + - " \n" + - " 2022-10-12T07:00:00+02:00\n" + - " NNE\n" + - " 15\n" + - " 22\n" + - " 15\n" + - " 16\n" + - " 12\n" + - " NNE\n" + - " 2022-10-12T05:54:55+02:00\n" + - " null\n" + - " 14\n" + - " null\n" + - " 69\n" + - " 0\n" + - " Mostly Sunny\n" + - " 2022-10-12T17:28:04+02:00\n" + - " 31\n" + - " 68\n" + - " NNE\n" + - " 88\n" + - " 0\n" + - " 27\n" + - " 21\n" + - " 21\n" + - " Mostly Sunny\n" + - " 33\n" + - " 55\n" + - " 33\n" + - " 18\n" + - " 13\n" + - " 0\n" + - " 18\n" + - " 12\n" + - " 26\n" + - " 10\n" + - " 72\n" + - " 2022-10-12T07:00:00+02:00\n" + - " 11\n" + - " 743\n" + - " 26\n" + - " 18\n" + - " ::FW\n" + - " 1665546895\n" + - " 79\n" + - " NNE\n" + - " \n" + - " fair.png\n" + - " 22\n" + - " 16\n" + - " FW\n" + - " 72\n" + - " 29\n" + - " 60\n" + - " 10\n" + - " 0\n" + - " 0\n" + - " E\n" + - " 12\n" + - " 19\n" + - " 15\n" + - " 31\n" + - " 1014\n" + - " 24.135\n" + - " 1665550800\n" + - " 88\n" + - " null\n" + - " 11\n" + - " 0\n" + - " 7\n" + - " 96\n" + - " 15\n" + - " 13\n" + - " 60\n" + - " 8\n" + - " 26\n" + - " null\n" + - " E\n" + - " 16\n" + - " 29.95\n" + - " 80\n" + - " null\n" + - " true\n" + - " 21\n" + - " 51\n" + - " 8\n" + - " 1665588484\n" + - " 4740\n" + - " 15\n" + - " 17\n" + - " 68\n" + - " 9\n" + - " 28\n" + - " 96\n" + - " null\n" + - " \n" + - " day\n" + - " \n" + - " eg\n" + - " cairo\n" + - " qh\n" + - " \n" + - "\n" + - "null\n"; - assertEquals(actual, expected); + try { + InputStream jsonStream = null; + try { + jsonStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue593.json"); + final JSONObject object = new JSONObject(new JSONTokener(jsonStream)); + String actualString = XML.toString(object, null, XMLParserConfiguration.KEEP_STRINGS,2); + InputStream xmlStream = null; + try { + xmlStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue593.xml"); + int bufferSize = 1024; + char[] buffer = new char[bufferSize]; + StringBuilder expected = new StringBuilder(); + Reader in = new InputStreamReader(xmlStream, "UTF-8"); + for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) { + expected.append(buffer, 0, numRead); + } + assertEquals(expected.toString(), actualString); + } finally { + if (xmlStream != null) { + xmlStream.close(); + } + } + } finally { + if (jsonStream != null) { + jsonStream.close(); + } + } + } catch (IOException e) { + fail("file writer error: " +e.getMessage()); + } } } diff --git a/src/test/resources/Issue593.json b/src/test/resources/Issue593.json new file mode 100644 index 0000000..b3c82fe --- /dev/null +++ b/src/test/resources/Issue593.json @@ -0,0 +1,189 @@ +{ + "clinical_study": { + "brief_summary": { + "textblock": "CLEAR SYNERGY is an international multi center 2x2 randomized placebo controlled trial of" + }, + "brief_title": "CLEAR SYNERGY Neutrophil Substudy", + "overall_status": "Recruiting", + "eligibility": { + "study_pop": { + "textblock": "Patients who are randomized to the drug RCT portion of the CLEAR SYNERGY (OASIS 9) trial" + }, + "minimum_age": "19 Years", + "sampling_method": "Non-Probability Sample", + "gender": "All", + "criteria": { + "textblock": "Inclusion Criteria:" + }, + "healthy_volunteers": "No", + "maximum_age": "110 Years" + }, + "number_of_groups": "2", + "source": "NYU Langone Health", + "location_countries": { + "country": "United States" + }, + "study_design_info": { + "time_perspective": "Prospective", + "observational_model": "Other" + }, + "last_update_submitted_qc": "September 10, 2019", + "intervention_browse": { + "mesh_term": "Colchicine" + }, + "official_title": "Studies on the Effects of Colchicine on Neutrophil Biology in Acute Myocardial Infarction: A Substudy of the CLEAR SYNERGY (OASIS 9) Trial", + "primary_completion_date": { + "type": "Anticipated", + "content": "February 1, 2021" + }, + "sponsors": { + "lead_sponsor": { + "agency_class": "Other", + "agency": "NYU Langone Health" + }, + "collaborator": [ + { + "agency_class": "Other", + "agency": "Population Health Research Institute" + }, + { + "agency_class": "NIH", + "agency": "National Heart, Lung, and Blood Institute (NHLBI)" + } + ] + }, + "overall_official": { + "role": "Principal Investigator", + "affiliation": "NYU School of Medicine", + "last_name": "Binita Shah, MD" + }, + "overall_contact_backup": { + "last_name": "Binita Shah, MD" + }, + "condition_browse": { + "mesh_term": [ + "Myocardial Infarction", + "ST Elevation Myocardial Infarction", + "Infarction" + ] + }, + "overall_contact": { + "phone": "646-501-9648", + "last_name": "Fatmira Curovic", + "email": "fatmira.curovic@nyumc.org" + }, + "responsible_party": { + "responsible_party_type": "Principal Investigator", + "investigator_title": "Assistant Professor of Medicine", + "investigator_full_name": "Binita Shah", + "investigator_affiliation": "NYU Langone Health" + }, + "study_first_submitted_qc": "March 12, 2019", + "start_date": { + "type": "Actual", + "content": "March 4, 2019" + }, + "has_expanded_access": "No", + "study_first_posted": { + "type": "Actual", + "content": "March 14, 2019" + }, + "arm_group": [ + { + "arm_group_label": "Colchicine" + }, + { + "arm_group_label": "Placebo" + } + ], + "primary_outcome": { + "measure": "soluble L-selectin", + "time_frame": "between baseline and 3 months", + "description": "Change in soluble L-selectin between baseline and 3 mo after STEMI in the placebo vs. colchicine groups." + }, + "secondary_outcome": [ + { + "measure": "Other soluble markers of neutrophil activity", + "time_frame": "between baseline and 3 months", + "description": "Other markers of neutrophil activity will be evaluated at baseline and 3 months after STEMI (myeloperoxidase, matrix metalloproteinase-9, neutrophil gelatinase-associated lipocalin, neutrophil elastase, intercellular/vascular cellular adhesion molecules)" + }, + { + "measure": "Markers of systemic inflammation", + "time_frame": "between baseline and 3 months", + "description": "Markers of systemic inflammation will be evaluated at baseline and 3 months after STEMI (high sensitive CRP, IL-1β)" + }, + { + "measure": "Neutrophil-driven responses that may further propagate injury", + "time_frame": "between baseline and 3 months", + "description": "Neutrophil-driven responses that may further propagate injury will be evaluated at baseline and 3 months after STEMI (neutrophil extracellular traps, neutrophil-derived microparticles)" + } + ], + "oversight_info": { + "is_fda_regulated_drug": "No", + "is_fda_regulated_device": "No", + "has_dmc": "No" + }, + "last_update_posted": { + "type": "Actual", + "content": "September 12, 2019" + }, + "id_info": { + "nct_id": "NCT03874338", + "org_study_id": "18-01323", + "secondary_id": "1R01HL146206" + }, + "enrollment": { + "type": "Anticipated", + "content": "670" + }, + "study_first_submitted": "March 12, 2019", + "condition": [ + "Neutrophils.Hypersegmented | Bld-Ser-Plas", + "STEMI - ST Elevation Myocardial Infarction" + ], + "study_type": "Observational", + "required_header": { + "download_date": "ClinicalTrials.gov processed this data on July 19, 2020", + "link_text": "Link to the current ClinicalTrials.gov record.", + "url": "https://clinicaltrials.gov/show/NCT03874338" + }, + "last_update_submitted": "September 10, 2019", + "completion_date": { + "type": "Anticipated", + "content": "February 1, 2022" + }, + "location": { + "contact": { + "phone": "646-501-9648", + "last_name": "Fatmira Curovic", + "email": "fatmira.curovic@nyumc.org" + }, + "facility": { + "address": { + "zip": "10016", + "country": "United States", + "city": "New York", + "state": "New York" + }, + "name": "NYU School of Medicine" + }, + "status": "Recruiting", + "contact_backup": { + "last_name": "Binita Shah, MD" + } + }, + "intervention": { + "intervention_type": "Drug", + "arm_group_label": [ + "Colchicine", + "Placebo" + ], + "description": "Participants in the main CLEAR SYNERGY trial are randomized to colchicine/spironolactone versus placebo in a 2x2 factorial design. The substudy is interested in the evaluation of biospecimens obtained from patients in the colchicine vs placebo group.", + "intervention_name": "Colchicine Pill" + }, + "patient_data": { + "sharing_ipd": "No" + }, + "verification_date": "September 2019" + } +} \ No newline at end of file diff --git a/src/test/resources/Issue593.xml b/src/test/resources/Issue593.xml new file mode 100644 index 0000000..bf78f3b --- /dev/null +++ b/src/test/resources/Issue593.xml @@ -0,0 +1,169 @@ + + + + + ClinicalTrials.gov processed this data on July 19, 2020 + Link to the current ClinicalTrials.gov record. + https://clinicaltrials.gov/show/NCT03874338 + + + 18-01323 + 1R01HL146206 + NCT03874338 + + CLEAR SYNERGY Neutrophil Substudy + Studies on the Effects of Colchicine on Neutrophil Biology in Acute Myocardial Infarction: A Substudy of the CLEAR SYNERGY (OASIS 9) Trial + + + NYU Langone Health + Other + + + Population Health Research Institute + Other + + + National Heart, Lung, and Blood Institute (NHLBI) + NIH + + + NYU Langone Health + + No + No + No + + + + CLEAR SYNERGY is an international multi center 2x2 randomized placebo controlled trial of + + + Recruiting + March 4, 2019 + February 1, 2022 + February 1, 2021 + Observational + No + + Other + Prospective + + + soluble L-selectin + between baseline and 3 months + Change in soluble L-selectin between baseline and 3 mo after STEMI in the placebo vs. colchicine groups. + + + Other soluble markers of neutrophil activity + between baseline and 3 months + Other markers of neutrophil activity will be evaluated at baseline and 3 months after STEMI (myeloperoxidase, matrix metalloproteinase-9, neutrophil gelatinase-associated lipocalin, neutrophil elastase, intercellular/vascular cellular adhesion molecules) + + + Markers of systemic inflammation + between baseline and 3 months + Markers of systemic inflammation will be evaluated at baseline and 3 months after STEMI (high sensitive CRP, IL-1β) + + + Neutrophil-driven responses that may further propagate injury + between baseline and 3 months + Neutrophil-driven responses that may further propagate injury will be evaluated at baseline and 3 months after STEMI (neutrophil extracellular traps, neutrophil-derived microparticles) + + 2 + 670 + Neutrophils.Hypersegmented | Bld-Ser-Plas + STEMI - ST Elevation Myocardial Infarction + + Colchicine + + + Placebo + + + Drug + Colchicine Pill + Participants in the main CLEAR SYNERGY trial are randomized to colchicine/spironolactone versus placebo in a 2x2 factorial design. The substudy is interested in the evaluation of biospecimens obtained from patients in the colchicine vs placebo group. + Colchicine + Placebo + + + + + Patients who are randomized to the drug RCT portion of the CLEAR SYNERGY (OASIS 9) trial + + + Non-Probability Sample + + + Inclusion Criteria: + + + All + 19 Years + 110 Years + No + + + Binita Shah, MD + Principal Investigator + NYU School of Medicine + + + Fatmira Curovic + 646-501-9648 + fatmira.curovic@nyumc.org + + + Binita Shah, MD + + + + NYU School of Medicine +
+ New York + New York + 10016 + United States +
+
+ Recruiting + + Fatmira Curovic + 646-501-9648 + fatmira.curovic@nyumc.org + + + Binita Shah, MD + +
+ + United States + + September 2019 + March 12, 2019 + March 12, 2019 + March 14, 2019 + September 10, 2019 + September 10, 2019 + September 12, 2019 + + Principal Investigator + NYU Langone Health + Binita Shah + Assistant Professor of Medicine + + + + Myocardial Infarction + ST Elevation Myocardial Infarction + Infarction + + + + Colchicine + + + No + + +
From 153972afdf3150fa08e48b30da6b70bec63d4664 Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 7 Oct 2022 10:35:14 +0100 Subject: [PATCH 13/48] Adding resources --- src/test/resources/Issue593.json | 875 ++++++++++++++++++++++++------- src/test/resources/Issue593.xml | 860 ++++++++++++++++++++++++------ 2 files changed, 1386 insertions(+), 349 deletions(-) diff --git a/src/test/resources/Issue593.json b/src/test/resources/Issue593.json index b3c82fe..213625a 100644 --- a/src/test/resources/Issue593.json +++ b/src/test/resources/Issue593.json @@ -1,189 +1,704 @@ { - "clinical_study": { - "brief_summary": { - "textblock": "CLEAR SYNERGY is an international multi center 2x2 randomized placebo controlled trial of" - }, - "brief_title": "CLEAR SYNERGY Neutrophil Substudy", - "overall_status": "Recruiting", - "eligibility": { - "study_pop": { - "textblock": "Patients who are randomized to the drug RCT portion of the CLEAR SYNERGY (OASIS 9) trial" + "success": true, + "error": null, + "response": [ + { + "loc": { + "long": 31.25, + "lat": 30.063 }, - "minimum_age": "19 Years", - "sampling_method": "Non-Probability Sample", - "gender": "All", - "criteria": { - "textblock": "Inclusion Criteria:" + "interval": "day", + "place": { + "name": "cairo", + "state": "qh", + "country": "eg" }, - "healthy_volunteers": "No", - "maximum_age": "110 Years" - }, - "number_of_groups": "2", - "source": "NYU Langone Health", - "location_countries": { - "country": "United States" - }, - "study_design_info": { - "time_perspective": "Prospective", - "observational_model": "Other" - }, - "last_update_submitted_qc": "September 10, 2019", - "intervention_browse": { - "mesh_term": "Colchicine" - }, - "official_title": "Studies on the Effects of Colchicine on Neutrophil Biology in Acute Myocardial Infarction: A Substudy of the CLEAR SYNERGY (OASIS 9) Trial", - "primary_completion_date": { - "type": "Anticipated", - "content": "February 1, 2021" - }, - "sponsors": { - "lead_sponsor": { - "agency_class": "Other", - "agency": "NYU Langone Health" - }, - "collaborator": [ + "periods": [ { - "agency_class": "Other", - "agency": "Population Health Research Institute" + "timestamp": 1665032400, + "validTime": "2022-10-06T07:00:00+02:00", + "dateTimeISO": "2022-10-06T07:00:00+02:00", + "maxTempC": 32, + "maxTempF": 90, + "minTempC": 19, + "minTempF": 66, + "avgTempC": 25, + "avgTempF": 78, + "tempC": null, + "tempF": null, + "maxFeelslikeC": 32, + "maxFeelslikeF": 89, + "minFeelslikeC": 21, + "minFeelslikeF": 70, + "avgFeelslikeC": 26, + "avgFeelslikeF": 80, + "feelslikeC": 21, + "feelslikeF": 70, + "maxDewpointC": 17, + "maxDewpointF": 63, + "minDewpointC": 11, + "minDewpointF": 52, + "avgDewpointC": 14, + "avgDewpointF": 58, + "dewpointC": 17, + "dewpointF": 63, + "maxHumidity": 77, + "minHumidity": 29, + "humidity": 77, + "pop": 0, + "precipMM": 0, + "precipIN": 0, + "iceaccum": null, + "iceaccumMM": null, + "iceaccumIN": null, + "snowCM": 0, + "snowIN": 0, + "pressureMB": 1015, + "pressureIN": 29.97, + "windDir": "N", + "windDirDEG": 353, + "windSpeedKTS": 5, + "windSpeedKPH": 9, + "windSpeedMPH": 6, + "windGustKTS": 21, + "windGustKPH": 40, + "windGustMPH": 25, + "windDirMax": "NNW", + "windDirMaxDEG": 342, + "windSpeedMaxKTS": 9, + "windSpeedMaxKPH": 16, + "windSpeedMaxMPH": 10, + "windDirMin": "N", + "windDirMinDEG": 353, + "windSpeedMinKTS": 1, + "windSpeedMinKPH": 2, + "windSpeedMinMPH": 1, + "windDir80m": "N", + "windDir80mDEG": 11, + "windSpeed80mKTS": 12, + "windSpeed80mKPH": 22, + "windSpeed80mMPH": 13, + "windGust80mKTS": 22, + "windGust80mKPH": 41, + "windGust80mMPH": 25, + "windDirMax80m": "NNW", + "windDirMax80mDEG": 343, + "windSpeedMax80mKTS": 22, + "windSpeedMax80mKPH": 41, + "windSpeedMax80mMPH": 25, + "windDirMin80m": "E", + "windDirMin80mDEG": 95, + "windSpeedMin80mKTS": 8, + "windSpeedMin80mKPH": 15, + "windSpeedMin80mMPH": 10, + "sky": 22, + "cloudsCoded": "FW", + "weather": "Mostly Sunny", + "weatherCoded": [], + "weatherPrimary": "Mostly Sunny", + "weatherPrimaryCoded": "::FW", + "icon": "fair.png", + "visibilityKM": 24.135, + "visibilityMI": 15, + "uvi": 6, + "solradWM2": 5608, + "solradMinWM2": 0, + "solradMaxWM2": 778, + "isDay": true, + "maxCoverage": "", + "sunrise": 1665028274, + "sunset": 1665070502, + "sunriseISO": "2022-10-06T05:51:14+02:00", + "sunsetISO": "2022-10-06T17:35:02+02:00" }, { - "agency_class": "NIH", - "agency": "National Heart, Lung, and Blood Institute (NHLBI)" - } - ] - }, - "overall_official": { - "role": "Principal Investigator", - "affiliation": "NYU School of Medicine", - "last_name": "Binita Shah, MD" - }, - "overall_contact_backup": { - "last_name": "Binita Shah, MD" - }, - "condition_browse": { - "mesh_term": [ - "Myocardial Infarction", - "ST Elevation Myocardial Infarction", - "Infarction" - ] - }, - "overall_contact": { - "phone": "646-501-9648", - "last_name": "Fatmira Curovic", - "email": "fatmira.curovic@nyumc.org" - }, - "responsible_party": { - "responsible_party_type": "Principal Investigator", - "investigator_title": "Assistant Professor of Medicine", - "investigator_full_name": "Binita Shah", - "investigator_affiliation": "NYU Langone Health" - }, - "study_first_submitted_qc": "March 12, 2019", - "start_date": { - "type": "Actual", - "content": "March 4, 2019" - }, - "has_expanded_access": "No", - "study_first_posted": { - "type": "Actual", - "content": "March 14, 2019" - }, - "arm_group": [ - { - "arm_group_label": "Colchicine" - }, - { - "arm_group_label": "Placebo" - } - ], - "primary_outcome": { - "measure": "soluble L-selectin", - "time_frame": "between baseline and 3 months", - "description": "Change in soluble L-selectin between baseline and 3 mo after STEMI in the placebo vs. colchicine groups." - }, - "secondary_outcome": [ - { - "measure": "Other soluble markers of neutrophil activity", - "time_frame": "between baseline and 3 months", - "description": "Other markers of neutrophil activity will be evaluated at baseline and 3 months after STEMI (myeloperoxidase, matrix metalloproteinase-9, neutrophil gelatinase-associated lipocalin, neutrophil elastase, intercellular/vascular cellular adhesion molecules)" - }, - { - "measure": "Markers of systemic inflammation", - "time_frame": "between baseline and 3 months", - "description": "Markers of systemic inflammation will be evaluated at baseline and 3 months after STEMI (high sensitive CRP, IL-1β)" - }, - { - "measure": "Neutrophil-driven responses that may further propagate injury", - "time_frame": "between baseline and 3 months", - "description": "Neutrophil-driven responses that may further propagate injury will be evaluated at baseline and 3 months after STEMI (neutrophil extracellular traps, neutrophil-derived microparticles)" - } - ], - "oversight_info": { - "is_fda_regulated_drug": "No", - "is_fda_regulated_device": "No", - "has_dmc": "No" - }, - "last_update_posted": { - "type": "Actual", - "content": "September 12, 2019" - }, - "id_info": { - "nct_id": "NCT03874338", - "org_study_id": "18-01323", - "secondary_id": "1R01HL146206" - }, - "enrollment": { - "type": "Anticipated", - "content": "670" - }, - "study_first_submitted": "March 12, 2019", - "condition": [ - "Neutrophils.Hypersegmented | Bld-Ser-Plas", - "STEMI - ST Elevation Myocardial Infarction" - ], - "study_type": "Observational", - "required_header": { - "download_date": "ClinicalTrials.gov processed this data on July 19, 2020", - "link_text": "Link to the current ClinicalTrials.gov record.", - "url": "https://clinicaltrials.gov/show/NCT03874338" - }, - "last_update_submitted": "September 10, 2019", - "completion_date": { - "type": "Anticipated", - "content": "February 1, 2022" - }, - "location": { - "contact": { - "phone": "646-501-9648", - "last_name": "Fatmira Curovic", - "email": "fatmira.curovic@nyumc.org" - }, - "facility": { - "address": { - "zip": "10016", - "country": "United States", - "city": "New York", - "state": "New York" + "timestamp": 1665118800, + "validTime": "2022-10-07T07:00:00+02:00", + "dateTimeISO": "2022-10-07T07:00:00+02:00", + "maxTempC": 30, + "maxTempF": 86, + "minTempC": 19, + "minTempF": 66, + "avgTempC": 24, + "avgTempF": 76, + "tempC": null, + "tempF": null, + "maxFeelslikeC": 29, + "maxFeelslikeF": 85, + "minFeelslikeC": 19, + "minFeelslikeF": 67, + "avgFeelslikeC": 24, + "avgFeelslikeF": 76, + "feelslikeC": 19, + "feelslikeF": 67, + "maxDewpointC": 15, + "maxDewpointF": 60, + "minDewpointC": 10, + "minDewpointF": 50, + "avgDewpointC": 12, + "avgDewpointF": 54, + "dewpointC": 15, + "dewpointF": 60, + "maxHumidity": 77, + "minHumidity": 30, + "humidity": 77, + "pop": 0, + "precipMM": 0, + "precipIN": 0, + "iceaccum": null, + "iceaccumMM": null, + "iceaccumIN": null, + "snowCM": 0, + "snowIN": 0, + "pressureMB": 1014, + "pressureIN": 29.95, + "windDir": "NW", + "windDirDEG": 325, + "windSpeedKTS": 1, + "windSpeedKPH": 2, + "windSpeedMPH": 1, + "windGustKTS": 16, + "windGustKPH": 29, + "windGustMPH": 18, + "windDirMax": "WNW", + "windDirMaxDEG": 298, + "windSpeedMaxKTS": 7, + "windSpeedMaxKPH": 13, + "windSpeedMaxMPH": 8, + "windDirMin": "NW", + "windDirMinDEG": 325, + "windSpeedMinKTS": 1, + "windSpeedMinKPH": 2, + "windSpeedMinMPH": 1, + "windDir80m": "NNW", + "windDir80mDEG": 347, + "windSpeed80mKTS": 6, + "windSpeed80mKPH": 10, + "windSpeed80mMPH": 6, + "windGust80mKTS": 20, + "windGust80mKPH": 37, + "windGust80mMPH": 23, + "windDirMax80m": "NW", + "windDirMax80mDEG": 316, + "windSpeedMax80mKTS": 20, + "windSpeedMax80mKPH": 37, + "windSpeedMax80mMPH": 23, + "windDirMin80m": "NNW", + "windDirMin80mDEG": 347, + "windSpeedMin80mKTS": 6, + "windSpeedMin80mKPH": 10, + "windSpeedMin80mMPH": 6, + "sky": 30, + "cloudsCoded": "FW", + "weather": "Mostly Sunny", + "weatherCoded": [], + "weatherPrimary": "Mostly Sunny", + "weatherPrimaryCoded": "::FW", + "icon": "fair.png", + "visibilityKM": 24.135, + "visibilityMI": 15, + "uvi": 6, + "solradWM2": 5486, + "solradMinWM2": 0, + "solradMaxWM2": 742, + "isDay": true, + "maxCoverage": "", + "sunrise": 1665114710, + "sunset": 1665156831, + "sunriseISO": "2022-10-07T05:51:50+02:00", + "sunsetISO": "2022-10-07T17:33:51+02:00" }, - "name": "NYU School of Medicine" - }, - "status": "Recruiting", - "contact_backup": { - "last_name": "Binita Shah, MD" - } - }, - "intervention": { - "intervention_type": "Drug", - "arm_group_label": [ - "Colchicine", - "Placebo" + { + "timestamp": 1665205200, + "validTime": "2022-10-08T07:00:00+02:00", + "dateTimeISO": "2022-10-08T07:00:00+02:00", + "maxTempC": 30, + "maxTempF": 87, + "minTempC": 19, + "minTempF": 66, + "avgTempC": 25, + "avgTempF": 76, + "tempC": null, + "tempF": null, + "maxFeelslikeC": 30, + "maxFeelslikeF": 86, + "minFeelslikeC": 19, + "minFeelslikeF": 67, + "avgFeelslikeC": 25, + "avgFeelslikeF": 76, + "feelslikeC": 19, + "feelslikeF": 67, + "maxDewpointC": 15, + "maxDewpointF": 59, + "minDewpointC": 11, + "minDewpointF": 52, + "avgDewpointC": 13, + "avgDewpointF": 56, + "dewpointC": 15, + "dewpointF": 59, + "maxHumidity": 76, + "minHumidity": 32, + "humidity": 76, + "pop": 0, + "precipMM": 0, + "precipIN": 0, + "iceaccum": null, + "iceaccumMM": null, + "iceaccumIN": null, + "snowCM": 0, + "snowIN": 0, + "pressureMB": 1014, + "pressureIN": 29.94, + "windDir": "NNE", + "windDirDEG": 21, + "windSpeedKTS": 1, + "windSpeedKPH": 2, + "windSpeedMPH": 1, + "windGustKTS": 17, + "windGustKPH": 32, + "windGustMPH": 20, + "windDirMax": "WNW", + "windDirMaxDEG": 301, + "windSpeedMaxKTS": 7, + "windSpeedMaxKPH": 13, + "windSpeedMaxMPH": 8, + "windDirMin": "NNE", + "windDirMinDEG": 21, + "windSpeedMinKTS": 1, + "windSpeedMinKPH": 2, + "windSpeedMinMPH": 1, + "windDir80m": "NW", + "windDir80mDEG": 309, + "windSpeed80mKTS": 5, + "windSpeed80mKPH": 9, + "windSpeed80mMPH": 5, + "windGust80mKTS": 17, + "windGust80mKPH": 31, + "windGust80mMPH": 19, + "windDirMax80m": "NW", + "windDirMax80mDEG": 322, + "windSpeedMax80mKTS": 17, + "windSpeedMax80mKPH": 31, + "windSpeedMax80mMPH": 19, + "windDirMin80m": "NW", + "windDirMin80mDEG": 309, + "windSpeedMin80mKTS": 5, + "windSpeedMin80mKPH": 9, + "windSpeedMin80mMPH": 5, + "sky": 47, + "cloudsCoded": "SC", + "weather": "Partly Cloudy", + "weatherCoded": [], + "weatherPrimary": "Partly Cloudy", + "weatherPrimaryCoded": "::SC", + "icon": "pcloudy.png", + "visibilityKM": 24.135, + "visibilityMI": 15, + "uvi": 7, + "solradWM2": 4785, + "solradMinWM2": 0, + "solradMaxWM2": 682, + "isDay": true, + "maxCoverage": "", + "sunrise": 1665201146, + "sunset": 1665243161, + "sunriseISO": "2022-10-08T05:52:26+02:00", + "sunsetISO": "2022-10-08T17:32:41+02:00" + }, + { + "timestamp": 1665291600, + "validTime": "2022-10-09T07:00:00+02:00", + "dateTimeISO": "2022-10-09T07:00:00+02:00", + "maxTempC": 31, + "maxTempF": 87, + "minTempC": 19, + "minTempF": 67, + "avgTempC": 25, + "avgTempF": 77, + "tempC": null, + "tempF": null, + "maxFeelslikeC": 30, + "maxFeelslikeF": 86, + "minFeelslikeC": 20, + "minFeelslikeF": 67, + "avgFeelslikeC": 25, + "avgFeelslikeF": 77, + "feelslikeC": 20, + "feelslikeF": 67, + "maxDewpointC": 17, + "maxDewpointF": 63, + "minDewpointC": 11, + "minDewpointF": 52, + "avgDewpointC": 14, + "avgDewpointF": 57, + "dewpointC": 17, + "dewpointF": 63, + "maxHumidity": 86, + "minHumidity": 31, + "humidity": 86, + "pop": 0, + "precipMM": 0, + "precipIN": 0, + "iceaccum": null, + "iceaccumMM": null, + "iceaccumIN": null, + "snowCM": 0, + "snowIN": 0, + "pressureMB": 1016, + "pressureIN": 29.99, + "windDir": "N", + "windDirDEG": 356, + "windSpeedKTS": 2, + "windSpeedKPH": 4, + "windSpeedMPH": 2, + "windGustKTS": 19, + "windGustKPH": 36, + "windGustMPH": 22, + "windDirMax": "NNW", + "windDirMaxDEG": 343, + "windSpeedMaxKTS": 8, + "windSpeedMaxKPH": 14, + "windSpeedMaxMPH": 9, + "windDirMin": "N", + "windDirMinDEG": 356, + "windSpeedMinKTS": 2, + "windSpeedMinKPH": 4, + "windSpeedMinMPH": 2, + "windDir80m": "NW", + "windDir80mDEG": 316, + "windSpeed80mKTS": 5, + "windSpeed80mKPH": 9, + "windSpeed80mMPH": 6, + "windGust80mKTS": 20, + "windGust80mKPH": 36, + "windGust80mMPH": 23, + "windDirMax80m": "N", + "windDirMax80mDEG": 354, + "windSpeedMax80mKTS": 20, + "windSpeedMax80mKPH": 36, + "windSpeedMax80mMPH": 23, + "windDirMin80m": "NW", + "windDirMin80mDEG": 316, + "windSpeedMin80mKTS": 5, + "windSpeedMin80mKPH": 9, + "windSpeedMin80mMPH": 6, + "sky": 47, + "cloudsCoded": "SC", + "weather": "Partly Cloudy", + "weatherCoded": [], + "weatherPrimary": "Partly Cloudy", + "weatherPrimaryCoded": "::SC", + "icon": "pcloudy.png", + "visibilityKM": 24.135, + "visibilityMI": 15, + "uvi": 7, + "solradWM2": 4768, + "solradMinWM2": 0, + "solradMaxWM2": 726, + "isDay": true, + "maxCoverage": "", + "sunrise": 1665287583, + "sunset": 1665329491, + "sunriseISO": "2022-10-09T05:53:03+02:00", + "sunsetISO": "2022-10-09T17:31:31+02:00" + }, + { + "timestamp": 1665378000, + "validTime": "2022-10-10T07:00:00+02:00", + "dateTimeISO": "2022-10-10T07:00:00+02:00", + "maxTempC": 31, + "maxTempF": 87, + "minTempC": 21, + "minTempF": 70, + "avgTempC": 26, + "avgTempF": 78, + "tempC": null, + "tempF": null, + "maxFeelslikeC": 30, + "maxFeelslikeF": 86, + "minFeelslikeC": 21, + "minFeelslikeF": 69, + "avgFeelslikeC": 25, + "avgFeelslikeF": 78, + "feelslikeC": 21, + "feelslikeF": 69, + "maxDewpointC": 16, + "maxDewpointF": 61, + "minDewpointC": 13, + "minDewpointF": 55, + "avgDewpointC": 14, + "avgDewpointF": 58, + "dewpointC": 16, + "dewpointF": 61, + "maxHumidity": 75, + "minHumidity": 35, + "humidity": 75, + "pop": 0, + "precipMM": 0, + "precipIN": 0, + "iceaccum": null, + "iceaccumMM": null, + "iceaccumIN": null, + "snowCM": 0, + "snowIN": 0, + "pressureMB": 1017, + "pressureIN": 30.03, + "windDir": "N", + "windDirDEG": 358, + "windSpeedKTS": 2, + "windSpeedKPH": 4, + "windSpeedMPH": 2, + "windGustKTS": 16, + "windGustKPH": 30, + "windGustMPH": 19, + "windDirMax": "N", + "windDirMaxDEG": 10, + "windSpeedMaxKTS": 8, + "windSpeedMaxKPH": 15, + "windSpeedMaxMPH": 9, + "windDirMin": "N", + "windDirMinDEG": 358, + "windSpeedMinKTS": 2, + "windSpeedMinKPH": 4, + "windSpeedMinMPH": 2, + "windDir80m": "N", + "windDir80mDEG": 8, + "windSpeed80mKTS": 7, + "windSpeed80mKPH": 13, + "windSpeed80mMPH": 8, + "windGust80mKTS": 19, + "windGust80mKPH": 36, + "windGust80mMPH": 22, + "windDirMax80m": "N", + "windDirMax80mDEG": 10, + "windSpeedMax80mKTS": 19, + "windSpeedMax80mKPH": 36, + "windSpeedMax80mMPH": 22, + "windDirMin80m": "E", + "windDirMin80mDEG": 91, + "windSpeedMin80mKTS": 7, + "windSpeedMin80mKPH": 13, + "windSpeedMin80mMPH": 8, + "sky": 64, + "cloudsCoded": "SC", + "weather": "Partly Cloudy", + "weatherCoded": [], + "weatherPrimary": "Partly Cloudy", + "weatherPrimaryCoded": "::SC", + "icon": "pcloudy.png", + "visibilityKM": 24.135, + "visibilityMI": 15, + "uvi": 6, + "solradWM2": 4494, + "solradMinWM2": 0, + "solradMaxWM2": 597, + "isDay": true, + "maxCoverage": "", + "sunrise": 1665374020, + "sunset": 1665415821, + "sunriseISO": "2022-10-10T05:53:40+02:00", + "sunsetISO": "2022-10-10T17:30:21+02:00" + }, + { + "timestamp": 1665464400, + "validTime": "2022-10-11T07:00:00+02:00", + "dateTimeISO": "2022-10-11T07:00:00+02:00", + "maxTempC": 31, + "maxTempF": 87, + "minTempC": 21, + "minTempF": 70, + "avgTempC": 26, + "avgTempF": 78, + "tempC": null, + "tempF": null, + "maxFeelslikeC": 31, + "maxFeelslikeF": 87, + "minFeelslikeC": 22, + "minFeelslikeF": 72, + "avgFeelslikeC": 26, + "avgFeelslikeF": 79, + "feelslikeC": 22, + "feelslikeF": 72, + "maxDewpointC": 17, + "maxDewpointF": 62, + "minDewpointC": 11, + "minDewpointF": 51, + "avgDewpointC": 13, + "avgDewpointF": 55, + "dewpointC": 17, + "dewpointF": 62, + "maxHumidity": 71, + "minHumidity": 30, + "humidity": 71, + "pop": 0, + "precipMM": 0, + "precipIN": 0, + "iceaccum": null, + "iceaccumMM": null, + "iceaccumIN": null, + "snowCM": 0, + "snowIN": 0, + "pressureMB": 1015, + "pressureIN": 29.98, + "windDir": "NNE", + "windDirDEG": 13, + "windSpeedKTS": 8, + "windSpeedKPH": 15, + "windSpeedMPH": 9, + "windGustKTS": 15, + "windGustKPH": 28, + "windGustMPH": 17, + "windDirMax": "NNE", + "windDirMaxDEG": 28, + "windSpeedMaxKTS": 15, + "windSpeedMaxKPH": 28, + "windSpeedMaxMPH": 18, + "windDirMin": "NNE", + "windDirMinDEG": 14, + "windSpeedMinKTS": 7, + "windSpeedMinKPH": 14, + "windSpeedMinMPH": 8, + "windDir80m": "NNE", + "windDir80mDEG": 16, + "windSpeed80mKTS": 10, + "windSpeed80mKPH": 19, + "windSpeed80mMPH": 12, + "windGust80mKTS": 17, + "windGust80mKPH": 31, + "windGust80mMPH": 19, + "windDirMax80m": "NNE", + "windDirMax80mDEG": 28, + "windSpeedMax80mKTS": 17, + "windSpeedMax80mKPH": 31, + "windSpeedMax80mMPH": 19, + "windDirMin80m": "NNE", + "windDirMin80mDEG": 13, + "windSpeedMin80mKTS": 9, + "windSpeedMin80mKPH": 18, + "windSpeedMin80mMPH": 11, + "sky": 0, + "cloudsCoded": "CL", + "weather": "Sunny", + "weatherCoded": [], + "weatherPrimary": "Sunny", + "weatherPrimaryCoded": "::CL", + "icon": "sunny.png", + "visibilityKM": 24.135, + "visibilityMI": 15, + "uvi": null, + "solradWM2": 5450, + "solradMinWM2": 0, + "solradMaxWM2": 758, + "isDay": true, + "maxCoverage": "", + "sunrise": 1665460458, + "sunset": 1665502153, + "sunriseISO": "2022-10-11T05:54:18+02:00", + "sunsetISO": "2022-10-11T17:29:13+02:00" + }, + { + "timestamp": 1665550800, + "validTime": "2022-10-12T07:00:00+02:00", + "dateTimeISO": "2022-10-12T07:00:00+02:00", + "maxTempC": 31, + "maxTempF": 88, + "minTempC": 21, + "minTempF": 69, + "avgTempC": 26, + "avgTempF": 79, + "tempC": null, + "tempF": null, + "maxFeelslikeC": 31, + "maxFeelslikeF": 88, + "minFeelslikeC": 22, + "minFeelslikeF": 72, + "avgFeelslikeC": 26, + "avgFeelslikeF": 80, + "feelslikeC": 22, + "feelslikeF": 72, + "maxDewpointC": 16, + "maxDewpointF": 60, + "minDewpointC": 11, + "minDewpointF": 51, + "avgDewpointC": 13, + "avgDewpointF": 55, + "dewpointC": 16, + "dewpointF": 60, + "maxHumidity": 68, + "minHumidity": 29, + "humidity": 68, + "pop": 0, + "precipMM": 0, + "precipIN": 0, + "iceaccum": null, + "iceaccumMM": null, + "iceaccumIN": null, + "snowCM": 0, + "snowIN": 0, + "pressureMB": 1014, + "pressureIN": 29.95, + "windDir": "NNE", + "windDirDEG": 12, + "windSpeedKTS": 8, + "windSpeedKPH": 15, + "windSpeedMPH": 9, + "windGustKTS": 15, + "windGustKPH": 28, + "windGustMPH": 17, + "windDirMax": "E", + "windDirMaxDEG": 96, + "windSpeedMaxKTS": 14, + "windSpeedMaxKPH": 26, + "windSpeedMaxMPH": 16, + "windDirMin": "NNE", + "windDirMinDEG": 12, + "windSpeedMinKTS": 7, + "windSpeedMinKPH": 13, + "windSpeedMinMPH": 8, + "windDir80m": "NNE", + "windDir80mDEG": 15, + "windSpeed80mKTS": 10, + "windSpeed80mKPH": 19, + "windSpeed80mMPH": 12, + "windGust80mKTS": 18, + "windGust80mKPH": 33, + "windGust80mMPH": 21, + "windDirMax80m": "E", + "windDirMax80mDEG": 96, + "windSpeedMax80mKTS": 18, + "windSpeedMax80mKPH": 33, + "windSpeedMax80mMPH": 21, + "windDirMin80m": "NNE", + "windDirMin80mDEG": 15, + "windSpeedMin80mKTS": 10, + "windSpeedMin80mKPH": 18, + "windSpeedMin80mMPH": 11, + "sky": 27, + "cloudsCoded": "FW", + "weather": "Mostly Sunny", + "weatherCoded": [], + "weatherPrimary": "Mostly Sunny", + "weatherPrimaryCoded": "::FW", + "icon": "fair.png", + "visibilityKM": 24.135, + "visibilityMI": 15, + "uvi": null, + "solradWM2": 4740, + "solradMinWM2": 0, + "solradMaxWM2": 743, + "isDay": true, + "maxCoverage": "", + "sunrise": 1665546895, + "sunset": 1665588484, + "sunriseISO": "2022-10-12T05:54:55+02:00", + "sunsetISO": "2022-10-12T17:28:04+02:00" + } ], - "description": "Participants in the main CLEAR SYNERGY trial are randomized to colchicine/spironolactone versus placebo in a 2x2 factorial design. The substudy is interested in the evaluation of biospecimens obtained from patients in the colchicine vs placebo group.", - "intervention_name": "Colchicine Pill" - }, - "patient_data": { - "sharing_ipd": "No" - }, - "verification_date": "September 2019" - } + "profile": { + "tz": "Africa/Cairo", + "elevM": 23, + "elevFT": 75 + } + } + ] } \ No newline at end of file diff --git a/src/test/resources/Issue593.xml b/src/test/resources/Issue593.xml index bf78f3b..0c6c038 100644 --- a/src/test/resources/Issue593.xml +++ b/src/test/resources/Issue593.xml @@ -1,169 +1,691 @@ - - - - - ClinicalTrials.gov processed this data on July 19, 2020 - Link to the current ClinicalTrials.gov record. - https://clinicaltrials.gov/show/NCT03874338 - - - 18-01323 - 1R01HL146206 - NCT03874338 - - CLEAR SYNERGY Neutrophil Substudy - Studies on the Effects of Colchicine on Neutrophil Biology in Acute Myocardial Infarction: A Substudy of the CLEAR SYNERGY (OASIS 9) Trial - - - NYU Langone Health - Other - - - Population Health Research Institute - Other - - - National Heart, Lung, and Blood Institute (NHLBI) - NIH - - - NYU Langone Health - - No - No - No - - - - CLEAR SYNERGY is an international multi center 2x2 randomized placebo controlled trial of - - - Recruiting - March 4, 2019 - February 1, 2022 - February 1, 2021 - Observational - No - - Other - Prospective - - - soluble L-selectin - between baseline and 3 months - Change in soluble L-selectin between baseline and 3 mo after STEMI in the placebo vs. colchicine groups. - - - Other soluble markers of neutrophil activity - between baseline and 3 months - Other markers of neutrophil activity will be evaluated at baseline and 3 months after STEMI (myeloperoxidase, matrix metalloproteinase-9, neutrophil gelatinase-associated lipocalin, neutrophil elastase, intercellular/vascular cellular adhesion molecules) - - - Markers of systemic inflammation - between baseline and 3 months - Markers of systemic inflammation will be evaluated at baseline and 3 months after STEMI (high sensitive CRP, IL-1β) - - - Neutrophil-driven responses that may further propagate injury - between baseline and 3 months - Neutrophil-driven responses that may further propagate injury will be evaluated at baseline and 3 months after STEMI (neutrophil extracellular traps, neutrophil-derived microparticles) - - 2 - 670 - Neutrophils.Hypersegmented | Bld-Ser-Plas - STEMI - ST Elevation Myocardial Infarction - - Colchicine - - - Placebo - - - Drug - Colchicine Pill - Participants in the main CLEAR SYNERGY trial are randomized to colchicine/spironolactone versus placebo in a 2x2 factorial design. The substudy is interested in the evaluation of biospecimens obtained from patients in the colchicine vs placebo group. - Colchicine - Placebo - - - - - Patients who are randomized to the drug RCT portion of the CLEAR SYNERGY (OASIS 9) trial - - - Non-Probability Sample - - - Inclusion Criteria: - - - All - 19 Years - 110 Years - No - - - Binita Shah, MD - Principal Investigator - NYU School of Medicine - - - Fatmira Curovic - 646-501-9648 - fatmira.curovic@nyumc.org - - - Binita Shah, MD - - - - NYU School of Medicine -
- New York - New York - 10016 - United States -
-
- Recruiting - - Fatmira Curovic - 646-501-9648 - fatmira.curovic@nyumc.org - - - Binita Shah, MD - -
- - United States - - September 2019 - March 12, 2019 - March 12, 2019 - March 14, 2019 - September 10, 2019 - September 10, 2019 - September 12, 2019 - - Principal Investigator - NYU Langone Health - Binita Shah - Assistant Professor of Medicine - - - - Myocardial Infarction - ST Elevation Myocardial Infarction - Infarction - - - - Colchicine - - - No - - -
+true + + + 31.25 + 30.063 + + + 23 + Africa/Cairo + 75 + + + 2022-10-06T07:00:00+02:00 + E + 95 + 21 + 15 + 10 + 353 + N + 2022-10-06T05:51:14+02:00 + null + 9 + null + 66 + 0 + Mostly Sunny + 2022-10-06T17:35:02+02:00 + 32 + 77 + N + 89 + 0 + 22 + 25 + 25 + Mostly Sunny + 41 + 58 + 41 + 22 + 14 + 0 + 22 + 353 + 16 + 8 + 70 + 2022-10-06T07:00:00+02:00 + 10 + 778 + 25 + 15 + ::FW + 1665028274 + 78 + N + + fair.png + 21 + 17 + FW + 70 + 29 + 63 + 12 + 0 + 0 + NNW + 13 + 22 + 11 + 32 + 1015 + 24.135 + 1665032400 + 90 + null + 11 + 0 + 1 + 343 + 21 + 2 + 63 + 1 + 26 + 6 + NNW + 17 + 29.97 + 80 + null + true + 19 + 52 + 5 + 1665070502 + 5608 + 9 + 25 + 77 + 6 + 40 + 342 + null + + + 2022-10-07T07:00:00+02:00 + NNW + 347 + 19 + 15 + 8 + 325 + NW + 2022-10-07T05:51:50+02:00 + null + 7 + null + 66 + 0 + Mostly Sunny + 2022-10-07T17:33:51+02:00 + 29 + 77 + NNW + 85 + 0 + 30 + 23 + 23 + Mostly Sunny + 37 + 54 + 37 + 20 + 12 + 0 + 20 + 325 + 13 + 6 + 67 + 2022-10-07T07:00:00+02:00 + 6 + 742 + 24 + 10 + ::FW + 1665114710 + 76 + NW + + fair.png + 19 + 15 + FW + 67 + 30 + 60 + 6 + 0 + 0 + WNW + 6 + 10 + 347 + 30 + 1014 + 24.135 + 1665118800 + 86 + null + 10 + 0 + 1 + 316 + 16 + 2 + 60 + 1 + 24 + 6 + NW + 15 + 29.95 + 76 + null + true + 19 + 50 + 1 + 1665156831 + 5486 + 2 + 18 + 77 + 1 + 29 + 298 + null + + + 2022-10-08T07:00:00+02:00 + NW + 309 + 19 + 15 + 8 + 21 + NNE + 2022-10-08T05:52:26+02:00 + null + 7 + null + 66 + 0 + Partly Cloudy + 2022-10-08T17:32:41+02:00 + 30 + 76 + NW + 86 + 0 + 47 + 19 + 19 + Partly Cloudy + 31 + 56 + 31 + 17 + 13 + 0 + 17 + 21 + 13 + 5 + 67 + 2022-10-08T07:00:00+02:00 + 5 + 682 + 25 + 9 + ::SC + 1665201146 + 76 + NNE + + pcloudy.png + 19 + 15 + SC + 67 + 32 + 59 + 5 + 0 + 0 + WNW + 5 + 9 + 309 + 30 + 1014 + 24.135 + 1665205200 + 87 + null + 11 + 0 + 1 + 322 + 17 + 2 + 59 + 1 + 25 + 7 + NW + 15 + 29.94 + 76 + null + true + 19 + 52 + 1 + 1665243161 + 4785 + 2 + 20 + 76 + 1 + 32 + 301 + null + + + 2022-10-09T07:00:00+02:00 + NW + 316 + 20 + 15 + 9 + 356 + N + 2022-10-09T05:53:03+02:00 + null + 8 + null + 67 + 0 + Partly Cloudy + 2022-10-09T17:31:31+02:00 + 30 + 86 + NW + 86 + 0 + 47 + 23 + 23 + Partly Cloudy + 36 + 57 + 36 + 20 + 14 + 0 + 20 + 356 + 14 + 5 + 67 + 2022-10-09T07:00:00+02:00 + 6 + 726 + 25 + 9 + ::SC + 1665287583 + 77 + N + + pcloudy.png + 20 + 17 + SC + 67 + 31 + 63 + 5 + 0 + 0 + NNW + 6 + 9 + 316 + 31 + 1016 + 24.135 + 1665291600 + 87 + null + 11 + 0 + 2 + 354 + 19 + 4 + 63 + 2 + 25 + 7 + N + 17 + 29.99 + 77 + null + true + 19 + 52 + 2 + 1665329491 + 4768 + 4 + 22 + 86 + 2 + 36 + 343 + null + + + 2022-10-10T07:00:00+02:00 + E + 91 + 21 + 15 + 9 + 358 + N + 2022-10-10T05:53:40+02:00 + null + 8 + null + 70 + 0 + Partly Cloudy + 2022-10-10T17:30:21+02:00 + 30 + 75 + N + 86 + 0 + 64 + 22 + 22 + Partly Cloudy + 36 + 58 + 36 + 19 + 14 + 0 + 19 + 358 + 15 + 7 + 69 + 2022-10-10T07:00:00+02:00 + 8 + 597 + 26 + 13 + ::SC + 1665374020 + 78 + N + + pcloudy.png + 21 + 16 + SC + 69 + 35 + 61 + 7 + 0 + 0 + N + 8 + 13 + 8 + 31 + 1017 + 24.135 + 1665378000 + 87 + null + 13 + 0 + 2 + 10 + 16 + 4 + 61 + 2 + 25 + 6 + N + 16 + 30.03 + 78 + null + true + 21 + 55 + 2 + 1665415821 + 4494 + 4 + 19 + 75 + 2 + 30 + 10 + null + + + 2022-10-11T07:00:00+02:00 + NNE + 13 + 22 + 15 + 18 + 13 + NNE + 2022-10-11T05:54:18+02:00 + null + 15 + null + 70 + 0 + Sunny + 2022-10-11T17:29:13+02:00 + 31 + 71 + NNE + 87 + 0 + 0 + 19 + 19 + Sunny + 31 + 55 + 31 + 17 + 13 + 0 + 17 + 14 + 28 + 9 + 72 + 2022-10-11T07:00:00+02:00 + 11 + 758 + 26 + 18 + ::CL + 1665460458 + 78 + NNE + + sunny.png + 22 + 17 + CL + 72 + 30 + 62 + 10 + 0 + 0 + NNE + 12 + 19 + 16 + 31 + 1015 + 24.135 + 1665464400 + 87 + null + 11 + 0 + 7 + 28 + 15 + 14 + 62 + 8 + 26 + null + NNE + 17 + 29.98 + 79 + null + true + 21 + 51 + 8 + 1665502153 + 5450 + 15 + 17 + 71 + 9 + 28 + 28 + null + + + 2022-10-12T07:00:00+02:00 + NNE + 15 + 22 + 15 + 16 + 12 + NNE + 2022-10-12T05:54:55+02:00 + null + 14 + null + 69 + 0 + Mostly Sunny + 2022-10-12T17:28:04+02:00 + 31 + 68 + NNE + 88 + 0 + 27 + 21 + 21 + Mostly Sunny + 33 + 55 + 33 + 18 + 13 + 0 + 18 + 12 + 26 + 10 + 72 + 2022-10-12T07:00:00+02:00 + 11 + 743 + 26 + 18 + ::FW + 1665546895 + 79 + NNE + + fair.png + 22 + 16 + FW + 72 + 29 + 60 + 10 + 0 + 0 + E + 12 + 19 + 15 + 31 + 1014 + 24.135 + 1665550800 + 88 + null + 11 + 0 + 7 + 96 + 15 + 13 + 60 + 8 + 26 + null + E + 16 + 29.95 + 80 + null + true + 21 + 51 + 8 + 1665588484 + 4740 + 15 + 17 + 68 + 9 + 28 + 96 + null + + day + + eg + cairo + qh + + +null From a2c0562e04badc81259cf1d703a52aba7965b464 Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 7 Oct 2022 15:04:09 +0100 Subject: [PATCH 14/48] Removed unused import --- src/test/java/org/json/junit/XMLTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 4c46cf1..bfb916c 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -18,7 +18,6 @@ import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; From 9cb8e153bf38e12554455d13c5c8e7c348236a98 Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 7 Oct 2022 17:57:07 +0100 Subject: [PATCH 15/48] Added JavaDocs --- src/main/java/org/json/XML.java | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 28a292f..bb614d4 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -755,6 +755,23 @@ public static String toString(final Object object, final String tagName, final X return toString(object, tagName, config, 0, 0); } + /** + * Convert a JSONObject into a well-formed, element-normal XML string, + * either pretty print or single-lined depending on indent factor. + * + * @param object + * A JSONObject. + * @param tagName + * The optional name of the enclosing tag. + * @param config + * Configuration that can control output to XML. + * @param indentFactor + * The number of spaces to add to each level of indentation. + * @param indent + * The current ident level in spaces. + * @return + * @throws JSONException + */ private static String toString(final Object object, final String tagName, final XMLParserConfiguration config, int indentFactor, int indent) throws JSONException { StringBuilder sb = new StringBuilder(); @@ -934,6 +951,13 @@ public static String toString(final Object object, final String tagName, final X return toString(object, tagName, config, indentFactor, 0); } + /** + * Return a String consisting of a number of space characters specified by indent + * + * @param indent + * The number of spaces to be appended to the String. + * @return + */ private static final String indent(int indent) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indent; i++) { From 7aba3ac941701aad36e70d3ae3c77780aeae73ec Mon Sep 17 00:00:00 2001 From: Dean Date: Mon, 10 Oct 2022 11:09:42 +0100 Subject: [PATCH 16/48] System line seperator now being used in JUnit test --- src/test/java/org/json/junit/XMLTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index bfb916c..88ea9ce 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1232,7 +1232,7 @@ public void testIndentComplicatedJsonObjectWithArrayAndWithConfig(){ for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) { expected.append(buffer, 0, numRead); } - assertEquals(expected.toString(), actualString); + assertEquals(expected.toString().replaceAll("\\n|\\r\\n", System.getProperty("line.separator")), actualString); } finally { if (xmlStream != null) { xmlStream.close(); From 85495facbd4a9e2e13f6ef50a35a4b35535c96b5 Mon Sep 17 00:00:00 2001 From: Dean Date: Mon, 10 Oct 2022 11:12:35 +0100 Subject: [PATCH 17/48] Corrected test --- src/test/java/org/json/junit/XMLTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 88ea9ce..937658e 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1232,7 +1232,7 @@ public void testIndentComplicatedJsonObjectWithArrayAndWithConfig(){ for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) { expected.append(buffer, 0, numRead); } - assertEquals(expected.toString().replaceAll("\\n|\\r\\n", System.getProperty("line.separator")), actualString); + assertEquals(expected.toString(), actualString.replaceAll("\\n|\\r\\n", System.getProperty("line.separator"))); } finally { if (xmlStream != null) { xmlStream.close(); From a2d3d3c9b5dca8359ca6aca2662f3b1b5f023982 Mon Sep 17 00:00:00 2001 From: Bharati Kulkarni Date: Tue, 11 Oct 2022 14:33:43 -0500 Subject: [PATCH 18/48] Fix Flaky Test --- src/test/java/org/json/junit/JSONPointerTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/json/junit/JSONPointerTest.java b/src/test/java/org/json/junit/JSONPointerTest.java index d88803e..45c7dbd 100644 --- a/src/test/java/org/json/junit/JSONPointerTest.java +++ b/src/test/java/org/json/junit/JSONPointerTest.java @@ -72,8 +72,10 @@ public void queryByEmptyKey() { @Test public void queryByEmptyKeySubObject() { - assertEquals( "{\"\":\"empty key of an object with an empty key\",\"subKey\":\"Some" + - " other value\"}", query("/obj/").toString()); + JSONObject json = new JSONObject("{\"\":\"empty key of an object with an empty key\",\"subKey\":\"Some" + + " other value\"}"); + JSONObject obj = (JSONObject) query("/obj/"); + assertTrue(json.similar(obj)); } @Test From 23d5e52a53b2b10825fbd42d2323285d6e84bb05 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 28 Oct 2022 08:45:54 +0300 Subject: [PATCH 19/48] feature/update-release-for-JSONMap-Change adding breaking change for JSONMap to corresponding release --- docs/RELEASES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/RELEASES.md b/docs/RELEASES.md index 0d49331..3fbab78 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -20,6 +20,7 @@ and artifactId "json". For example: 20190722 Recent commits 20180813 POM change to include Automatic-Module-Name (#431) + JSONObject(Map) now throws an exception if any of a map keys are null (#405) 20180130 Recent commits From c798c76ddd46d79140c27ecb15146c588ea34945 Mon Sep 17 00:00:00 2001 From: Niranjani Date: Sun, 30 Oct 2022 22:10:38 +0530 Subject: [PATCH 20/48] move javadoc comments above the interface definition to make it visible Fix #670 --- src/main/java/org/json/JSONPropertyIgnore.java | 6 +++--- src/main/java/org/json/JSONPropertyName.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/json/JSONPropertyIgnore.java b/src/main/java/org/json/JSONPropertyIgnore.java index 7c5fa53..d3a5bc5 100644 --- a/src/main/java/org/json/JSONPropertyIgnore.java +++ b/src/main/java/org/json/JSONPropertyIgnore.java @@ -11,13 +11,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Documented -@Retention(RUNTIME) -@Target({METHOD}) /** * Use this annotation on a getter method to override the Bean name * parser for Bean -> JSONObject mapping. If this annotation is * present at any level in the class hierarchy, then the method will * not be serialized from the bean into the JSONObject. */ +@Documented +@Retention(RUNTIME) +@Target({METHOD}) public @interface JSONPropertyIgnore { } diff --git a/src/main/java/org/json/JSONPropertyName.java b/src/main/java/org/json/JSONPropertyName.java index a66f4ad..4391bb7 100644 --- a/src/main/java/org/json/JSONPropertyName.java +++ b/src/main/java/org/json/JSONPropertyName.java @@ -11,14 +11,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Documented -@Retention(RUNTIME) -@Target({METHOD}) /** * Use this annotation on a getter method to override the Bean name * parser for Bean -> JSONObject mapping. A value set to empty string "" * will have the Bean parser fall back to the default field name processing. */ +@Documented +@Retention(RUNTIME) +@Target({METHOD}) public @interface JSONPropertyName { /** * @return The name of the property as to be used in the JSON Object. From 5369442671090b0e50b8ed95c6e628fefc2fc2aa Mon Sep 17 00:00:00 2001 From: ASAlisha <115576463+ASAlishaa@users.noreply.github.com> Date: Mon, 14 Nov 2022 03:26:18 +0530 Subject: [PATCH 21/48] Added new resource to the repos Added new useful JSON resource. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5181b8b..003c295 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ JSON in Java [package org.json] [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. +The JSON-Java package is a reference implementation that demonstrates how to parse [JSON documents](https://www.interviewbit.com/problems/pretty-json/) into Java objects and how to generate new JSON documents from the Java classes. Project goals include: * Reliable and consistent results From b732188e4e66824227cb299e76b6b6aa57cdbe53 Mon Sep 17 00:00:00 2001 From: ASAlisha <115576463+ASAlishaa@users.noreply.github.com> Date: Tue, 15 Nov 2022 16:31:05 +0530 Subject: [PATCH 22/48] Added new resource to this repos. Added resource in the correct format. --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 003c295..2fdda56 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ JSON in Java [package org.json] [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](https://www.interviewbit.com/problems/pretty-json/) into Java objects and how to generate new JSON documents from the Java classes. +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 @@ -102,6 +102,10 @@ For more information, please see [NOTES.md](https://github.com/stleary/JSON-java For more information on files, please see [FILES.md](https://github.com/stleary/JSON-java/blob/master/docs/FILES.md) +# Interview Practice Problems + +[Pretty Json](https://www.interviewbit.com/problems/pretty-json/) - Problem asked in Microsoft & Facebook + # Release history: For the release history, please see [RELEASES.md](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) From 3b097d051ac2182f6dcf38ed3bd8d6e63d4e5d50 Mon Sep 17 00:00:00 2001 From: 6d64 <61372877+6d64@users.noreply.github.com> Date: Thu, 1 Dec 2022 03:21:26 +1100 Subject: [PATCH 23/48] Revert pull 707 - interviewbit spam Reverted commit that was added by a bot adding interviewbit spam to repos on github --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 2fdda56..5181b8b 100644 --- a/README.md +++ b/README.md @@ -102,10 +102,6 @@ For more information, please see [NOTES.md](https://github.com/stleary/JSON-java For more information on files, please see [FILES.md](https://github.com/stleary/JSON-java/blob/master/docs/FILES.md) -# Interview Practice Problems - -[Pretty Json](https://www.interviewbit.com/problems/pretty-json/) - Problem asked in Microsoft & Facebook - # Release history: For the release history, please see [RELEASES.md](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) From 038775c36dbab9d4e56ff7a9b549149fa8e502df Mon Sep 17 00:00:00 2001 From: DinjerChang Date: Mon, 30 Jan 2023 15:56:42 -0800 Subject: [PATCH 24/48] milestone 2 second function done --- src/main/java/M2Test.java | 45 +++ src/main/java/org/json/XML.java | 683 +++++++++++++++++++++++++------- 2 files changed, 581 insertions(+), 147 deletions(-) create mode 100644 src/main/java/M2Test.java diff --git a/src/main/java/M2Test.java b/src/main/java/M2Test.java new file mode 100644 index 0000000..92aecd5 --- /dev/null +++ b/src/main/java/M2Test.java @@ -0,0 +1,45 @@ +import java.io.StringReader; + +import org.json.JSONException; +import org.json.JSONPointer; +import org.json.JSONObject; +import org.json.XML; + +public class M2Test { + public static void main(String[] args) { + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
\n" + + " Verano Road\n" + + " 92617\n" + + "
\n" + + "
"; + + try { +// JSONObject test = XML.toJSONObject(xmlString); +// System.out.println(test); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/")); + System.out.println(jobj); + + } catch (JSONException e) { + System.out.println(e); + } + + System.out.println("-----------------------"); + + try { +// JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); +// System.out.println("Given replacement: " + replacement); +// JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/0"), replacement); +// System.out.println(jobj); + } catch (JSONException e) { + System.out.println(e); + } + } +} diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index bb614d4..a798458 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -8,7 +8,9 @@ import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; /** @@ -21,31 +23,49 @@ @SuppressWarnings("boxing") public class XML { - /** The Character '&'. */ + /** + * The Character '&'. + */ public static final Character AMP = '&'; - /** The Character '''. */ + /** + * The Character '''. + */ public static final Character APOS = '\''; - /** The Character '!'. */ + /** + * The Character '!'. + */ public static final Character BANG = '!'; - /** The Character '='. */ + /** + * The Character '='. + */ public static final Character EQ = '='; - /** The Character
{@code '>'. }
*/ + /** + * The Character
{@code '>'. }
+ */ public static final Character GT = '>'; - /** The Character '<'. */ + /** + * The Character '<'. + */ public static final Character LT = '<'; - /** The Character '?'. */ + /** + * The Character '?'. + */ public static final Character QUEST = '?'; - /** The Character '"'. */ + /** + * The Character '"'. + */ public static final Character QUOT = '"'; - /** The Character '/'. */ + /** + * The Character '/'. + */ public static final Character SLASH = '/'; /** @@ -64,7 +84,7 @@ public class XML { * which is available in Java8 and above. * * @see http://stackoverflow.com/a/21791059/6030888 + * "http://stackoverflow.com/a/21791059/6030888">http://stackoverflow.com/a/21791059/6030888 */ private static Iterable codePointIterator(final String string) { return new Iterable() { @@ -98,7 +118,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;
@@ -106,37 +126,36 @@ public void remove() {
      * ' (single quote / apostrophe) is replaced by &apos;
      * }
* - * @param string - * The string to be escaped. + * @param string The string to be escaped. * @return The escaped string. */ public static String escape(String string) { StringBuilder sb = new StringBuilder(string.length()); for (final int cp : codePointIterator(string)) { switch (cp) { - case '&': - sb.append("&"); - break; - case '<': - sb.append("<"); - break; - case '>': - sb.append(">"); - break; - case '"': - sb.append("""); - break; - case '\'': - sb.append("'"); - break; - default: - if (mustEscape(cp)) { - sb.append("&#x"); - sb.append(Integer.toHexString(cp)); - sb.append(';'); - } else { - sb.appendCodePoint(cp); - } + case '&': + sb.append("&"); + break; + case '<': + sb.append("<"); + break; + case '>': + sb.append(">"); + break; + case '"': + sb.append("""); + break; + case '\'': + sb.append("'"); + break; + default: + if (mustEscape(cp)) { + sb.append("&#x"); + sb.append(Integer.toHexString(cp)); + sb.append(';'); + } else { + sb.appendCodePoint(cp); + } } } return sb.toString(); @@ -159,20 +178,19 @@ private static boolean mustEscape(int cp) { && cp != 0x9 && cp != 0xA && cp != 0xD - ) || !( + ) || !( // valid the range of acceptable characters that aren't control (cp >= 0x20 && cp <= 0xD7FF) - || (cp >= 0xE000 && cp <= 0xFFFD) - || (cp >= 0x10000 && cp <= 0x10FFFF) - ) - ; + || (cp >= 0xE000 && cp <= 0xFFFD) + || (cp >= 0x10000 && cp <= 0x10FFFF) + ) + ; } /** * Removes XML escapes from the string. * - * @param string - * string to remove escapes from + * @param string string to remove escapes from * @return string with converted entities */ public static String unescape(String string) { @@ -203,8 +221,7 @@ public static String unescape(String string) { * Throw an exception if the string contains whitespace. Whitespace is not * allowed in tagNames and attributes. * - * @param string - * A string. + * @param string A string. * @throws JSONException Thrown if the string contains whitespace or is empty. */ public static void noSpace(String string) throws JSONException { @@ -223,12 +240,9 @@ public static void noSpace(String string) throws JSONException { /** * 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 x The XMLTokener containing the source string. + * @param context The JSONObject that will include the new material. + * @param name The tag name. * @return true if the close tag is processed. * @throws JSONException */ @@ -253,11 +267,13 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP // << token = x.nextToken(); + System.out.println("token: " + token); // "); @@ -266,6 +282,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP x.back(); } else if (c == '[') { token = x.nextToken(); + System.out.println("token :[ " + token); if ("CDATA".equals(token)) { if (x.next() == '[') { string = x.nextCDATA(); @@ -292,11 +309,11 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP } else if (token == QUEST) { // "); + x.skipPast("?>"); // return false; } else if (token == SLASH) { - // Close tag and - for (;;) { + for (; ; ) { token = x.nextContent(); if (token == null) { if (tagName != null) { @@ -391,7 +408,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP } else if (token instanceof String) { string = (String) token; if (string.length() > 0) { - if(xmlXsiTypeConverter != null) { + if (xmlXsiTypeConverter != null) { jsonObject.accumulate(config.getcDataTagName(), stringToValue(string, xmlXsiTypeConverter)); } else { @@ -423,7 +440,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP context.accumulate(tagName, jsonObject); } } - + return false; } } @@ -437,12 +454,13 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP /** * This method tries to convert the given string value to the target object - * @param string String to convert + * + * @param string String to convert * @param typeConverter value converter to convert string to integer, boolean e.t.c * @return JSON value of this string or the string */ public static Object stringToValue(String string, XMLXsiTypeConverter typeConverter) { - if(typeConverter != null) { + if (typeConverter != null) { return typeConverter.convert(string); } return stringToValue(string); @@ -487,7 +505,7 @@ public static Object stringToValue(String string) { } return string; } - + /** * direct copy of {@link JSONObject#stringToNumber(String)} to maintain Android support. */ @@ -501,7 +519,7 @@ private static Number stringToNumber(final String val) throws NumberFormatExcept // keep that by forcing a decimal. try { BigDecimal bd = new BigDecimal(val); - if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) { + if (initial == '-' && BigDecimal.ZERO.compareTo(bd) == 0) { return Double.valueOf(-0.0); } return bd; @@ -509,48 +527,48 @@ private static Number stringToNumber(final String val) throws NumberFormatExcept // this is to support "Hex Floats" like this: 0x1.0P-1074 try { Double d = Double.valueOf(val); - if(d.isNaN() || d.isInfinite()) { - throw new NumberFormatException("val ["+val+"] is not a valid number."); + if (d.isNaN() || d.isInfinite()) { + throw new NumberFormatException("val [" + val + "] is not a valid number."); } return d; } catch (NumberFormatException ignore) { - throw new NumberFormatException("val ["+val+"] is not a valid number."); + throw new NumberFormatException("val [" + val + "] is not a valid number."); } } } // block items like 00 01 etc. Java number parsers treat these as Octal. - if(initial == '0' && val.length() > 1) { + if (initial == '0' && val.length() > 1) { char at1 = val.charAt(1); - if(at1 >= '0' && at1 <= '9') { - throw new NumberFormatException("val ["+val+"] is not a valid number."); + if (at1 >= '0' && at1 <= '9') { + throw new NumberFormatException("val [" + val + "] is not a valid number."); } } else if (initial == '-' && val.length() > 2) { char at1 = val.charAt(1); char at2 = val.charAt(2); - if(at1 == '0' && at2 >= '0' && at2 <= '9') { - throw new NumberFormatException("val ["+val+"] is not a valid number."); + if (at1 == '0' && at2 >= '0' && at2 <= '9') { + throw new NumberFormatException("val [" + val + "] is not a valid number."); } } // 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 // long lived. BigInteger bi = new BigInteger(val); - if(bi.bitLength() <= 31){ + if (bi.bitLength() <= 31) { return Integer.valueOf(bi.intValue()); } - if(bi.bitLength() <= 63){ + if (bi.bitLength() <= 63) { return Long.valueOf(bi.longValue()); } return bi; } - throw new NumberFormatException("val ["+val+"] is not a valid number."); + throw new NumberFormatException("val [" + val + "] is not a valid number."); } - + /** * direct copy of {@link JSONObject#isDecimalNotation(String)} to maintain Android support. */ @@ -568,12 +586,11 @@ 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. * - * @param string - * The source string. + * @param string The source string. * @return A JSONObject containing the structured data from the XML string. * @throws JSONException Thrown if there is an errors while parsing the string */ @@ -581,6 +598,7 @@ public static JSONObject toJSONObject(String string) throws JSONException { return toJSONObject(string, XMLParserConfiguration.ORIGINAL); } + /** * Convert a well-formed (but not necessarily valid) XML into a * JSONObject. Some information may be lost in this transformation because @@ -589,7 +607,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. * @@ -600,7 +618,6 @@ public static JSONObject toJSONObject(String string) throws JSONException { public static JSONObject toJSONObject(Reader reader) throws JSONException { return toJSONObject(reader, XMLParserConfiguration.ORIGINAL); } - /** * Convert a well-formed (but not necessarily valid) XML into a * JSONObject. Some information may be lost in this transformation because @@ -612,18 +629,18 @@ public static JSONObject toJSONObject(Reader reader) throws JSONException { * "content" member. Comments, prologs, DTDs, and
{@code
      * <[ [ ]]>}
* are ignored. - * + *

* All values are converted as strings, for 1, 01, 29.0 will not be coerced to * numbers but will instead be the exact value as seen in the XML document. * - * @param reader The XML source reader. + * @param reader The XML source reader. * @param keepStrings If true, then values will not be coerced into boolean - * or numeric values and will instead be left as strings + * or numeric values and will instead be left as strings * @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, boolean keepStrings) throws JSONException { - if(keepStrings) { + if (keepStrings) { return toJSONObject(reader, XMLParserConfiguration.KEEP_STRINGS); } return toJSONObject(reader, XMLParserConfiguration.ORIGINAL); @@ -640,7 +657,7 @@ public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws * "content" member. Comments, prologs, DTDs, and

{@code
      * <[ [ ]]>}
* are ignored. - * + *

* All values are converted as strings, for 1, 01, 29.0 will not be coerced to * numbers but will instead be the exact value as seen in the XML document. * @@ -661,6 +678,186 @@ public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration conf return jo; } +// public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JSONException { +// XMLTokener tokener = new XMLTokener(reader); +// boolean isPathReached = false; +// String[] keyPath = path.toString().substring(1).split("/"); +// int keyNums = keyPath.length; +// int keyIdx = 0; +// int rightArrowIdx; +// List tags = new ArrayList<>(); +// +// while (tokener.more()) { +// tokener.skipPast("<"); +// Object line = tokener.nextContent(); +// System.out.println(line.toString()); +// if (line instanceof String) { +// String lineString = (String) line; +// rightArrowIdx = lineString.indexOf(">"); +// if (!isPathReached) { +// // found start tag +// +// if (keyPath[keyIdx].equals(lineString.substring(0,rightArrowIdx))) { +// // final start tag +// if (keyIdx == keyNums - 1) { +// isPathReached = true; +// tags.add("<" + lineString); +// } else { +// keyIdx++; +// } +// } +// } else { +// // found last end tag then break, optimize the original way +// if (keyIdx == keyNums - 1 && keyPath[keyIdx].equals(lineString.substring(1,rightArrowIdx))){ +// tags.add("<" + lineString); +// break; +// +// } else { +// // keep adding content +// tags.add("<" + lineString); +// } +// } +// } +// } +// +// if (!isPathReached){ +// System.out.println("Invalid Pointer"); +// return null; +// } +// StringBuilder output = new StringBuilder(); +// for (String ele: tags){ +// output.append(ele); +// } +// System.out.print("tags: "); +// System.out.println(tags); +// return toJSONObject(output.toString()); +// } + + public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JSONException { + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(reader); + String desiredPath = path.toString(); + while (x.more()) { + x.skipPast("<"); + if(x.more()) { + customParse(x, jo, null, XMLParserConfiguration.ORIGINAL,desiredPath,""); + } + } + return jo; + } + public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) throws JSONException { + Object obj = toJSONObject(reader); + String keyPath = path.toString(); + if (keyPath.charAt(0) == '/'){ + keyPath = keyPath.substring(1); + } + if (keyPath.charAt(keyPath.length()-1)=='/'){ + keyPath = keyPath.substring(0,keyPath.length()-1); + } + System.out.println("keyPath: " + keyPath); + String[] keys = keyPath.split("/"); + for (String s: keys){ + System.out.println("key: "+ s); + } + + Object current = obj; + for (int i = 0; i < keys.length - 1; i++) { + + if (current instanceof JSONObject) { + if (((JSONObject) current).has(keys[i])){ + current = ((JSONObject) current).get(keys[i]); + }else{ + System.out.println("Invalid Path"); + return (JSONObject) obj; + } + + } else if (current instanceof JSONArray) { + if (((JSONArray) current).isNull(Integer.parseInt (keys[i]))){ + System.out.println("Invalid Path"); + return (JSONObject) obj; + + }else{ + current = ((JSONArray) current).get(Integer.parseInt(keys[i])); + } + + } + System.out.println("current: " + current); + } + + + // replace the sub-object with custom object + if (current instanceof JSONObject) { + if (((JSONObject) current).has(keys[keys.length-1])){ + ((JSONObject) current).put(keys[keys.length - 1], replacement); + }else{ + System.out.println("Invalid Path"); + return (JSONObject)obj; + } + + } else if (current instanceof JSONArray) { + if (((JSONArray) current).isNull(Integer.parseInt (keys[keys.length-1]))){ + System.out.println("Invalid Path"); + return (JSONObject) obj; + }else{ + ((JSONArray) current).put(Integer.parseInt(keys[keys.length - 1]), replacement); + } + + } + return (JSONObject) obj; + } + + private static JSONObject updateJsonObjectWithKeyString(JSONObject currentJson, String key, + String pathPrefix, JSONObject replacement) { + // get current object's keys + Iterator iter = currentJson.keys(); + String currentKey; + String globalKeyPath; + while (iter.hasNext()) { + currentKey = (String) iter.next(); + globalKeyPath = pathPrefix + '/' + currentKey; + + // check if match the key. + if (globalKeyPath.equals(key)) { + currentJson.put(currentKey, replacement); + return currentJson; + } + + // expanding the nested structure. + if (currentJson.optJSONObject(currentKey) != null) { + updateJsonObjectWithKeyString(currentJson.getJSONObject(currentKey), key, globalKeyPath, replacement); + } + + if (currentJson.optJSONArray(currentKey) != null) { + JSONArray arr = currentJson.getJSONArray(currentKey); + String[] paths = key.split("/"); + + // find the selected index + String[] pres = globalKeyPath.split("/"); + for (int i = 1; i < paths.length; ++i) { + if (paths[i - 1].equals(pres[pres.length - 1])) { + // if has reached the end + if (i == paths.length - 1) { + arr.put(Integer.parseInt(paths[paths.length - 1]), replacement); + return currentJson; + } else { + if (paths[i].matches("[0-9]+")) { + // update key path with array index + int selectedIndex = Integer.parseInt(paths[i]); + globalKeyPath = globalKeyPath + "/" + paths[i]; // update keypath properly + updateJsonObjectWithKeyString(arr.getJSONObject(selectedIndex), + key, globalKeyPath, replacement); + } else { + throw new Error("unexpected reach."); + } + } + } + } + } + + } + + return currentJson; + } /** * Convert a well-formed (but not necessarily valid) XML string into a * JSONObject. Some information may be lost in this transformation because @@ -669,17 +866,16 @@ 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. - * + *

* All values are converted as strings, for 1, 01, 29.0 will not be coerced to * numbers but will instead be the exact value as seen in the XML document. * - * @param string - * The source string. + * @param string The source string. * @param keepStrings If true, then values will not be coerced into boolean - * or numeric values and will instead be left as strings + * or numeric values and will instead be left as strings * @return A JSONObject containing the structured data from the XML string. * @throws JSONException Thrown if there is an errors while parsing the string */ @@ -695,15 +891,14 @@ 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. - * + *

* All values are converted as strings, for 1, 01, 29.0 will not be coerced to * numbers but will instead be the exact value as seen in the XML document. * - * @param string - * The source string. + * @param string The source string. * @param config 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 @@ -715,8 +910,7 @@ public static JSONObject toJSONObject(String string, XMLParserConfiguration conf /** * Convert a JSONObject into a well-formed, element-normal XML string. * - * @param object - * A JSONObject. + * @param object A JSONObject. * @return A string. * @throws JSONException Thrown if there is an error parsing the string */ @@ -727,10 +921,8 @@ public static String toString(Object object) throws JSONException { /** * Convert a JSONObject into a well-formed, element-normal XML string. * - * @param object - * A JSONObject. - * @param tagName - * The optional name of the enclosing tag. + * @param object A JSONObject. + * @param tagName The optional name of the enclosing tag. * @return A string. * @throws JSONException Thrown if there is an error parsing the string */ @@ -741,12 +933,9 @@ public static String toString(final Object object, final String tagName) { /** * Convert a JSONObject into a well-formed, element-normal XML string. * - * @param object - * A JSONObject. - * @param tagName - * The optional name of the enclosing tag. - * @param config - * Configuration that can control output to XML. + * @param object A JSONObject. + * @param tagName The optional name of the enclosing tag. + * @param config Configuration that can control output to XML. * @return A string. * @throws JSONException Thrown if there is an error parsing the string */ @@ -759,16 +948,11 @@ public static String toString(final Object object, final String tagName, final X * Convert a JSONObject into a well-formed, element-normal XML string, * either pretty print or single-lined depending on indent factor. * - * @param object - * A JSONObject. - * @param tagName - * The optional name of the enclosing tag. - * @param config - * Configuration that can control output to XML. - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @param indent - * The current ident level in spaces. + * @param object A JSONObject. + * @param tagName The optional name of the enclosing tag. + * @param config Configuration that can control output to XML. + * @param indentFactor The number of spaces to add to each level of indentation. + * @param indent The current ident level in spaces. * @return * @throws JSONException */ @@ -787,7 +971,7 @@ private static String toString(final Object object, final String tagName, final sb.append('<'); sb.append(tagName); sb.append('>'); - if(indentFactor > 0){ + if (indentFactor > 0) { sb.append("\n"); indent += indentFactor; } @@ -810,7 +994,7 @@ private static String toString(final Object object, final String tagName, final ja = (JSONArray) value; int jaLength = ja.length(); // don't use the new iterator API to maintain support for Android - for (int i = 0; i < jaLength; i++) { + for (int i = 0; i < jaLength; i++) { if (i > 0) { sb.append('\n'); } @@ -827,7 +1011,7 @@ private static String toString(final Object object, final String tagName, final ja = (JSONArray) value; int jaLength = ja.length(); // don't use the new iterator API to maintain support for Android - for (int i = 0; i < jaLength; i++) { + for (int i = 0; i < jaLength; i++) { Object val = ja.opt(i); if (val instanceof JSONArray) { sb.append('<'); @@ -846,7 +1030,7 @@ private static String toString(final Object object, final String tagName, final sb.append('<'); sb.append(key); sb.append("/>"); - if(indentFactor > 0){ + if (indentFactor > 0) { sb.append("\n"); } @@ -863,7 +1047,7 @@ private static String toString(final Object object, final String tagName, final sb.append("'); - if(indentFactor > 0){ + if (indentFactor > 0) { sb.append("\n"); } } @@ -871,15 +1055,15 @@ private static String toString(final Object object, final String tagName, final } - if (object != null && (object instanceof JSONArray || object.getClass().isArray())) { - if(object.getClass().isArray()) { + if (object != null && (object instanceof JSONArray || object.getClass().isArray())) { + if (object.getClass().isArray()) { ja = new JSONArray(object); } else { ja = (JSONArray) object; } int jaLength = ja.length(); // don't use the new iterator API to maintain support for Android - for (int i = 0; i < jaLength; i++) { + for (int i = 0; i < jaLength; i++) { Object val = ja.opt(i); // XML does not have good support for arrays. If an array // appears in a place where XML is lacking, synthesize an @@ -892,9 +1076,9 @@ private static String toString(final Object object, final String tagName, final string = (object == null) ? "null" : escape(object.toString()); - if(tagName == null){ + if (tagName == null) { return indent(indent) + "\"" + string + "\"" + ((indentFactor > 0) ? "\n" : ""); - } else if(string.length() == 0){ + } else if (string.length() == 0) { return indent(indent) + "<" + tagName + "/>" + ((indentFactor > 0) ? "\n" : ""); } else { return indent(indent) + "<" + tagName @@ -905,26 +1089,21 @@ private static String toString(final Object object, final String tagName, final /** * Convert a JSONObject into a well-formed, pretty printed element-normal XML string. * - * @param object - * A JSONObject. - * @param indentFactor - * The number of spaces to add to each level of indentation. + * @param object A JSONObject. + * @param indentFactor The number of spaces to add to each level of indentation. * @return A string. * @throws JSONException Thrown if there is an error parsing the string */ - public static String toString(Object object, int indentFactor){ + public static String toString(Object object, int indentFactor) { return toString(object, null, XMLParserConfiguration.ORIGINAL, indentFactor); } /** * Convert a JSONObject into a well-formed, pretty printed element-normal XML string. * - * @param object - * A JSONObject. - * @param tagName - * The optional name of the enclosing tag. - * @param indentFactor - * The number of spaces to add to each level of indentation. + * @param object A JSONObject. + * @param tagName The optional name of the enclosing tag. + * @param indentFactor The number of spaces to add to each level of indentation. * @return A string. * @throws JSONException Thrown if there is an error parsing the string */ @@ -935,14 +1114,10 @@ public static String toString(final Object object, final String tagName, int ind /** * Convert a JSONObject into a well-formed, pretty printed element-normal XML string. * - * @param object - * A JSONObject. - * @param tagName - * The optional name of the enclosing tag. - * @param config - * Configuration that can control output to XML. - * @param indentFactor - * The number of spaces to add to each level of indentation. + * @param object A JSONObject. + * @param tagName The optional name of the enclosing tag. + * @param config Configuration that can control output to XML. + * @param indentFactor The number of spaces to add to each level of indentation. * @return A string. * @throws JSONException Thrown if there is an error parsing the string */ @@ -954,8 +1129,7 @@ public static String toString(final Object object, final String tagName, final X /** * Return a String consisting of a number of space characters specified by indent * - * @param indent - * The number of spaces to be appended to the String. + * @param indent The number of spaces to be appended to the String. * @return */ private static final String indent(int indent) { @@ -965,4 +1139,219 @@ private static final String indent(int indent) { } return sb.toString(); } + + /** + * 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. + * @return true if the close tag is processed. + * @throws JSONException + */ + private static boolean customParse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, String desiredPath, String currPath) + throws JSONException { + // keep track of the path + 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(); + + // "); + 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 + 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) { + 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 + for (; ; ) { + 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) { + // Nested element + if (customParse(x, jsonObject, tagName, config,desiredPath, currPath)) { + 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) { + context.accumulate(tagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.accumulate(tagName, jsonObject); + } + } + + return false; + } + } + } + } else { + throw x.syntaxError("Misshaped tag"); + } + } + } + } + + } From 46eee6464d283fbd751346cfb7b574855bbd4609 Mon Sep 17 00:00:00 2001 From: DinjerChang Date: Tue, 31 Jan 2023 21:51:27 -0800 Subject: [PATCH 25/48] milestone 2 first func done --- src/main/java/M2Test.java | 12 +- src/main/java/org/json/XML.java | 197 +++++++++++++++++--------------- 2 files changed, 107 insertions(+), 102 deletions(-) diff --git a/src/main/java/M2Test.java b/src/main/java/M2Test.java index 92aecd5..8fe07e5 100644 --- a/src/main/java/M2Test.java +++ b/src/main/java/M2Test.java @@ -15,19 +15,13 @@ public static void main(String[] args) { " Ave of Nowhere\n" + " 92614\n" + " \n" + - "

\n" + - " Verano Road\n" + - " 92617\n" + - "
\n" + ""; try { -// JSONObject test = XML.toJSONObject(xmlString); -// System.out.println(test); - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/")); - System.out.println(jobj); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address")); + System.out.println("res: " + jobj); - } catch (JSONException e) { + } catch (JSONException e) { System.out.println(e); } diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index a798458..a0f8617 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -9,6 +9,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -23,6 +24,16 @@ @SuppressWarnings("boxing") public class XML { + public static boolean isReached = false; + + private static class EarlyTermination extends Exception { + + public JSONObject subObject; + public EarlyTermination(JSONObject obj) { + subObject = obj; + } + } + /** * The Character '&'. */ @@ -267,13 +278,12 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP // << token = x.nextToken(); - System.out.println("token: " + token); +// System.out.println("token: " + token); // "); @@ -282,7 +292,6 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP x.back(); } else if (c == '[') { token = x.nextToken(); - System.out.println("token :[ " + token); if ("CDATA".equals(token)) { if (x.next() == '[') { string = x.nextCDATA(); @@ -737,10 +746,20 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JS JSONObject jo = new JSONObject(); XMLTokener x = new XMLTokener(reader); String desiredPath = path.toString(); + + if (Character.toString(desiredPath.charAt(desiredPath.length()- 1)).equalsIgnoreCase("/")){ + throw new JSONPointerException("Invalid Path"); + } while (x.more()) { x.skipPast("<"); if(x.more()) { - customParse(x, jo, null, XMLParserConfiguration.ORIGINAL,desiredPath,""); + try{ + parse1(x, jo, null, XMLParserConfiguration.ORIGINAL,desiredPath,""); + }catch (EarlyTermination e){ + System.out.println(e); + return e.subObject; + } + } } return jo; @@ -806,58 +825,6 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObjec return (JSONObject) obj; } - private static JSONObject updateJsonObjectWithKeyString(JSONObject currentJson, String key, - String pathPrefix, JSONObject replacement) { - // get current object's keys - Iterator iter = currentJson.keys(); - String currentKey; - String globalKeyPath; - while (iter.hasNext()) { - currentKey = (String) iter.next(); - globalKeyPath = pathPrefix + '/' + currentKey; - - // check if match the key. - if (globalKeyPath.equals(key)) { - currentJson.put(currentKey, replacement); - return currentJson; - } - - // expanding the nested structure. - if (currentJson.optJSONObject(currentKey) != null) { - updateJsonObjectWithKeyString(currentJson.getJSONObject(currentKey), key, globalKeyPath, replacement); - } - - if (currentJson.optJSONArray(currentKey) != null) { - JSONArray arr = currentJson.getJSONArray(currentKey); - String[] paths = key.split("/"); - - // find the selected index - String[] pres = globalKeyPath.split("/"); - for (int i = 1; i < paths.length; ++i) { - if (paths[i - 1].equals(pres[pres.length - 1])) { - // if has reached the end - if (i == paths.length - 1) { - arr.put(Integer.parseInt(paths[paths.length - 1]), replacement); - return currentJson; - } else { - if (paths[i].matches("[0-9]+")) { - // update key path with array index - int selectedIndex = Integer.parseInt(paths[i]); - globalKeyPath = globalKeyPath + "/" + paths[i]; // update keypath properly - updateJsonObjectWithKeyString(arr.getJSONObject(selectedIndex), - key, globalKeyPath, replacement); - } else { - throw new Error("unexpected reach."); - } - } - } - } - } - - } - - return currentJson; - } /** * Convert a well-formed (but not necessarily valid) XML string into a * JSONObject. Some information may be lost in this transformation because @@ -1149,8 +1116,8 @@ private static final String indent(int indent) { * @return true if the close tag is processed. * @throws JSONException */ - private static boolean customParse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, String desiredPath, String currPath) - throws JSONException { + private static boolean parse1(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, String desiredPath, String currPath) + throws JSONException, EarlyTermination { // keep track of the path char c; int i; @@ -1171,6 +1138,9 @@ private static boolean customParse(XMLTokener x, JSONObject context, String name // << token = x.nextToken(); +// System.out.println(currPath); + System.out.println("token: " + token); + // level = new ArrayList<>(Arrays.asList(desiredPath.split("/"))); + System.out.println("level: " + level.get(level.size()-1)); + if (token.toString().equalsIgnoreCase(level.get(level.size()-1))){ + isReached = true; +// System.out.println("isReached:" + isReached); + } if (name == null) { throw x.syntaxError("Mismatched close tag " + token); } @@ -1229,23 +1207,31 @@ private static boolean customParse(XMLTokener x, JSONObject context, String name return true; } else if (token instanceof Character) { + System.out.println("a"); throw x.syntaxError("Misshaped tag"); // Open tag < } else { tagName = (String) token; + currPath += "/" + tagName; + System.out.println("curr: " + currPath); token = null; jsonObject = new JSONObject(); boolean nilAttributeFound = false; xmlXsiTypeConverter = null; + for (; ; ) { if (token == null) { token = x.nextToken(); +// System.out.println(x.next()); + System.out.println("token after tagName: " + token); } // attribute = value if (token instanceof String) { + string = (String) token; + System.out.println("inString: " + string); token = x.nextToken(); if (token == EQ) { token = x.nextToken(); @@ -1275,32 +1261,39 @@ private static boolean customParse(XMLTokener x, JSONObject context, String name } else if (token == SLASH) { // Empty tag <.../> if (x.nextToken() != GT) { + System.out.println("b"); 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) { - 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); + if (currPath.startsWith(desiredPath)) { + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (nilAttributeFound) { + context.append(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.append(tagName, jsonObject); + } else { + context.put(tagName, new JSONArray()); + } } else { - context.accumulate(tagName, ""); + if (nilAttributeFound) { + context.accumulate(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.accumulate(tagName, jsonObject); + } else { + context.accumulate(tagName, ""); + } } + return false; } - return false; + } else if (token == GT) { // Content, between <...> and for (; ; ) { + token = x.nextContent(); + System.out.println(token); + System.out.println("token next Content: " + token); if (token == null) { if (tagName != null) { throw x.syntaxError("Unclosed tag " + tagName); @@ -1313,39 +1306,58 @@ private static boolean customParse(XMLTokener x, JSONObject context, String name jsonObject.accumulate(config.getcDataTagName(), stringToValue(string, xmlXsiTypeConverter)); } else { +// System.out.println("In"); jsonObject.accumulate(config.getcDataTagName(), config.isKeepStrings() ? string : stringToValue(string)); } } + } else if (token == LT) { // Nested element - if (customParse(x, jsonObject, tagName, config,desiredPath, currPath)) { - 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())); + System.out.println("In LT"); +// System.out.println(currPath + " ----" + desiredPath); + if (parse1(x, jsonObject, tagName, config, desiredPath, currPath)) { + System.out.println(currPath + " " + desiredPath); +// System.out.println("subJo: " +jsonObject); + if (currPath.startsWith(desiredPath)) { +// System.out.println("TEST"); +// System.out.println("subJo: " +jsonObject); +// System.out.println(token ); + 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 { - context.append(tagName, jsonObject); - } - } else { - if (jsonObject.length() == 0) { - context.accumulate(tagName, ""); - } else if (jsonObject.length() == 1 - && jsonObject.opt(config.getcDataTagName()) != null) { - context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); - } else { - context.accumulate(tagName, jsonObject); + if (jsonObject.length() == 0) { + context.accumulate(tagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.accumulate(tagName, jsonObject); + } } + System.out.println("context: " + context); + } + System.out.println("isReached:" + isReached); + System.out.println("token after reached: " + token); + if (isReached == true){ + throw new EarlyTermination(context); } return false; } } + } + } else { throw x.syntaxError("Misshaped tag"); } @@ -1353,5 +1365,4 @@ private static boolean customParse(XMLTokener x, JSONObject context, String name } } - } From ac5f998da77d95258ab62dff4e15d80c2ccc32ef Mon Sep 17 00:00:00 2001 From: DinjerChang Date: Tue, 31 Jan 2023 23:29:20 -0800 Subject: [PATCH 26/48] milestone 2 sec func done --- src/main/java/M2Test.java | 10 +- src/main/java/org/json/XML.java | 364 +++++++++++++++++++++++++------- 2 files changed, 296 insertions(+), 78 deletions(-) diff --git a/src/main/java/M2Test.java b/src/main/java/M2Test.java index 8fe07e5..e9dcd3e 100644 --- a/src/main/java/M2Test.java +++ b/src/main/java/M2Test.java @@ -18,7 +18,7 @@ public static void main(String[] args) { ""; try { - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address")); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/aress")); System.out.println("res: " + jobj); } catch (JSONException e) { @@ -28,10 +28,10 @@ public static void main(String[] args) { System.out.println("-----------------------"); try { -// JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); -// System.out.println("Given replacement: " + replacement); -// JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/0"), replacement); -// System.out.println(jobj); + JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); + System.out.println("Given replacement: " + replacement); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/street"), replacement); + System.out.println("res: " + jobj); } catch (JSONException e) { System.out.println(e); } diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index a0f8617..33eeb97 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -24,7 +24,8 @@ @SuppressWarnings("boxing") public class XML { - public static boolean isReached = false; + public static boolean isReachedParse1 = false; + public static boolean isReachedParse2 = false; private static class EarlyTermination extends Exception { @@ -748,7 +749,7 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JS String desiredPath = path.toString(); if (Character.toString(desiredPath.charAt(desiredPath.length()- 1)).equalsIgnoreCase("/")){ - throw new JSONPointerException("Invalid Path"); + throw new JSONPointerException("Invalid Path with '/' ending"); } while (x.more()) { x.skipPast("<"); @@ -762,67 +763,30 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JS } } + if (isReachedParse1 ==false){ + throw new JSONException("Path not found"); + } return jo; } - public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) throws JSONException { - Object obj = toJSONObject(reader); - String keyPath = path.toString(); - if (keyPath.charAt(0) == '/'){ - keyPath = keyPath.substring(1); - } - if (keyPath.charAt(keyPath.length()-1)=='/'){ - keyPath = keyPath.substring(0,keyPath.length()-1); - } - System.out.println("keyPath: " + keyPath); - String[] keys = keyPath.split("/"); - for (String s: keys){ - System.out.println("key: "+ s); - } - - Object current = obj; - for (int i = 0; i < keys.length - 1; i++) { - - if (current instanceof JSONObject) { - if (((JSONObject) current).has(keys[i])){ - current = ((JSONObject) current).get(keys[i]); - }else{ - System.out.println("Invalid Path"); - return (JSONObject) obj; - } - - } else if (current instanceof JSONArray) { - if (((JSONArray) current).isNull(Integer.parseInt (keys[i]))){ - System.out.println("Invalid Path"); - return (JSONObject) obj; - }else{ - current = ((JSONArray) current).get(Integer.parseInt(keys[i])); - } + public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) throws JSONException { + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(reader); + String desiredPath = path.toString(); - } - System.out.println("current: " + current); + if (Character.toString(desiredPath.charAt(desiredPath.length()- 1)).equalsIgnoreCase("/")){ + throw new JSONPointerException("Invalid Path with '/' ending"); } - - - // replace the sub-object with custom object - if (current instanceof JSONObject) { - if (((JSONObject) current).has(keys[keys.length-1])){ - ((JSONObject) current).put(keys[keys.length - 1], replacement); - }else{ - System.out.println("Invalid Path"); - return (JSONObject)obj; - } - - } else if (current instanceof JSONArray) { - if (((JSONArray) current).isNull(Integer.parseInt (keys[keys.length-1]))){ - System.out.println("Invalid Path"); - return (JSONObject) obj; - }else{ - ((JSONArray) current).put(Integer.parseInt(keys[keys.length - 1]), replacement); + while (x.more()) { + x.skipPast("<"); + if(x.more()) { + parse2(x, jo, null, XMLParserConfiguration.ORIGINAL,desiredPath,"",replacement); } - } - return (JSONObject) obj; + if (isReachedParse2 == false){ + throw new JSONPointerException("Path not found"); + } + return jo; } /** @@ -1139,7 +1103,7 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML token = x.nextToken(); // System.out.println(currPath); - System.out.println("token: " + token); +// System.out.println("token: " + token); // level = new ArrayList<>(Arrays.asList(desiredPath.split("/"))); - System.out.println("level: " + level.get(level.size()-1)); +// System.out.println("level: " + level.get(level.size()-1)); if (token.toString().equalsIgnoreCase(level.get(level.size()-1))){ - isReached = true; + isReachedParse1 = true; // System.out.println("isReached:" + isReached); } if (name == null) { @@ -1207,7 +1171,7 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML return true; } else if (token instanceof Character) { - System.out.println("a"); + throw x.syntaxError("Misshaped tag"); // Open tag < @@ -1215,7 +1179,7 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML } else { tagName = (String) token; currPath += "/" + tagName; - System.out.println("curr: " + currPath); +// System.out.println("curr: " + currPath); token = null; jsonObject = new JSONObject(); boolean nilAttributeFound = false; @@ -1225,13 +1189,13 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML if (token == null) { token = x.nextToken(); // System.out.println(x.next()); - System.out.println("token after tagName: " + token); +// System.out.println("token after tagName: " + token); } // attribute = value if (token instanceof String) { string = (String) token; - System.out.println("inString: " + string); +// System.out.println("inString: " + string); token = x.nextToken(); if (token == EQ) { token = x.nextToken(); @@ -1261,7 +1225,7 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML } else if (token == SLASH) { // Empty tag <.../> if (x.nextToken() != GT) { - System.out.println("b"); + throw x.syntaxError("Misshaped tag"); } if (currPath.startsWith(desiredPath)) { @@ -1292,8 +1256,7 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML for (; ; ) { token = x.nextContent(); - System.out.println(token); - System.out.println("token next Content: " + token); +// System.out.println("token next Content: " + token); if (token == null) { if (tagName != null) { throw x.syntaxError("Unclosed tag " + tagName); @@ -1315,10 +1278,10 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML } else if (token == LT) { // Nested element - System.out.println("In LT"); +// System.out.println("In LT"); // System.out.println(currPath + " ----" + desiredPath); if (parse1(x, jsonObject, tagName, config, desiredPath, currPath)) { - System.out.println(currPath + " " + desiredPath); +// System.out.println(currPath + " " + desiredPath); // System.out.println("subJo: " +jsonObject); if (currPath.startsWith(desiredPath)) { // System.out.println("TEST"); @@ -1344,11 +1307,11 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML context.accumulate(tagName, jsonObject); } } - System.out.println("context: " + context); + } - System.out.println("isReached:" + isReached); - System.out.println("token after reached: " + token); - if (isReached == true){ +// System.out.println("isReached1:" + isReachedParse1); + if (isReachedParse1 == true){ + System.out.println("context: " + context); throw new EarlyTermination(context); } @@ -1365,4 +1328,259 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML } } + private static boolean parse2(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, String desiredPath, String currPath, JSONObject replacement) + throws JSONException { + // keep track of the path + 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(); +// System.out.println(currPath); +// System.out.println("token: " + token); + + + // "); + 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 level = new ArrayList<>(Arrays.asList(desiredPath.split("/"))); +// System.out.println("level: " + level.get(level.size()-1)); + if (token.toString().equalsIgnoreCase(level.get(level.size()-1))){ + isReachedParse2 = true; +// System.out.println("isReached:" + isReached); + } + if (name == null) { + throw x.syntaxError("Mismatched close tag " + token); + } + if (!token.equals(name)) { + throw x.syntaxError("Mismatched " + name + " and " + token); + } + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped close tag"); + } + return true; + + } else if (token instanceof Character) { + + throw x.syntaxError("Misshaped tag"); + + // Open tag < + + } else { + tagName = (String) token; + currPath += "/" + tagName; +// System.out.println("curr: " + currPath); + token = null; + jsonObject = new JSONObject(); + boolean nilAttributeFound = false; + xmlXsiTypeConverter = null; + + for (; ; ) { + if (token == null) { + token = x.nextToken(); +// System.out.println(x.next()); +// System.out.println("token after tagName: " + token); + } + // attribute = value + if (token instanceof String) { + + string = (String) token; +// System.out.println("inString: " + 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) { + 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 + for (; ; ) { + + token = x.nextContent(); +// System.out.println("token next Content: " + token); + 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 { +// System.out.println("In"); + jsonObject.accumulate(config.getcDataTagName(), + config.isKeepStrings() ? string : stringToValue(string)); + } + } + + + } else if (token == LT) { + // Nested element +// System.out.println("In LT"); +// System.out.println(currPath + " ----" + desiredPath); + if (parse2(x, jsonObject, tagName, config, desiredPath, currPath, replacement)) { + + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (jsonObject.length() == 0) { +// System.out.println("a" + tagName); + context.put(tagName, new JSONArray()); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { +// System.out.println("b" + tagName); + context.append(tagName, jsonObject.opt(config.getcDataTagName())); + } else { +// System.out.println("c" + tagName); + context.append(tagName, jsonObject); + } + } else { + if (jsonObject.length() == 0) { +// System.out.println("d" + tagName); + context.accumulate(tagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + if (currPath.equalsIgnoreCase(desiredPath)){ + +// System.out.println("tagName: " + tagName); + context.accumulate(tagName, replacement.get(tagName)); + }else{ + context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); +// System.out.println("f" + tagName); + } + } else { +// System.out.println("e" + tagName); + if (currPath.equalsIgnoreCase(desiredPath)){ + context.accumulate(tagName, replacement); + }else{ + context.accumulate(tagName, jsonObject); + } + + } +// System.out.println(currPath); +// System.out.println("context: " + context); + } + + return false; + } + } + + } + + } else { + throw x.syntaxError("Misshaped tag"); + } + } + } + } + } From 13ac9a7e3b2dfa5cf18ce501750fda931c72c250 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Tue, 31 Jan 2023 23:35:30 -0800 Subject: [PATCH 27/48] testcase done --- src/main/java/M2Test.java | 2 +- src/test/java/org/json/junit/XMLTest.java | 138 ++++++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/src/main/java/M2Test.java b/src/main/java/M2Test.java index 8fe07e5..99491f7 100644 --- a/src/main/java/M2Test.java +++ b/src/main/java/M2Test.java @@ -18,7 +18,7 @@ public static void main(String[] args) { ""; try { - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address")); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact")); System.out.println("res: " + jobj); } catch (JSONException e) { diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 937658e..acfde58 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -24,6 +24,7 @@ import org.json.*; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; @@ -1247,6 +1248,143 @@ public void testIndentComplicatedJsonObjectWithArrayAndWithConfig(){ fail("file writer error: " +e.getMessage()); } } + + @Test + public void testM2P1WithNoSlash_1(){ + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address")); + String expectedJsonString = "{\"address\":{\"street\":\"Ave of Nowhere\",\"zipcode\":92614}}"; + JSONObject expectedJson = new JSONObject(expectedJsonString); + Util.compareActualVsExpectedJsonObjects(jobj, expectedJson); + } + + @Test + public void testM2P1WithNoSlash_2(){ + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact")); + String expectedJsonString = "{\"contact\":{\"nick\":\"Crista\",\"address\":{\"zipcode\":92614,\"street\":\"Ave of Nowhere\"},\"name\":\"Crista Lopes\"}}"; + JSONObject expectedJson = new JSONObject(expectedJsonString); + Util.compareActualVsExpectedJsonObjects(jobj, expectedJson); + } + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void testM2P1WithSlash() { + exceptionRule.expect(JSONPointerException.class); + exceptionRule.expectMessage("Invalid Path with '/' ending"); + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/")); + } + + @Test + public void testM2P1WrongPath() { + exceptionRule.expect(JSONPointerException.class); + exceptionRule.expectMessage("Path not found"); + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo")); + } + + @Test + public void testM2P2WithNoSlash_1(){ + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/street"), replacement); + String expectedJsonString = "{\"contact\":{\"nick\":\"Crista\",\"address\":{\"zipcode\":92614,\"street\":{\"Ave of the Arts\"},\"name\":\"Crista Lopes\"}}"; + JSONObject expectedJson = new JSONObject(expectedJsonString); + Util.compareActualVsExpectedJsonObjects(jobj, expectedJson); + } + + @Test + public void testM2P2WithNoSlash_2(){ + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact"), replacement); + String expectedJsonString = "{\"contact\":{\"street\":\"Ave of the Arts\"}"; + JSONObject expectedJson = new JSONObject(expectedJsonString); + Util.compareActualVsExpectedJsonObjects(jobj, expectedJson); + } + + @Test + public void testM2P2WithSlash() { + exceptionRule.expect(JSONPointerException.class); + exceptionRule.expectMessage("Invalid Path with '/' ending"); + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/")); + } + + @Test + public void testM2P2WrongPath() { + exceptionRule.expect(JSONPointerException.class); + exceptionRule.expectMessage("Path not found"); + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo")); + } } From e7200bef51c58307a94ae5d36158bf1b95201569 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Wed, 1 Feb 2023 16:28:34 -0800 Subject: [PATCH 28/48] fix testcase --- src/main/java/M2Test.java | 2 +- src/main/java/org/json/XML.java | 12 ++++++++---- src/test/java/org/json/junit/XMLTest.java | 16 +++++++++------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/M2Test.java b/src/main/java/M2Test.java index 41dd7b0..f89a43f 100644 --- a/src/main/java/M2Test.java +++ b/src/main/java/M2Test.java @@ -19,7 +19,7 @@ public static void main(String[] args) { try { - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact")); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/street")); System.out.println("res: " + jobj); diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index b58ed84..614f49a 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -1101,7 +1101,7 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML List level = new ArrayList<>(Arrays.asList(desiredPath.split("/"))); // System.out.println("level: " + level.get(level.size()-1)); - if (token.toString().equalsIgnoreCase(level.get(level.size()-1))){ + if (currPath.equalsIgnoreCase(desiredPath) && token.toString().equalsIgnoreCase(level.get(level.size()-1))){ isReachedParse1 = true; // System.out.println("isReached:" + isReached); } @@ -1253,13 +1253,17 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML context.accumulate(tagName, jsonObject); } } + if (isReachedParse1 == true){ + System.out.println("context: " + context); + throw new EarlyTermination(context); + } } // System.out.println("isReached1:" + isReachedParse1); - if (isReachedParse1 == true){ + /*if (isReachedParse1 == true){ System.out.println("context: " + context); throw new EarlyTermination(context); - } + }*/ return false; } @@ -1349,7 +1353,7 @@ private static boolean parse2(XMLTokener x, JSONObject context, String name, XML List level = new ArrayList<>(Arrays.asList(desiredPath.split("/"))); // System.out.println("level: " + level.get(level.size()-1)); - if (token.toString().equalsIgnoreCase(level.get(level.size()-1))){ + if (currPath.equalsIgnoreCase(desiredPath) && token.toString().equalsIgnoreCase(level.get(level.size()-1))){ isReachedParse2 = true; // System.out.println("isReached:" + isReached); } diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 89c14f1..5a3403a 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1277,15 +1277,12 @@ public void testM2P1WithNoSlash_2(){ " 92614\n" + " \n" + ""; - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact")); - String expectedJsonString = "{\"contact\":{\"nick\":\"Crista\",\"address\":{\"zipcode\":92614,\"street\":\"Ave of Nowhere\"},\"name\":\"Crista Lopes\"}}"; + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/street")); + String expectedJsonString = "{\"street\":\"Ave of Nowhere\"}"; JSONObject expectedJson = new JSONObject(expectedJsonString); Util.compareActualVsExpectedJsonObjects(jobj, expectedJson); } - @Rule - public ExpectedException exceptionRule = ExpectedException.none(); - @Test public void testM2P1WithSlash() { exceptionRule.expect(JSONPointerException.class); @@ -1302,6 +1299,9 @@ public void testM2P1WithSlash() { XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/")); } + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + @Test public void testM2P1WrongPath() { exceptionRule.expect(JSONPointerException.class); @@ -1367,7 +1367,8 @@ public void testM2P2WithSlash() { " 92614\n" + " \n" + ""; - XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/")); + JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); + XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/"), replacement); } @Test @@ -1383,7 +1384,8 @@ public void testM2P2WrongPath() { " 92614\n" + " \n" + ""; - XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo")); + JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); + XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo"), replacement); } } From ede7ddb2f0d23fe8977532cc196fbfab62b8c7b6 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Wed, 1 Feb 2023 18:50:29 -0800 Subject: [PATCH 29/48] readme updated --- README.md | 2 ++ pom.xml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9f8f0b1..71b5dfe 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ We wrote eight tests for testing Problem1 and Problem2. 2. testM2P2WithNoSlash_2() //No slash at the end of JSONPointer 3. testM2P2WithSlash() //With slash at the end of JSONPointer (throws exception: "Invalid Path with '/' ending") 4. testM2P2WrongPath() //Wrong JSONPointer (throws exception: "Path not found") + +Run "mvn clean test -Dtest=XMLTest" to test XMLTest.java
JSON in Java [package org.json] diff --git a/pom.xml b/pom.xml index 4ef85a8..7222d60 100644 --- a/pom.xml +++ b/pom.xml @@ -95,8 +95,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.7 + 1.7 From 0621b001f38c28bc3d988ee1cbf219c1b90a4717 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Wed, 1 Feb 2023 20:30:17 -0800 Subject: [PATCH 30/48] test updated --- src/main/java/M2Test.java | 2 +- src/main/java/org/json/XML.java | 4 ++-- src/test/java/org/json/junit/XMLTest.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/M2Test.java b/src/main/java/M2Test.java index f89a43f..41dd7b0 100644 --- a/src/main/java/M2Test.java +++ b/src/main/java/M2Test.java @@ -19,7 +19,7 @@ public static void main(String[] args) { try { - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/street")); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact")); System.out.println("res: " + jobj); diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 614f49a..682abb1 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -693,7 +693,7 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JS JSONObject jo = new JSONObject(); XMLTokener x = new XMLTokener(reader); String desiredPath = path.toString(); - + isReachedParse1 = false; if (Character.toString(desiredPath.charAt(desiredPath.length()- 1)).equalsIgnoreCase("/")){ throw new JSONPointerException("Invalid Path with '/' ending"); } @@ -719,7 +719,7 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObjec JSONObject jo = new JSONObject(); XMLTokener x = new XMLTokener(reader); String desiredPath = path.toString(); - + isReachedParse2 = false; if (Character.toString(desiredPath.charAt(desiredPath.length()- 1)).equalsIgnoreCase("/")){ throw new JSONPointerException("Invalid Path with '/' ending"); } diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 5a3403a..b005d96 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1277,8 +1277,8 @@ public void testM2P1WithNoSlash_2(){ " 92614\n" + " \n" + ""; - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/street")); - String expectedJsonString = "{\"street\":\"Ave of Nowhere\"}"; + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact")); + String expectedJsonString = "{\"contact\":{\"nick\":\"Crista\",\"address\":{\"zipcode\":92614,\"street\":\"Ave of Nowhere\"},\"name\":\"Crista Lopes\"}}"; JSONObject expectedJson = new JSONObject(expectedJsonString); Util.compareActualVsExpectedJsonObjects(jobj, expectedJson); } From e110eb54614c89ce0181908b7ff5ab4968168684 Mon Sep 17 00:00:00 2001 From: DinjerChang Date: Thu, 9 Feb 2023 14:53:48 -0800 Subject: [PATCH 31/48] MileStone3 function without same tag name handle --- build.gradle | 3 +- src/main/java/M3.java | 35 ++++ src/main/java/org/json/XML.java | 357 ++++++++++++++++++++++++++------ 3 files changed, 333 insertions(+), 62 deletions(-) create mode 100644 src/main/java/M3.java diff --git a/build.gradle b/build.gradle index 63a31a7..a10ee60 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,7 @@ subprojects { group = 'org.json' version = 'v20211205-SNAPSHOT' description = 'JSON in Java' -sourceCompatibility = '1.7' +sourceCompatibility = "1.8" configurations.all { } @@ -53,3 +53,4 @@ publishing { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } +targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/src/main/java/M3.java b/src/main/java/M3.java new file mode 100644 index 0000000..2be13e2 --- /dev/null +++ b/src/main/java/M3.java @@ -0,0 +1,35 @@ +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONPointer; +import org.json.XML; + +import java.io.StringReader; +import java.util.function.Function; + +public class M3 { + public static void main(String[] args) { + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + + try { + + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), s -> { + StringBuilder builder = new StringBuilder(s); + builder.reverse(); + return builder.toString(); + }); + System.out.println("all keys: " + jobj.keySet()); + System.out.println("res: " + jobj); + + } catch (JSONException e) { + System.out.println(e); + } + } +} diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 682abb1..88a0678 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.function.Function; /** @@ -30,6 +31,7 @@ public class XML { private static class EarlyTermination extends Exception { public JSONObject subObject; + public EarlyTermination(JSONObject obj) { subObject = obj; } @@ -628,6 +630,7 @@ public static JSONObject toJSONObject(String string) throws JSONException { public static JSONObject toJSONObject(Reader reader) throws JSONException { return toJSONObject(reader, XMLParserConfiguration.ORIGINAL); } + /** * Convert a well-formed (but not necessarily valid) XML into a * JSONObject. Some information may be lost in this transformation because @@ -681,7 +684,7 @@ public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration conf XMLTokener x = new XMLTokener(reader); while (x.more()) { x.skipPast("<"); - if(x.more()) { + if (x.more()) { parse(x, jo, null, config); } } @@ -694,22 +697,22 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JS XMLTokener x = new XMLTokener(reader); String desiredPath = path.toString(); isReachedParse1 = false; - if (Character.toString(desiredPath.charAt(desiredPath.length()- 1)).equalsIgnoreCase("/")){ - throw new JSONPointerException("Invalid Path with '/' ending"); + if (Character.toString(desiredPath.charAt(desiredPath.length() - 1)).equalsIgnoreCase("/")) { + throw new JSONPointerException("Invalid Path with '/' ending"); } while (x.more()) { x.skipPast("<"); - if(x.more()) { - try{ - parse1(x, jo, null, XMLParserConfiguration.ORIGINAL,desiredPath,""); - }catch (EarlyTermination e){ + if (x.more()) { + try { + parse1(x, jo, null, XMLParserConfiguration.ORIGINAL, desiredPath, ""); + } catch (EarlyTermination e) { System.out.println(e); return e.subObject; } } } - if (isReachedParse1 ==false){ + if (isReachedParse1 == false) { throw new JSONPointerException("Path not found"); } return jo; @@ -720,21 +723,34 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObjec XMLTokener x = new XMLTokener(reader); String desiredPath = path.toString(); isReachedParse2 = false; - if (Character.toString(desiredPath.charAt(desiredPath.length()- 1)).equalsIgnoreCase("/")){ + if (Character.toString(desiredPath.charAt(desiredPath.length() - 1)).equalsIgnoreCase("/")) { throw new JSONPointerException("Invalid Path with '/' ending"); } while (x.more()) { x.skipPast("<"); - if(x.more()) { - parse2(x, jo, null, XMLParserConfiguration.ORIGINAL,desiredPath,"",replacement); + if (x.more()) { + parse2(x, jo, null, XMLParserConfiguration.ORIGINAL, desiredPath, "", replacement); } } - if (isReachedParse2 == false){ + if (isReachedParse2 == false) { throw new JSONPointerException("Path not found"); } return jo; } + public static JSONObject toJSONObject(Reader reader, Function keyTransformer) { + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(reader); + while (x.more()) { + x.skipPast("<"); + if (x.more()) { + parse3(x, jo, null, XMLParserConfiguration.ORIGINAL, keyTransformer); + } + } + return jo; + + } + /** * Convert a well-formed (but not necessarily valid) XML string into a * JSONObject. Some information may be lost in this transformation because @@ -1101,7 +1117,7 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML List level = new ArrayList<>(Arrays.asList(desiredPath.split("/"))); // System.out.println("level: " + level.get(level.size()-1)); - if (currPath.equalsIgnoreCase(desiredPath) && token.toString().equalsIgnoreCase(level.get(level.size()-1))){ + if (currPath.equalsIgnoreCase(desiredPath) && token.toString().equalsIgnoreCase(level.get(level.size() - 1))) { isReachedParse1 = true; // System.out.println("isReached:" + isReached); } @@ -1253,7 +1269,7 @@ private static boolean parse1(XMLTokener x, JSONObject context, String name, XML context.accumulate(tagName, jsonObject); } } - if (isReachedParse1 == true){ + if (isReachedParse1 == true) { System.out.println("context: " + context); throw new EarlyTermination(context); } @@ -1353,7 +1369,7 @@ private static boolean parse2(XMLTokener x, JSONObject context, String name, XML List level = new ArrayList<>(Arrays.asList(desiredPath.split("/"))); // System.out.println("level: " + level.get(level.size()-1)); - if (currPath.equalsIgnoreCase(desiredPath) && token.toString().equalsIgnoreCase(level.get(level.size()-1))){ + if (currPath.equalsIgnoreCase(desiredPath) && token.toString().equalsIgnoreCase(level.get(level.size() - 1))) { isReachedParse2 = true; // System.out.println("isReached:" + isReached); } @@ -1427,26 +1443,25 @@ private static boolean parse2(XMLTokener x, JSONObject context, String name, XML 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) { - context.append(tagName, jsonObject); - } else { - context.put(tagName, new JSONArray()); - } + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (nilAttributeFound) { + context.append(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.append(tagName, jsonObject); } else { - if (nilAttributeFound) { - context.accumulate(tagName, JSONObject.NULL); - } else if (jsonObject.length() > 0) { - context.accumulate(tagName, jsonObject); - } else { - context.accumulate(tagName, ""); - } + context.put(tagName, new JSONArray()); } - return false; - + } 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) { @@ -1480,45 +1495,45 @@ private static boolean parse2(XMLTokener x, JSONObject context, String name, XML // System.out.println(currPath + " ----" + desiredPath); if (parse2(x, jsonObject, tagName, config, desiredPath, currPath, replacement)) { - if (config.getForceList().contains(tagName)) { - // Force the value to be an array - if (jsonObject.length() == 0) { + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (jsonObject.length() == 0) { // System.out.println("a" + tagName); - context.put(tagName, new JSONArray()); - } else if (jsonObject.length() == 1 - && jsonObject.opt(config.getcDataTagName()) != null) { + context.put(tagName, new JSONArray()); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { // System.out.println("b" + tagName); - context.append(tagName, jsonObject.opt(config.getcDataTagName())); - } else { -// System.out.println("c" + tagName); - context.append(tagName, jsonObject); - } + context.append(tagName, jsonObject.opt(config.getcDataTagName())); } else { - if (jsonObject.length() == 0) { +// System.out.println("c" + tagName); + context.append(tagName, jsonObject); + } + } else { + if (jsonObject.length() == 0) { // System.out.println("d" + tagName); - context.accumulate(tagName, ""); - } else if (jsonObject.length() == 1 - && jsonObject.opt(config.getcDataTagName()) != null) { - if (currPath.equalsIgnoreCase(desiredPath)){ + context.accumulate(tagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + if (currPath.equalsIgnoreCase(desiredPath)) { // System.out.println("tagName: " + tagName); - context.accumulate(tagName, replacement.get(tagName)); - }else{ - context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); -// System.out.println("f" + tagName); - } + context.accumulate(tagName, replacement.get(tagName)); } else { + context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); +// System.out.println("f" + tagName); + } + } else { // System.out.println("e" + tagName); - if (currPath.equalsIgnoreCase(desiredPath)){ - context.accumulate(tagName, replacement); - }else{ - context.accumulate(tagName, jsonObject); - } - + if (currPath.equalsIgnoreCase(desiredPath)) { + context.accumulate(tagName, replacement); + } else { + context.accumulate(tagName, jsonObject); } + + } // System.out.println(currPath); // System.out.println("context: " + context); - } + } return false; } @@ -1533,4 +1548,224 @@ private static boolean parse2(XMLTokener x, JSONObject context, String name, XML } } + private static boolean parse3(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(); +// System.out.println("token: " + token); + + // "); + 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 return True + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped tag"); + } + + String newTagName = keyTransformer.apply(tagName); + if (newTagName == null) { + throw new JSONException("Invalid: transform key to null"); + } + if (newTagName.equals("")) { + throw new JSONException("Invalid: transform key to empty string"); + } + if (config.getForceList().contains(tagName)) { + + // Force the value to be an array + if (nilAttributeFound) { + context.append(newTagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.append(newTagName, jsonObject); + } else { + context.put(newTagName, new JSONArray()); + } + } else { + if (nilAttributeFound) { + context.accumulate(newTagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.accumulate(newTagName, jsonObject); + } else { + context.accumulate(newTagName, ""); + } + } + return false; + + } else if (token == GT) { + // Content, between <...> and + for (; ; ) { + 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) { + // Nested element + if (parse3(x, jsonObject, tagName, config, keyTransformer)) { + String newTagName = keyTransformer.apply(tagName); + if (newTagName == null) { + throw new JSONException("Invalid: transform key to null"); + } + if (newTagName.equals("")) { + throw new JSONException("Invalid: transform key to empty string"); + } + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + + if (jsonObject.length() == 0) { + context.put(newTagName, new JSONArray()); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + context.append(newTagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.append(newTagName, jsonObject); + } + } else { + if (jsonObject.length() == 0) { + context.accumulate(newTagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + context.accumulate(newTagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.accumulate(newTagName, jsonObject); + } + } + + return false; + } + } + } + } else { + throw x.syntaxError("Misshaped tag"); + } + } + } + } } From d2f0352596b89dc049c0e554ab201addff27bc1e Mon Sep 17 00:00:00 2001 From: eric052199 Date: Thu, 9 Feb 2023 14:54:48 -0800 Subject: [PATCH 32/48] milestone 2 test --- src/test/java/org/json/junit/XMLTest.java | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index b005d96..1da6788 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1373,7 +1373,25 @@ public void testM2P2WithSlash() { @Test public void testM2P2WrongPath() { - exceptionRule.expect(JSONPointerException.class); + try{ + String xmlString = "\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); + XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo"), replacement); + fail("Should have thrown wrong path exception, but not!"); + } + catch (JSONPointerException e){ + String errorMessage = "Path not found"; + assertEquals(errorMessage, e.getMessage()); + } + /*exceptionRule.expect(JSONPointerException.class); exceptionRule.expectMessage("Path not found"); String xmlString = "\n"+ "\n"+ @@ -1385,7 +1403,7 @@ public void testM2P2WrongPath() { " \n" + ""; JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); - XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo"), replacement); + XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo"), replacement);*/ } } From 3aa372b36b04ffdd29df163aac1eb9509b9b5ebe Mon Sep 17 00:00:00 2001 From: eric052199 Date: Thu, 9 Feb 2023 17:12:01 -0800 Subject: [PATCH 33/48] test added --- src/main/java/M3.java | 41 +++++-- src/main/java/org/json/XML.java | 4 +- src/test/java/org/json/junit/XMLTest.java | 139 ++++++++++++++++++++++ 3 files changed, 173 insertions(+), 11 deletions(-) diff --git a/src/main/java/M3.java b/src/main/java/M3.java index 2be13e2..3beb334 100644 --- a/src/main/java/M3.java +++ b/src/main/java/M3.java @@ -8,15 +8,38 @@ public class M3 { public static void main(String[] args) { - String xmlString = "\n"+ - "\n"+ - " Crista \n"+ - " Crista Lopes\n" + - "
\n" + - " Ave of Nowhere\n" + - " 92614\n" + - "
\n" + - "
"; + String xmlString ="\n" + + "\n" + + " \n" + + " Gambardella, Matthew\n" + + " XML Developer's Guide\n" + + " Computer\n" + + " 44.95\n" + + " 2000-10-01\n" + + " An in-depth look at creating applications\n" + + " with XML.\n" + + " \n" + + " \n" + + " Ralls, Kim\n" + + " Midnight Rain\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-12-16\n" + + " A former architect battles corporate zombies,\n" + + " an evil sorceress, and her own childhood to become queen\n" + + " of the world.\n" + + " \n" + + " \n" + + " Corets, Eva\n" + + " Maeve Ascendant\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-11-17\n" + + " After the collapse of a nanotechnology\n" + + " society in England, the young survivors lay the\n" + + " foundation for a new society.\n" + + " \n" + + ""; try { diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 88a0678..2d3c742 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -1660,14 +1660,14 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML && TYPE_ATTR.equals(string)) { xmlXsiTypeConverter = config.getXsiTypeMap().get(token); } else if (!nilAttributeFound) { - jsonObject.accumulate(string, + jsonObject.accumulate(keyTransformer.apply(string), config.isKeepStrings() ? ((String) token) : stringToValue((String) token)); } token = null; } else { - jsonObject.accumulate(string, ""); + jsonObject.accumulate(keyTransformer.apply(string), ""); } diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 1da6788..b49718e 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1405,6 +1405,145 @@ public void testM2P2WrongPath() { JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo"), replacement);*/ } + + @Test + public void testM3(){ + String xmlStr ="\n" + + "\n" + + " \n" + + " Gambardella, Matthew\n" + + " XML Developer's Guide\n" + + " Computer\n" + + " 44.95\n" + + " 2000-10-01\n" + + " An in-depth look at creating applications\n" + + " with XML.\n" + + " \n" + + " \n" + + " Ralls, Kim\n" + + " Midnight Rain\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-12-16\n" + + " A former architect battles corporate zombies,\n" + + " an evil sorceress, and her own childhood to become queen\n" + + " of the world.\n" + + " \n" + + " \n" + + " Corets, Eva\n" + + " Maeve Ascendant\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-11-17\n" + + " After the collapse of a nanotechnology\n" + + " society in England, the young survivors lay the\n" + + " foundation for a new society.\n" + + " \n" + + ""; + try { + Reader reader = new StringReader(xmlStr); + JSONObject jo = XML.toJSONObject(reader, s -> "SWE262_" + s); + reader.close(); + assertEquals("Correct result.","{\"SWE262_catalog\":{\"SWE262_book\":[{\"SWE262_author\":\"Gambardella, Matthew\",\"SWE262_title\":\"XML Developer's Guide\",\"SWE262_publish_date\":\"2000-10-01\",\"SWE262_genre\":\"Computer\",\"SWE262_description\":\"An in-depth look at creating applications\\n with XML.\",\"SWE262_id\":\"bk101\",\"SWE262_price\":44.95},{\"SWE262_author\":\"Ralls, Kim\",\"SWE262_title\":\"Midnight Rain\",\"SWE262_publish_date\":\"2000-12-16\",\"SWE262_genre\":\"Fantasy\",\"SWE262_description\":\"A former architect battles corporate zombies,\\n an evil sorceress, and her own childhood to become queen\\n of the world.\",\"SWE262_id\":\"bk102\",\"SWE262_price\":5.95},{\"SWE262_author\":\"Corets, Eva\",\"SWE262_title\":\"Maeve Ascendant\",\"SWE262_publish_date\":\"2000-11-17\",\"SWE262_genre\":\"Fantasy\",\"SWE262_description\":\"After the collapse of a nanotechnology\\n society in England, the young survivors lay the\\n foundation for a new society.\",\"SWE262_id\":\"bk103\",\"SWE262_price\":5.95}]}}", + jo.toString()); + } + catch (IOException e){ + System.out.println("Caught a IO Exception "); + e.printStackTrace(); + } + } + + @Test + public void testM3WithEmptyString(){ + String xmlStr ="\n" + + "\n" + + " \n" + + " Gambardella, Matthew\n" + + " XML Developer's Guide\n" + + " Computer\n" + + " 44.95\n" + + " 2000-10-01\n" + + " An in-depth look at creating applications\n" + + " with XML.\n" + + " \n" + + " \n" + + " Ralls, Kim\n" + + " Midnight Rain\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-12-16\n" + + " A former architect battles corporate zombies,\n" + + " an evil sorceress, and her own childhood to become queen\n" + + " of the world.\n" + + " \n" + + " \n" + + " Corets, Eva\n" + + " Maeve Ascendant\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-11-17\n" + + " After the collapse of a nanotechnology\n" + + " society in England, the young survivors lay the\n" + + " foundation for a new society.\n" + + " \n" + + ""; + try { + Reader reader = new StringReader(xmlStr); + JSONObject jo = XML.toJSONObject(reader, s -> ""); + fail("Should have thrown empty string exception. "); + + } + catch (JSONException e){ + String expected = "Invalid: transform key to empty string"; + assertEquals(e.getMessage(), expected); + } + } + + @Test + public void testM3WithNull(){ + String xmlStr ="\n" + + "\n" + + " \n" + + " Gambardella, Matthew\n" + + " XML Developer's Guide\n" + + " Computer\n" + + " 44.95\n" + + " 2000-10-01\n" + + " An in-depth look at creating applications\n" + + " with XML.\n" + + " \n" + + " \n" + + " Ralls, Kim\n" + + " Midnight Rain\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-12-16\n" + + " A former architect battles corporate zombies,\n" + + " an evil sorceress, and her own childhood to become queen\n" + + " of the world.\n" + + " \n" + + " \n" + + " Corets, Eva\n" + + " Maeve Ascendant\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-11-17\n" + + " After the collapse of a nanotechnology\n" + + " society in England, the young survivors lay the\n" + + " foundation for a new society.\n" + + " \n" + + ""; + try { + Reader reader = new StringReader(xmlStr); + JSONObject jo = XML.toJSONObject(reader, s -> null); + fail("Should have thrown null exception. "); + + } + catch (JSONException e){ + String expected = "Invalid: transform key to null"; + assertEquals(e.getMessage(), expected); + } + } } From 32c5a21da81fcbce5a6de1ad8563f40837208bda Mon Sep 17 00:00:00 2001 From: DinjerChang Date: Thu, 9 Feb 2023 17:17:27 -0800 Subject: [PATCH 34/48] MileStone3 function same tag name handle done --- src/main/java/M3.java | 12 +++++----- src/main/java/org/json/XML.java | 39 +++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/main/java/M3.java b/src/main/java/M3.java index 2be13e2..73489c6 100644 --- a/src/main/java/M3.java +++ b/src/main/java/M3.java @@ -20,11 +20,13 @@ public static void main(String[] args) { try { - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), s -> { - StringBuilder builder = new StringBuilder(s); - builder.reverse(); - return builder.toString(); - }); +// JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), s -> { +// StringBuilder builder = new StringBuilder(s); +// builder.reverse(); +// return builder.toString(); +// }); + + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), s -> "swe262p_" + s); System.out.println("all keys: " + jobj.keySet()); System.out.println("res: " + jobj); diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 88a0678..271c6d9 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -739,6 +739,21 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObjec } public static JSONObject toJSONObject(Reader reader, Function keyTransformer) { + // check string condition + String res1 = keyTransformer.apply("res1"); + String res2 = keyTransformer.apply("res2"); + // Transform key to null + if (res1 == null) { + throw new JSONException("Invalid: transform key to null"); + } + // Transform key to empty string + if (res2.equals("")) { + throw new JSONException("Invalid: transform key to empty string"); + } + // Transform key to all the same string + if (res1.equals(res2)){ + throw new JSONException("Invalid: transform key to all the same string"); + } JSONObject jo = new JSONObject(); XMLTokener x = new XMLTokener(reader); while (x.more()) { @@ -1587,7 +1602,7 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML if (x.next() == '[') { string = x.nextCDATA(); if (string.length() > 0) { - context.accumulate(config.getcDataTagName(), string); + context.accumulate(keyTransformer.apply(config.getcDataTagName()), string); } return false; } @@ -1660,14 +1675,14 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML && TYPE_ATTR.equals(string)) { xmlXsiTypeConverter = config.getXsiTypeMap().get(token); } else if (!nilAttributeFound) { - jsonObject.accumulate(string, + jsonObject.accumulate(keyTransformer.apply(string), config.isKeepStrings() ? ((String) token) : stringToValue((String) token)); } token = null; } else { - jsonObject.accumulate(string, ""); + jsonObject.accumulate(keyTransformer.apply(string), ""); } @@ -1678,12 +1693,7 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML } String newTagName = keyTransformer.apply(tagName); - if (newTagName == null) { - throw new JSONException("Invalid: transform key to null"); - } - if (newTagName.equals("")) { - throw new JSONException("Invalid: transform key to empty string"); - } + if (config.getForceList().contains(tagName)) { // Force the value to be an array @@ -1718,10 +1728,10 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML string = (String) token; if (string.length() > 0) { if (xmlXsiTypeConverter != null) { - jsonObject.accumulate(config.getcDataTagName(), + jsonObject.accumulate(keyTransformer.apply(config.getcDataTagName()), stringToValue(string, xmlXsiTypeConverter)); } else { - jsonObject.accumulate(config.getcDataTagName(), + jsonObject.accumulate(keyTransformer.apply(config.getcDataTagName()), config.isKeepStrings() ? string : stringToValue(string)); } } @@ -1730,12 +1740,7 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML // Nested element if (parse3(x, jsonObject, tagName, config, keyTransformer)) { String newTagName = keyTransformer.apply(tagName); - if (newTagName == null) { - throw new JSONException("Invalid: transform key to null"); - } - if (newTagName.equals("")) { - throw new JSONException("Invalid: transform key to empty string"); - } + if (config.getForceList().contains(tagName)) { // Force the value to be an array From 456b11c0432c1cd7cfa6c508ab51a7ebf38a1c03 Mon Sep 17 00:00:00 2001 From: DinjerChang Date: Thu, 9 Feb 2023 17:27:29 -0800 Subject: [PATCH 35/48] MileStone3 parse3 fixed --- src/main/java/org/json/XML.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 271c6d9..678c8b6 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -1602,7 +1602,7 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML if (x.next() == '[') { string = x.nextCDATA(); if (string.length() > 0) { - context.accumulate(keyTransformer.apply(config.getcDataTagName()), string); + context.accumulate(config.getcDataTagName(), string); } return false; } @@ -1728,10 +1728,10 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML string = (String) token; if (string.length() > 0) { if (xmlXsiTypeConverter != null) { - jsonObject.accumulate(keyTransformer.apply(config.getcDataTagName()), + jsonObject.accumulate(config.getcDataTagName(), stringToValue(string, xmlXsiTypeConverter)); } else { - jsonObject.accumulate(keyTransformer.apply(config.getcDataTagName()), + jsonObject.accumulate(config.getcDataTagName(), config.isKeepStrings() ? string : stringToValue(string)); } } From ff247febf495e773e2d54d74b5363ce25e2b758e Mon Sep 17 00:00:00 2001 From: eric052199 Date: Thu, 9 Feb 2023 17:28:23 -0800 Subject: [PATCH 36/48] test added --- src/test/java/org/json/junit/XMLTest.java | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index b49718e..eb2af4d 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1544,6 +1544,52 @@ public void testM3WithNull(){ assertEquals(e.getMessage(), expected); } } + + @Test + public void testM3WithDuplicateKeyName(){ + String xmlStr ="\n" + + "\n" + + " \n" + + " Gambardella, Matthew\n" + + " XML Developer's Guide\n" + + " Computer\n" + + " 44.95\n" + + " 2000-10-01\n" + + " An in-depth look at creating applications\n" + + " with XML.\n" + + " \n" + + " \n" + + " Ralls, Kim\n" + + " Midnight Rain\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-12-16\n" + + " A former architect battles corporate zombies,\n" + + " an evil sorceress, and her own childhood to become queen\n" + + " of the world.\n" + + " \n" + + " \n" + + " Corets, Eva\n" + + " Maeve Ascendant\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-11-17\n" + + " After the collapse of a nanotechnology\n" + + " society in England, the young survivors lay the\n" + + " foundation for a new society.\n" + + " \n" + + ""; + try { + Reader reader = new StringReader(xmlStr); + JSONObject jo = XML.toJSONObject(reader, s -> "random string"); + fail("Should have thrown duplicate exception. "); + + } + catch (JSONException e){ + String expected = "Invalid: transform key to all the same string"; + assertEquals(e.getMessage(), expected); + } + } } From 3d60cfeef33830160e1ba8741de61da16c162e55 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Thu, 9 Feb 2023 17:32:45 -0800 Subject: [PATCH 37/48] milestone 3 test case added --- src/test/java/org/json/junit/XMLTest.java | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index eb2af4d..dda716a 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1391,19 +1391,19 @@ public void testM2P2WrongPath() { String errorMessage = "Path not found"; assertEquals(errorMessage, e.getMessage()); } - /*exceptionRule.expect(JSONPointerException.class); - exceptionRule.expectMessage("Path not found"); - String xmlString = "\n"+ - "\n"+ - " Crista \n"+ - " Crista Lopes\n" + - "
\n" + - " Ave of Nowhere\n" + - " 92614\n" + - "
\n" + - "
"; - JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); - XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo"), replacement);*/ +// exceptionRule.expect(JSONPointerException.class); +// exceptionRule.expectMessage("Path not found"); +// String xmlString = "\n"+ +// "\n"+ +// " Crista \n"+ +// " Crista Lopes\n" + +// "
\n" + +// " Ave of Nowhere\n" + +// " 92614\n" + +// "
\n" + +// "
"; +// JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); +// XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo"), replacement); } @Test From ff73430ba157c37f6a1e35e19d13e033989b86f3 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Fri, 10 Feb 2023 15:27:11 -0800 Subject: [PATCH 38/48] Milestone3_Readme added --- Milestone3_Readme.md | 30 +++++++++++++ README.md | 12 ++++++ m1_4.png | Bin 0 -> 51773 bytes m3.png | Bin 0 -> 57766 bytes pom.xml | 4 +- src/main/java/M3.java | 12 +++--- src/test/java/org/json/junit/XMLTest.java | 50 ++++++++++++++++++++++ 7 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 Milestone3_Readme.md create mode 100644 m1_4.png create mode 100644 m3.png diff --git a/Milestone3_Readme.md b/Milestone3_Readme.md new file mode 100644 index 0000000..daba129 --- /dev/null +++ b/Milestone3_Readme.md @@ -0,0 +1,30 @@ +# Milestone3 + +### Parse() and toJSONObject() methods are added in src/main/java/org.json/XML.java +JSONObject toJSONObject(Reader reader, Function func) [line 741] +* Rewrite toJSONObject() method, inputs are a reader and a lambda function +* Purpose: Add a prefix to all of its keys +* Main method: + * Rewrite Parse() [line 1566]. Compared with original parse(), replace string and tagName variable with keyTransformer.apply(string) and keyTransformer.apply(tagName) + * Call the new Parse() method in toJSONObject(), put reader and func as input + +### Unit Tests are added in src/test/java/org.json.junit/XMLTest.java +We wrote five tests for testing Milestone 3. +
+1. testM3()                        //add "SWE262_" to every keys as prefix
+2. testM3Reverse()                 //reverse the key names
+3. testM3WithEmptyString()         //throws exception: "Invalid: transform key to empty string" 
+4. testM3WithNull()                //throws exception: "Invalid: transform key to null"
+5. testM3WithDuplicateKeyName()    //throws exception: "Invalid: transform key to all the same string"
+
+Run "mvn clean test -Dtest=XMLTest" to test XMLTest.java + +### Performance Comparison + +running time: in-library = 123 ms; +outside = 205 ms +The efficiency increased by 40%. + +![m1](m1_4.png) + +![m3](m3.png) diff --git a/README.md b/README.md index 71b5dfe..213e16a 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,18 @@ We wrote eight tests for testing Problem1 and Problem2. Run "mvn clean test -Dtest=XMLTest" to test XMLTest.java
+### < Milestone3 > +
+We wrote four tests for testing Milestone 3.  
+
+1. testM3()                        //add "SWE262_" to every keys as prefix
+2. testM3WithEmptyString()         //throws exception: "Invalid: transform key to empty string" 
+3. testM3WithNull()                //throws exception: "Invalid: transform key to null"
+4. testM3WithDuplicateKeyName()    //throws exception: "Invalid: transform key to all the same string"
+
+Run "mvn clean test -Dtest=XMLTest" to test XMLTest.java
+
+ JSON in Java [package org.json] =============================== diff --git a/m1_4.png b/m1_4.png new file mode 100644 index 0000000000000000000000000000000000000000..a1a4f4a1d1e3e2a42a6ff6d6cd5e83350af85594 GIT binary patch literal 51773 zcmeFZcT|(x);@~T1Vm5-q+>7{77dLg#tkfhVB(y4z z6tzi6$k|Cq&RCzL0G`l&(rY6jIS;l|P|#FSP~g^dcd@o}v?3vS^zO4hrNPr5OsU4v zQF6BFs#J|s*Y3UgL~lg0u0;PiitOCYZ*rQYnPJpLCj8G8^p!44b2DAm;^%))i?xtf zGGwOlUVcKoR#zN=449k7&Ov)mSG#LHx=7Nzqpx3D&e$ONNk!9EdiBhq+mAb*gHK

TydD5%{qI(>Dak?R-~>MKux~A%#@a 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

MQQpd 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

`F*dR-1q$)zvp=Wd;fd;t0TH* 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 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index 7222d60..93fed2c 100644 --- a/pom.xml +++ b/pom.xml @@ -95,8 +95,8 @@ maven-compiler-plugin 2.3.2 - 1.7 - 1.7 + 1.8 + 1.8 diff --git a/src/main/java/M3.java b/src/main/java/M3.java index d87bc2d..71713f8 100644 --- a/src/main/java/M3.java +++ b/src/main/java/M3.java @@ -43,13 +43,13 @@ public static void main(String[] args) { try { -// JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), s -> { -// StringBuilder builder = new StringBuilder(s); -// builder.reverse(); -// return builder.toString(); -// }); + JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), s -> { + StringBuilder builder = new StringBuilder(s); + builder.reverse(); + return builder.toString(); + }); - JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), s -> "swe262p_" + s); + //JSONObject jobj = XML.toJSONObject(new StringReader(xmlString), s -> "swe262p_" + s); System.out.println("all keys: " + jobj.keySet()); System.out.println("res: " + jobj); diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index dda716a..cbd4b4f 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1453,6 +1453,56 @@ public void testM3(){ } } + @Test + public void testM3Reverse(){ + String xmlStr ="\n" + + "\n" + + " \n" + + " Gambardella, Matthew\n" + + " XML Developer's Guide\n" + + " Computer\n" + + " 44.95\n" + + " 2000-10-01\n" + + " An in-depth look at creating applications\n" + + " with XML.\n" + + " \n" + + " \n" + + " Ralls, Kim\n" + + " Midnight Rain\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-12-16\n" + + " A former architect battles corporate zombies,\n" + + " an evil sorceress, and her own childhood to become queen\n" + + " of the world.\n" + + " \n" + + " \n" + + " Corets, Eva\n" + + " Maeve Ascendant\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-11-17\n" + + " After the collapse of a nanotechnology\n" + + " society in England, the young survivors lay the\n" + + " foundation for a new society.\n" + + " \n" + + ""; + try { + Reader reader = new StringReader(xmlStr); + JSONObject jo = XML.toJSONObject(reader, s -> { + StringBuilder builder = new StringBuilder(s); + builder.reverse(); + return builder.toString(); + }); + reader.close(); + assertEquals("Correct result.", "{\"golatac\":{\"koob\":[{\"rohtua\":\"Gambardella, Matthew\",\"ecirp\":44.95,\"di\":\"bk101\",\"erneg\":\"Computer\",\"etad_hsilbup\":\"2000-10-01\",\"eltit\":\"XML Developer's Guide\",\"noitpircsed\":\"An in-depth look at creating applications\\n with XML.\"},{\"rohtua\":\"Ralls, Kim\",\"ecirp\":5.95,\"di\":\"bk102\",\"erneg\":\"Fantasy\",\"etad_hsilbup\":\"2000-12-16\",\"eltit\":\"Midnight Rain\",\"noitpircsed\":\"A former architect battles corporate zombies,\\n an evil sorceress, and her own childhood to become queen\\n of the world.\"},{\"rohtua\":\"Corets, Eva\",\"ecirp\":5.95,\"di\":\"bk103\",\"erneg\":\"Fantasy\",\"etad_hsilbup\":\"2000-11-17\",\"eltit\":\"Maeve Ascendant\",\"noitpircsed\":\"After the collapse of a nanotechnology\\n society in England, the young survivors lay the\\n foundation for a new society.\"}]}}", jo.toString()); + } + catch (IOException e){ + System.out.println("Caught a IO Exception "); + e.printStackTrace(); + } + } + @Test public void testM3WithEmptyString(){ String xmlStr ="\n" + From 06c3af6743565128936e0e69c2d825c48449c94f Mon Sep 17 00:00:00 2001 From: eric052199 Date: Sun, 26 Feb 2023 21:16:22 -0800 Subject: [PATCH 39/48] milestone 4 --- src/main/java/M4.java | 74 ++++++++++ src/main/java/org/json/JSONObject.java | 100 ++++++++++++- .../java/org/json/junit/Milestone4Test.java | 135 ++++++++++++++++++ 3 files changed, 302 insertions(+), 7 deletions(-) create mode 100644 src/main/java/M4.java create mode 100644 src/test/java/org/json/junit/Milestone4Test.java diff --git a/src/main/java/M4.java b/src/main/java/M4.java new file mode 100644 index 0000000..f0ca124 --- /dev/null +++ b/src/main/java/M4.java @@ -0,0 +1,74 @@ +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONPointer; +import org.json.XML; + +import java.io.StringReader; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class M4 { + public static void main(String[] args) { + String xmlString ="\n" + + "\n" + + " \n" + + " Gambardella, Matthew\n" + + " XML Developer's Guide\n" + + " Computer\n" + + " 44.95\n" + + " 2000-10-01\n" + + " An in-depth look at creating applications\n" + + " with XML.\n" + + " \n" + + " \n" + + " Ralls, Kim\n" + + " Midnight Rain\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-12-16\n" + + " A former architect battles corporate zombies,\n" + + " an evil sorceress, and her own childhood to become queen\n" + + " of the world.\n" + + " \n" + + " \n" + + " Corets, Eva\n" + + " Maeve Ascendant\n" + + " Fantasy\n" + + " 5.95\n" + + " 2000-11-17\n" + + " After the collapse of a nanotechnology\n" + + " society in England, the young survivors lay the\n" + + " foundation for a new society.\n" + + " \n" + + ""; + + try { + + JSONObject obj = XML.toJSONObject(xmlString); + Stream stream = obj.toStream(); + + System.out.println(obj); + List l = stream.collect(Collectors.toList()); + for (JSONObject node : l) { + System.out.println(node.toString()); + } + + /*List l2 = stream.filter(node -> node.has("tt")).collect(Collectors.toList()); + for (JSONObject j : l2) { + System.out.println(j.toString()); + }*/ + + /*System.out.println(obj.keySet()); + List> l3 = stream.map(node -> node.keySet()).collect(Collectors.toList()); + for (Set s : l3) { + System.out.println(s.toString());; + }*/ + + } catch (JSONException e) { + System.out.println(e); + } + } +} diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 68d302d..0a63b26 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -15,18 +15,15 @@ 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.*; import java.util.Map; 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 @@ -494,6 +491,95 @@ public JSONObject accumulate(String key, Object value) throws JSONException { return this; } + public Stream toStream() { + return StreamSupport.stream(this.spliterator(), false); + } + + public Spliterator spliterator() { + return new NodeSpliterator(this); + } + + /** + * NodeSpliterator implements Spliterator and methods are overwritten + */ + class NodeSpliterator implements Spliterator { + + private final JSONObject root; + private JSONObject tree; + + NodeSpliterator(JSONObject t) { + root = tree = t; + } + + /** + * DISTINCT: Characteristic value signifying that, for each pair of encountered elements x, y, !x.equals(y). + * IMMUTABLE: Characteristic value signifying that the element source cannot be structurally modified; + * NONNULL: Characteristic value signifying that the source guarantees that encountered elements will not be null. + * + * @return + */ + @Override + public int characteristics() { + return Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL; + } + + @Override + public long estimateSize() { + return Long.MAX_VALUE; + } + + /** + * tryAdvance() iterate elements individually + * If a remaining element exists, performs the given action on it, returning true; else returns false. + * We only manipulate the JSONObject. So in the stream, all the elements are JSONObject + * + * @param action + * @return + */ + @Override + public boolean tryAdvance(Consumer action) { + // tree is a global variable responsible for recording tree node + // current gets the value of tree + JSONObject current = tree; + + // entrySet() gets a set of entries of the JSONObject. + Set> all_entry = tree.entrySet(); + // iterate the set of entries of tree node + for (Entry entry : all_entry) { + if (entry.getValue() instanceof JSONObject) { + // update tree node + tree = (JSONObject) entry.getValue(); + //Repackage JSONObject so that it contains its key value and puts it into the stream + JSONObject newObj = new JSONObject(); + newObj.put(entry.getKey(), entry.getValue()); + action.accept(newObj); + // DFS: recall the tryAdvance to get all the JSONObjects + tryAdvance(action); + } else if (!(entry.getValue() instanceof JSONArray)) { + //Repackage JSONObject so that it contains its key value and puts it into the stream + JSONObject newObj = new JSONObject(); + newObj.put(entry.getKey(), entry.getValue()); + action.accept(newObj); + } + } + tree = current; + if (tree == root) + return false; + else + return true; + } + + /** + * return null: this spliterator cannot be split + * + * @return + */ + @Override + public Spliterator trySplit() { + return null; + } + } + /** * Append values to the array under a key. If the key does not exist in the * JSONObject, then the key is put in the JSONObject with its value being a diff --git a/src/test/java/org/json/junit/Milestone4Test.java b/src/test/java/org/json/junit/Milestone4Test.java new file mode 100644 index 0000000..5ffc12a --- /dev/null +++ b/src/test/java/org/json/junit/Milestone4Test.java @@ -0,0 +1,135 @@ +package org.json.junit; + +import org.json.JSONObject; +import org.json.XML; +import org.junit.Test; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; + +// Add streaming methods to the library that allow the client code to chain operations on JSON nodes + +public class Milestone4Test { + @Test + public void streamTestPrint() { + String xml = "\n" + + "\n" + + " Crista \n" + + " Crista Lopes\n" + + "

\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + ""; + + String xml1 = "\n" + + "Crista \n"; + String xml2 = "\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n"; + String xml3 = "\n" + + "92614\n"; + String xml4 = "\n" + + "Ave of Nowhere\n"; + String xml5 = "\n" + + "Crista Lopes\n"; + + JSONObject[] xmlArray = {XML.toJSONObject(xml), + XML.toJSONObject(xml1), + XML.toJSONObject(xml2), + XML.toJSONObject(xml3), + XML.toJSONObject(xml4), + XML.toJSONObject(xml5), + }; + JSONObject jsonObject = + XML.toJSONObject(xml); + Stream stream = jsonObject.toStream(); + List l = stream.collect(Collectors.toList()); + int i = 0; + for (JSONObject node : l) { + assertEquals(node.toString(), xmlArray[i].toString()); + i++; + } + } + + @Test + public void streamTestfilter() { + String xml = "\n" + + "\n" + + "\n" + + " Crista \n" + + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
" + + "\n" + + " Eric \n" + + " Eric\n" + + "
\n" + + " Verano\n" + + " 92617\n" + + "
\n" + + "
" + + "
"; + + + String xml1 = "\n" + + "92617\n"; + + + JSONObject[] xmlArray = { + XML.toJSONObject(xml1), + }; + JSONObject jsonObject = + XML.toJSONObject(xml); + Stream stream = jsonObject.toStream(); + int i = 0; + List l = stream.filter(node -> node.has("zipcode")).collect(Collectors.toList()); + for (JSONObject j : l) { + assertEquals(xmlArray[i].toString(), j.toString()); + i++; + } + } + + @Test + public void streamGetKey() { + String xml = "\n" + + "\n" + + " Crista \n" + + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + List> expect = new ArrayList<>(); + Set set1 = new HashSet<>(Arrays.asList("contact")); + Set set2 = new HashSet<>(Arrays.asList("nick")); + Set set3 = new HashSet<>(Arrays.asList("address")); + Set set4 = new HashSet<>(Arrays.asList("zipcode")); + Set set5 = new HashSet<>(Arrays.asList("street")); + Set set6 = new HashSet<>(Arrays.asList("name")); + expect.add(set1); + expect.add(set2); + expect.add(set3); + expect.add(set4); + expect.add(set5); + expect.add(set6); + JSONObject jsonObject = + XML.toJSONObject(xml); + Stream stream = jsonObject.toStream(); + int i = 0; + List> l = stream.map(node -> node.keySet()).collect(Collectors.toList()); + for (Set s : l) { + assertEquals(expect.get(i), s); + i++; + } + } +} \ No newline at end of file From b119de80d8b75b72e8819331fc4b2159b64e13bb Mon Sep 17 00:00:00 2001 From: eric052199 Date: Sun, 26 Feb 2023 23:13:55 -0800 Subject: [PATCH 40/48] JSONObject.java milestone 4 done --- src/main/java/M4.java | 52 ++++++- src/main/java/org/json/JSONObject.java | 43 +++++- .../java/org/json/junit/Milestone4Test.java | 135 ------------------ 3 files changed, 91 insertions(+), 139 deletions(-) delete mode 100644 src/test/java/org/json/junit/Milestone4Test.java diff --git a/src/main/java/M4.java b/src/main/java/M4.java index f0ca124..9196f26 100644 --- a/src/main/java/M4.java +++ b/src/main/java/M4.java @@ -45,18 +45,64 @@ public static void main(String[] args) { " \n" + ""; + String xmlString2 = "\n" + + "\n" + + " Crista \n" + + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + + String xmlString3 = "\n" + + "\n" + + "\n" + + " Crista \n" + + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
" + + "\n" + + " Eric \n" + + " Eric\n" + + "
\n" + + " Verano\n" + + " 92617\n" + + "
\n" + + "
" + + "
"; + + String xmlString4 = "AAAASmithBBBBSmith"; + + String xml1 = "\n" + + "92617\n"; + + + JSONObject[] xmlArray = { + XML.toJSONObject(xml1), + }; + try { - JSONObject obj = XML.toJSONObject(xmlString); + JSONObject obj = XML.toJSONObject(xmlString3); Stream stream = obj.toStream(); - System.out.println(obj); + //System.out.println(obj); List l = stream.collect(Collectors.toList()); + int i = 0; for (JSONObject node : l) { + //System.out.println(i); System.out.println(node.toString()); + i++; } - /*List l2 = stream.filter(node -> node.has("tt")).collect(Collectors.toList()); + //stream.filter(node -> node.has("zipcode")).forEach(node -> System.out.println(node)); + //System.out.println(xmlArray[0].toString()); + //System.out.println(stream.filter(node -> node.has("zipcode")).count()); + /*List l2 = stream.filter(node -> node.has("zipcode")).collect(Collectors.toList()); for (JSONObject j : l2) { System.out.println(j.toString()); }*/ diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 0a63b26..36c67ce 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -555,7 +555,22 @@ public boolean tryAdvance(Consumer action) { action.accept(newObj); // DFS: recall the tryAdvance to get all the JSONObjects tryAdvance(action); - } else if (!(entry.getValue() instanceof JSONArray)) { + } + else if(entry.getValue() instanceof JSONArray) { + JSONArray ja = (JSONArray) entry.getValue(); + JSONObject newObj = new JSONObject(); + newObj.put(entry.getKey(), entry.getValue()); + action.accept(newObj); + for (Object jo : ja) { + JSONObject newObjInArray = new JSONObject(); + newObjInArray.put(entry.getKey(), jo); + tree = (JSONObject) jo; + action.accept(newObjInArray); + // DFS: recall the tryAdvance to get all the JSONObjects + tryAdvance(action); + } + } + else if (!(entry.getValue() instanceof JSONArray)) { //Repackage JSONObject so that it contains its key value and puts it into the stream JSONObject newObj = new JSONObject(); newObj.put(entry.getKey(), entry.getValue()); @@ -580,6 +595,32 @@ public Spliterator trySplit() { } } +// public Stream toStream(){ +// Stream.Builder builder = Stream.builder(); +// Set> entrySet = this.entrySet(); +// for(Entry e:entrySet){ +// fillStream(e.getKey(),e.getValue(),builder); +// } +// Stream stream = builder.build(); +// return stream; +// } +// +// private void fillStream(String key, Object o,Stream.Builder builder){ +// if(o instanceof JSONObject){ +// for(Entry e:((JSONObject) o).map.entrySet()){ +// fillStream(e.getKey(),e.getValue(),builder); +// } +// }else if (o instanceof JSONArray){ +// for(int i=0; i<((JSONArray) o).length();i++){ +// fillStream(key,((JSONArray) o).get(i),builder); +// } +// }else { +// JSONObject newObj = new JSONObject(); +// newObj.put(key,o); +// builder.accept(newObj); +// } +// } + /** * Append values to the array under a key. If the key does not exist in the * JSONObject, then the key is put in the JSONObject with its value being a diff --git a/src/test/java/org/json/junit/Milestone4Test.java b/src/test/java/org/json/junit/Milestone4Test.java deleted file mode 100644 index 5ffc12a..0000000 --- a/src/test/java/org/json/junit/Milestone4Test.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.json.junit; - -import org.json.JSONObject; -import org.json.XML; -import org.junit.Test; - -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.junit.Assert.assertEquals; - -// Add streaming methods to the library that allow the client code to chain operations on JSON nodes - -public class Milestone4Test { - @Test - public void streamTestPrint() { - String xml = "\n" + - "\n" + - " Crista \n" + - " Crista Lopes\n" + - "
\n" + - " Ave of Nowhere\n" + - " 92614\n" + - "
\n" + - "
"; - - String xml1 = "\n" + - "Crista \n"; - String xml2 = "\n" + - "
\n" + - " Ave of Nowhere\n" + - " 92614\n" + - "
\n"; - String xml3 = "\n" + - "92614\n"; - String xml4 = "\n" + - "Ave of Nowhere\n"; - String xml5 = "\n" + - "Crista Lopes\n"; - - JSONObject[] xmlArray = {XML.toJSONObject(xml), - XML.toJSONObject(xml1), - XML.toJSONObject(xml2), - XML.toJSONObject(xml3), - XML.toJSONObject(xml4), - XML.toJSONObject(xml5), - }; - JSONObject jsonObject = - XML.toJSONObject(xml); - Stream stream = jsonObject.toStream(); - List l = stream.collect(Collectors.toList()); - int i = 0; - for (JSONObject node : l) { - assertEquals(node.toString(), xmlArray[i].toString()); - i++; - } - } - - @Test - public void streamTestfilter() { - String xml = "\n" + - "\n" + - "\n" + - " Crista \n" + - " Crista Lopes\n" + - "
\n" + - " Ave of Nowhere\n" + - " 92614\n" + - "
\n" + - "
" + - "\n" + - " Eric \n" + - " Eric\n" + - "
\n" + - " Verano\n" + - " 92617\n" + - "
\n" + - "
" + - "
"; - - - String xml1 = "\n" + - "92617\n"; - - - JSONObject[] xmlArray = { - XML.toJSONObject(xml1), - }; - JSONObject jsonObject = - XML.toJSONObject(xml); - Stream stream = jsonObject.toStream(); - int i = 0; - List l = stream.filter(node -> node.has("zipcode")).collect(Collectors.toList()); - for (JSONObject j : l) { - assertEquals(xmlArray[i].toString(), j.toString()); - i++; - } - } - - @Test - public void streamGetKey() { - String xml = "\n" + - "\n" + - " Crista \n" + - " Crista Lopes\n" + - "
\n" + - " Ave of Nowhere\n" + - " 92614\n" + - "
\n" + - "
"; - List> expect = new ArrayList<>(); - Set set1 = new HashSet<>(Arrays.asList("contact")); - Set set2 = new HashSet<>(Arrays.asList("nick")); - Set set3 = new HashSet<>(Arrays.asList("address")); - Set set4 = new HashSet<>(Arrays.asList("zipcode")); - Set set5 = new HashSet<>(Arrays.asList("street")); - Set set6 = new HashSet<>(Arrays.asList("name")); - expect.add(set1); - expect.add(set2); - expect.add(set3); - expect.add(set4); - expect.add(set5); - expect.add(set6); - JSONObject jsonObject = - XML.toJSONObject(xml); - Stream stream = jsonObject.toStream(); - int i = 0; - List> l = stream.map(node -> node.keySet()).collect(Collectors.toList()); - for (Set s : l) { - assertEquals(expect.get(i), s); - i++; - } - } -} \ No newline at end of file From cde9ca7240fe4a6d53b3de023e0d6eb7b210cd55 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Mon, 27 Feb 2023 00:22:38 -0800 Subject: [PATCH 41/48] Milestone4_Readme upload --- Milestone4_Readme.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Milestone4_Readme.md diff --git a/Milestone4_Readme.md b/Milestone4_Readme.md new file mode 100644 index 0000000..5c620e6 --- /dev/null +++ b/Milestone4_Readme.md @@ -0,0 +1,28 @@ +# Milestone4 + +### toStream() methods is added in src/main/java/org.json/JSONObject.java at [line 494] + +* toStream() method returns a stream of JSONObjects inside the input JSONObject. It returns the elements in DFS manner. + +For example, the input JSONObject is as follows: +
+{"contact": {
+"nick": "Crista",
+"address": {
+"zipcode": 92614,
+"street": "Ave of Nowhere"
+},
+"name": "Crista Lopes"
+}}
+
+ +The expected output stream should look like this: +
+{"nick":"Crista"}
+{"address":{"zipcode":92614,"street":"Ave of Nowhere"}}
+{"zipcode":92614}
+{"street":"Ave of Nowhere"}
+{"name":"Crista Lopes"}
+
+ +### Unit Tests are added in src/test/java/org.json.junit/Milestone4Test.java From 28bd7ab2913c99e4b72e90f3060747372faf6e94 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Mon, 27 Feb 2023 00:25:51 -0800 Subject: [PATCH 42/48] Milestone4_Readme update --- Milestone4_Readme.md | 1 + src/main/java/M4.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Milestone4_Readme.md b/Milestone4_Readme.md index 5c620e6..9519673 100644 --- a/Milestone4_Readme.md +++ b/Milestone4_Readme.md @@ -18,6 +18,7 @@ For example, the input JSONObject is as follows: The expected output stream should look like this:
+{"contact":{"nick":"Crista","address":{"zipcode":92614,"street":"Ave of Nowhere"},"name":"Crista Lopes"}}
 {"nick":"Crista"}
 {"address":{"zipcode":92614,"street":"Ave of Nowhere"}}
 {"zipcode":92614}
diff --git a/src/main/java/M4.java b/src/main/java/M4.java
index 9196f26..533850c 100644
--- a/src/main/java/M4.java
+++ b/src/main/java/M4.java
@@ -87,7 +87,7 @@ public static void main(String[] args) {
 
         try {
 
-            JSONObject obj = XML.toJSONObject(xmlString3);
+            JSONObject obj = XML.toJSONObject(xmlString2);
             Stream stream = obj.toStream();
 
             //System.out.println(obj);

From a4f72035c86b5812776fbb9d92319f5868b5c98d Mon Sep 17 00:00:00 2001
From: DinjerChang 
Date: Mon, 27 Feb 2023 00:51:57 -0800
Subject: [PATCH 43/48] milestone4 test done

---
 .../java/org/json/junit/Milestone4Test.java   | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 src/test/java/org/json/junit/Milestone4Test.java

diff --git a/src/test/java/org/json/junit/Milestone4Test.java b/src/test/java/org/json/junit/Milestone4Test.java
new file mode 100644
index 0000000..f48bd5c
--- /dev/null
+++ b/src/test/java/org/json/junit/Milestone4Test.java
@@ -0,0 +1,64 @@
+package org.json.junit;
+
+import org.json.JSONObject;
+import org.json.XML;
+import org.junit.Test;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+import static org.junit.Assert.*;
+
+// Add streaming methods to the library that allow the client code to chain operations on JSON nodes
+
+public class Milestone4Test {
+
+
+    @Test
+    public void testForEach() {
+        JSONObject obj = XML.toJSONObject("AAAASmith" +
+                "BBBBSmith");
+
+        obj.toStream().forEach(node -> {
+
+            for (String key : node.keySet()) {
+//                System.out.println(key);
+                Object value = node.get(key);
+                node.remove(key);
+                node.put(key + "_postfix", value);
+
+            }
+            node.keySet().stream().forEach(modifiedKey -> assertTrue(modifiedKey.contains("_postfix")));
+//            System.out.println(node);
+        });
+    }
+
+    @Test
+    public void testFilter() {
+        JSONObject obj = XML.toJSONObject("AAAASmith" +
+                "BBBBSmith");
+        List JOwithTitles = obj.toStream().filter(node -> node.has("author")).collect(Collectors.toList());
+//        System.out.println(JOwithTitles);
+        List expected = new ArrayList<>();
+        JSONObject jo1 = XML.toJSONObject("ASmith");
+        JSONObject jo2 = XML.toJSONObject("BSmith");
+        expected.add(jo1);
+        expected.add(jo2);
+        for (int i = 0; i < JOwithTitles.size(); i++) {
+            assertEquals(expected.get(i).get("author"), JOwithTitles.get(i).get("author"));
+        }
+    }
+
+    @Test
+    public void testMap() {
+        JSONObject obj = XML.toJSONObject("AAAASmith" +
+                "BBBBSmith");
+        List titles = obj.toStream().map(node ->
+                (node.has("title")) ? node.getString("title") : null).filter(res -> res != null).collect(Collectors.toList());
+        List expected = new ArrayList<>();
+        expected.add("AAA");
+        expected.add("BBB");
+        assertEquals(expected, titles);
+    }
+}
\ No newline at end of file

From cabd7068bac1fb8fe556c4fd1d6d958aee46a95a Mon Sep 17 00:00:00 2001
From: DinjerChang 
Date: Mon, 27 Feb 2023 00:57:13 -0800
Subject: [PATCH 44/48] milestone 4 readme done

---
 Milestone4_Readme.md | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Milestone4_Readme.md b/Milestone4_Readme.md
index 9519673..36d8916 100644
--- a/Milestone4_Readme.md
+++ b/Milestone4_Readme.md
@@ -27,3 +27,8 @@ The expected output stream should look like this:
 
### Unit Tests are added in src/test/java/org.json.junit/Milestone4Test.java + +`testForEach()` at line 19 ~ 34 +`testFilter()` at line 38 ~ 51 +`testMap()` at line 54 ~ 63 + From 6a032c782a9d8522d078ed8144b8c56491319318 Mon Sep 17 00:00:00 2001 From: eric052199 Date: Fri, 10 Mar 2023 17:20:23 -0800 Subject: [PATCH 45/48] milestone 5 test added --- .../java/org/json/junit/Milestone5Test.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/test/java/org/json/junit/Milestone5Test.java diff --git a/src/test/java/org/json/junit/Milestone5Test.java b/src/test/java/org/json/junit/Milestone5Test.java new file mode 100644 index 0000000..b5f510e --- /dev/null +++ b/src/test/java/org/json/junit/Milestone5Test.java @@ -0,0 +1,64 @@ +package org.json.junit; + +import org.json.JSONObject; +import org.json.XML; +import org.junit.Test; +import static org.junit.Assert.*; +import java.io.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.function.Function; + +// Add asynchronous methods to the library that allow the client code to proceed, while specifying what to do when the JSONObject becomes available. +public class Milestone5Test { + /** + * Test on "Future toJSONObjectMS5(Reader reader, Function f, Consumer c)" + * Test whether the function can handle with XML successfully and return the correct output + */ + + String xml ="\n"+ + "\n"+ + " Crista \n"+ + " Crista Lopes\n" + + "
\n" + + " Ave of Nowhere\n" + + " 92614\n" + + "
\n" + + "
"; + + String expect = "{\"SWE262_contact\":{\"SWE262_name\":\"Crista Lopes\",\"SWE262_nick\":\"Crista\",\"SWE262_address\":{\"SWE262_street\":\"Ave of Nowhere\",\"SWE262_zipcode\":92614}}}"; + + @Test + public void testSuccess(){ + try { + Reader reader = new StringReader(xml); + Function f = (key) -> "SWE262_" + key; + Consumer c = (e) -> e.printStackTrace(); + + // call the method to get future + Future future = XML.toJSONObject(reader, f, c); + JSONObject current = future.get(); + + // check equals + assertEquals(current.toString(), expect); + + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * Test on "Future toJSONObjectMS5(Reader reader, Function k, Consumer c)" + * Test the exception occasion, expect print "No input function!" and future return NULL + */ + @Test + public void shouldThrowExecutionException() { + Reader reader = new StringReader(xml); + Consumer c = (e) -> System.out.println("No input function!"); + Future future = XML.toJSONObject(reader, null, c); + assertNull(future); + } +} \ No newline at end of file From e6329d52dd2a493fe43d8c7419f043afc772e831 Mon Sep 17 00:00:00 2001 From: DinjerChang Date: Fri, 10 Mar 2023 17:29:15 -0800 Subject: [PATCH 46/48] milestone 5 main function done --- src/main/java/org/json/JSONObject.java | 27 +----------------- src/main/java/org/json/XML.java | 38 +++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index 36c67ce..0912117 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -490,7 +490,7 @@ public JSONObject accumulate(String key, Object value) throws JSONException { } return this; } - + // milestone 4 public Stream toStream() { return StreamSupport.stream(this.spliterator(), false); } @@ -595,31 +595,6 @@ public Spliterator trySplit() { } } -// public Stream toStream(){ -// Stream.Builder builder = Stream.builder(); -// Set> entrySet = this.entrySet(); -// for(Entry e:entrySet){ -// fillStream(e.getKey(),e.getValue(),builder); -// } -// Stream stream = builder.build(); -// return stream; -// } -// -// private void fillStream(String key, Object o,Stream.Builder builder){ -// if(o instanceof JSONObject){ -// for(Entry e:((JSONObject) o).map.entrySet()){ -// fillStream(e.getKey(),e.getValue(),builder); -// } -// }else if (o instanceof JSONArray){ -// for(int i=0; i<((JSONArray) o).length();i++){ -// fillStream(key,((JSONArray) o).get(i),builder); -// } -// }else { -// JSONObject newObj = new JSONObject(); -// newObj.put(key,o); -// builder.accept(newObj); -// } -// } /** * Append values to the array under a key. If the key does not exist in the diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 678c8b6..f760b3c 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -12,6 +12,8 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.concurrent.*; +import java.util.function.Consumer; import java.util.function.Function; @@ -691,7 +693,7 @@ public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration conf return jo; } - + // milestone 2 P1 public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JSONException { JSONObject jo = new JSONObject(); XMLTokener x = new XMLTokener(reader); @@ -718,6 +720,7 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path) throws JS return jo; } + // milestone 2 P2 public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObject replacement) throws JSONException { JSONObject jo = new JSONObject(); XMLTokener x = new XMLTokener(reader); @@ -738,6 +741,7 @@ public static JSONObject toJSONObject(Reader reader, JSONPointer path, JSONObjec return jo; } + // milestone 3 public static JSONObject toJSONObject(Reader reader, Function keyTransformer) { // check string condition String res1 = keyTransformer.apply("res1"); @@ -1563,6 +1567,7 @@ private static boolean parse2(XMLTokener x, JSONObject context, String name, XML } } + // milestone 3 private static boolean parse3(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, Function keyTransformer) throws JSONException { char c; @@ -1773,4 +1778,35 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML } } } + + /* + milestone 5 + + When a task is submitted to the ExecutorService, it is added to a queue of tasks to be executed. + The ExecutorService then uses a pool of worker threads to execute the tasks in the queue. + + The ExecutorService also provides a way to obtain a Future object when a task is submitted. + A Future represents the result of an asynchronous computation, and allows the calling thread to continue executing while the task is being processed by the worker thread. + The calling thread can later retrieve the result of the computation by calling the get() method on the Future. + */ + + public static Future toJSONObject(Reader reader, Function keyTransformer, + Consumer exceptionHandler) { + + ExecutorService executor = Executors.newSingleThreadExecutor(); + + Future future; + if (keyTransformer == null) { + exceptionHandler.accept(new Exception()); + return null; + } + //milestone 3 function using in lambda expression body as a task + future = executor.submit(()->XML.toJSONObject(reader, keyTransformer)); + + if (future.isDone()) { + executor.shutdown(); + } + return future; + } + } From 6f202c72b6612ce35856aab9d18b3e6ba38b7b6a Mon Sep 17 00:00:00 2001 From: eric052199 Date: Fri, 10 Mar 2023 17:40:01 -0800 Subject: [PATCH 47/48] milestone 5 readme added --- Milestone5_Readme.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Milestone5_Readme.md diff --git a/Milestone5_Readme.md b/Milestone5_Readme.md new file mode 100644 index 0000000..c46f351 --- /dev/null +++ b/Milestone5_Readme.md @@ -0,0 +1,6 @@ +# Milestone5 + +### toJSONObject() method is added in src/main/java/org.json/XML.java +* ```Future toJSONObject(Reader reader, Function keyTransformer, Consumer exceptionHandler)``` method returns a Future which contains the JSONObject info or NUll. + +### Unit Tests are added in src/test/java/org.json.junit/Milestone5Test.java \ No newline at end of file From 20d046723998d9fbdac075397537363d6a6bc655 Mon Sep 17 00:00:00 2001 From: DinjerChang Date: Fri, 10 Mar 2023 19:35:13 -0800 Subject: [PATCH 48/48] parse modified --- src/main/java/org/json/XML.java | 215 +--------------------- src/test/java/org/json/junit/XMLTest.java | 14 +- 2 files changed, 6 insertions(+), 223 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index f760b3c..56318df 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -262,209 +262,6 @@ public static void noSpace(String string) throws JSONException { * @return true if the close tag is processed. * @throws JSONException */ - private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config) - 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(); -// System.out.println("token: " + token); - - // "); - 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 return True - 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) { - 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 - for (; ; ) { - 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) { - // Nested element - if (parse(x, jsonObject, tagName, config)) { - 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) { - context.accumulate(tagName, ""); - } else if (jsonObject.length() == 1 - && jsonObject.opt(config.getcDataTagName()) != null) { - context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); - } else { - context.accumulate(tagName, jsonObject); - } - } - - return false; - } - } - } - } else { - throw x.syntaxError("Misshaped tag"); - } - } - } - } /** * This method tries to convert the given string value to the target object @@ -687,7 +484,7 @@ public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration conf while (x.more()) { x.skipPast("<"); if (x.more()) { - parse(x, jo, null, config); + parse(x, jo, null, config,s->s); } } return jo; @@ -763,7 +560,7 @@ public static JSONObject toJSONObject(Reader reader, Function ke while (x.more()) { x.skipPast("<"); if (x.more()) { - parse3(x, jo, null, XMLParserConfiguration.ORIGINAL, keyTransformer); + parse(x, jo, null, XMLParserConfiguration.ORIGINAL, keyTransformer); } } return jo; @@ -1335,8 +1132,7 @@ private static boolean parse2(XMLTokener x, JSONObject context, String name, XML // << token = x.nextToken(); -// System.out.println(currPath); -// System.out.println("token: " + token); + // keyTransformer) + private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, Function keyTransformer) throws JSONException { char c; int i; @@ -1743,7 +1538,7 @@ private static boolean parse3(XMLTokener x, JSONObject context, String name, XML } else if (token == LT) { // Nested element - if (parse3(x, jsonObject, tagName, config, keyTransformer)) { + if (parse(x, jsonObject, tagName, config, keyTransformer)) { String newTagName = keyTransformer.apply(tagName); if (config.getForceList().contains(tagName)) { diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index cbd4b4f..16cb8d6 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1391,19 +1391,7 @@ public void testM2P2WrongPath() { String errorMessage = "Path not found"; assertEquals(errorMessage, e.getMessage()); } -// exceptionRule.expect(JSONPointerException.class); -// exceptionRule.expectMessage("Path not found"); -// String xmlString = "\n"+ -// "\n"+ -// " Crista \n"+ -// " Crista Lopes\n" + -// "
\n" + -// " Ave of Nowhere\n" + -// " 92614\n" + -// "
\n" + -// "
"; -// JSONObject replacement = XML.toJSONObject("Ave of the Arts\n"); -// XML.toJSONObject(new StringReader(xmlString), new JSONPointer("/contact/address/nooo"), replacement); + } @Test