From c6f00f8909f53e54659cc54ec7c72a25a7b3935e Mon Sep 17 00:00:00 2001 From: John DeRegnaucourt Date: Fri, 25 Oct 2024 08:50:29 -0400 Subject: [PATCH] JsonParser - resolving [] after close array ] is processed. Working toward eliminating 2nd pass through graph. --- .../java/com/cedarsoftware/io/JsonIo.java | 4 ++-- .../java/com/cedarsoftware/io/JsonParser.java | 19 +++-------------- .../java/com/cedarsoftware/io/JsonReader.java | 3 +++ .../com/cedarsoftware/io/ObjectResolver.java | 7 ++++++- .../java/com/cedarsoftware/io/Resolver.java | 21 +++++++++++++++++-- .../java/com/cedarsoftware/io/NoTypeTest.java | 17 +++++++++++---- src/test/resources/noTypes/persons.json | 3 ++- user-guide-readOptions.md | 2 +- 8 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/cedarsoftware/io/JsonIo.java b/src/main/java/com/cedarsoftware/io/JsonIo.java index ee584148c..5546681f0 100644 --- a/src/main/java/com/cedarsoftware/io/JsonIo.java +++ b/src/main/java/com/cedarsoftware/io/JsonIo.java @@ -192,8 +192,8 @@ public static T toObjects(InputStream in, ReadOptions readOptions, Class throw new JsonIoException(e); } finally { - if (readOptions.isCloseStream()) { - if (jr != null) { + if (jr != null) { + if (readOptions.isCloseStream()) { jr.close(); } } diff --git a/src/main/java/com/cedarsoftware/io/JsonParser.java b/src/main/java/com/cedarsoftware/io/JsonParser.java index 1dc5b7e3e..69d271a0d 100644 --- a/src/main/java/com/cedarsoftware/io/JsonParser.java +++ b/src/main/java/com/cedarsoftware/io/JsonParser.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.io.Reader; -import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; @@ -77,6 +76,7 @@ protected boolean removeEldestEntry(Map.Entry eldest) { private int curParseDepth = 0; private final boolean allowNanAndInfinity; private final int maxParseDepth; + private final Resolver resolver; private final ReadOptions readOptions; private final ReferenceTracker references; @@ -140,6 +140,7 @@ protected boolean removeEldestEntry(Map.Entry eldest) { JsonParser(FastReader reader, Resolver resolver) { input = reader; + this.resolver = resolver; readOptions = resolver.getReadOptions(); references = resolver.getReferences(); maxParseDepth = readOptions.getMaxDepth(); @@ -298,21 +299,7 @@ private Object readArray(Class suggestedType) throws IOException { } --curParseDepth; - -// JsonObject jsonArray = new JsonObject(); -// jsonArray.setJsonArray(list.toArray()); -// jsonArray.setTarget(Array.newInstance(suggestedType == null ? Object.class : suggestedType, list.size())); -// return jsonArray; - if (suggestedType == null || suggestedType == Object.class) { - // No suggested type, so use Object[] - return list.toArray(); - } else { - // suggested type is the component type of the array, for example "Person.class" for Person[], "Location.class" for Location[], etc. - JsonObject jsonArray = new JsonObject(); - jsonArray.setJsonArray(list.toArray()); - jsonArray.setTarget(Array.newInstance(suggestedType, list.size())); - return jsonArray; - } + return resolver.resolveArray(suggestedType, list); } /** diff --git a/src/main/java/com/cedarsoftware/io/JsonReader.java b/src/main/java/com/cedarsoftware/io/JsonReader.java index cd304ddf0..3ea38435e 100644 --- a/src/main/java/com/cedarsoftware/io/JsonReader.java +++ b/src/main/java/com/cedarsoftware/io/JsonReader.java @@ -207,6 +207,9 @@ public JsonReader(ReadOptions readOptions) { public T readObject(Class rootType) { T returnValue; try { +// if (readOptions.isReturningJsonObjects()) { +// rootType = null; +// } returnValue = (T) parser.readValue(rootType); if (returnValue == null) { return null; // easy, done. diff --git a/src/main/java/com/cedarsoftware/io/ObjectResolver.java b/src/main/java/com/cedarsoftware/io/ObjectResolver.java index 1c26e7b5c..155e26052 100644 --- a/src/main/java/com/cedarsoftware/io/ObjectResolver.java +++ b/src/main/java/com/cedarsoftware/io/ObjectResolver.java @@ -97,6 +97,7 @@ public void traverseFields(final JsonObject jsonObj) handleMissingField(jsonObj, rhs, key); } //else no handler so ignore. } + jsonObj.setFinished(); } /** @@ -294,7 +295,10 @@ protected void traverseCollection(final JsonObject jsonObj) col.add(null); } else if ((special = readWithFactoryIfExists(element, null)) != null) { col.add(special); - } else if (element instanceof String || element instanceof Boolean || element instanceof Double || element instanceof Long) { // Allow Strings, Booleans, Longs, and Doubles to be "inline" without Java object decoration (@id, @type, etc.) + } else if (element instanceof String || element instanceof Boolean || element instanceof Double || element instanceof Long) { + // TODO: (String) element, shouldn't that cause a ClassCastException when Boolean, Doouble, or Long. Does + // the readWithFactoryIfExists pick up these types? If so, does this line ever get hit? + // Allow Strings, Booleans, Longs, and Doubles to be "inline" without Java object decoration (@id, @type, etc.) col.add(mayEnumClass == null ? element : Enum.valueOf(mayEnumClass, (String) element)); } else if (element.getClass().isArray()) { final JsonObject jObj = new JsonObject(); @@ -425,6 +429,7 @@ protected void traverseArray(final JsonObject jsonObj) } } } + jsonObj.setFinished(); jsonObj.clear(); } diff --git a/src/main/java/com/cedarsoftware/io/Resolver.java b/src/main/java/com/cedarsoftware/io/Resolver.java index 60cdc1e87..30447ef2d 100644 --- a/src/main/java/com/cedarsoftware/io/Resolver.java +++ b/src/main/java/com/cedarsoftware/io/Resolver.java @@ -242,6 +242,7 @@ public T traverseJsonObject(JsonObject root) { } visited.put(jsonObj, null); traverseSpecificType(jsonObj); +// jsonObj.setFinished(); } return (T) root.getTarget(); } @@ -453,11 +454,11 @@ private Object createInstanceUsingType(JsonObject jsonObj) { /** * If a ClassFactory is associated to the passed in Class (clazz), then use the ClassFactory - * to create an instance. If a ClassFactory create the instance, it may optionall load + * to create an instance. If a ClassFactory creates the instance, it may optionally load * the values into the instance, using the values from the passed in JsonObject. If the * ClassFactory instance creates AND loads the object, it is indicated on the ClassFactory * by the isObjectFinal() method returning true. Therefore, the JsonObject instance that is - * loaded, is marked with 'isFinished=true' so that no more process is needed for this instance. + * loaded, is marked with 'isFinished=true' so that no more processing is needed for this instance. */ Object createInstanceUsingClassFactory(Class c, JsonObject jsonObj) { // If a ClassFactory exists for a class, use it to instantiate the class. The ClassFactory @@ -689,4 +690,20 @@ Object createJavaFromJson(Object root) { return root; } } + + Object resolveArray(Class suggestedType, List list) + { + if (suggestedType == null || suggestedType == Object.class) { + // No suggested type, so use Object[] + return list.toArray(); + } + + JsonObject jsonArray = new JsonObject(); + jsonArray.setTarget(Array.newInstance(suggestedType, list.size())); + jsonArray.setJsonArray(list.toArray()); + traverseJsonObject(jsonArray); + jsonArray.setFinished(); +// return jsonArray.getTarget(); + return jsonArray; + } } \ No newline at end of file diff --git a/src/test/java/com/cedarsoftware/io/NoTypeTest.java b/src/test/java/com/cedarsoftware/io/NoTypeTest.java index 80c866f78..b65282b13 100644 --- a/src/test/java/com/cedarsoftware/io/NoTypeTest.java +++ b/src/test/java/com/cedarsoftware/io/NoTypeTest.java @@ -99,7 +99,8 @@ void personsTestWithPersonArrayRootAsNativeJsonObjects() String json = MetaUtils.loadResourceAsString("noTypes/persons.json"); try { - TestUtil.toObjects(json, new ReadOptionsBuilder().returnAsNativeJsonObjects().build(), Person[].class); + Object o = TestUtil.toObjects(json, new ReadOptionsBuilder().returnAsNativeJsonObjects().build(), Person[].class); + System.out.println("o = " + o); fail(); } catch (JsonIoException e) { assert e.getMessage().contains("Either use null for root, or set"); @@ -206,10 +207,18 @@ void testStringArrayHeteroItems() { @Test void testStringArrayHeteroItemsNoChance() { - String json = "[\"foo\", [null]]"; + // String[] does not allow an array as an element + assertThrows(JsonIoException.class, () -> {String[] strings = TestUtil.toObjects("[\"foo\", [null]]", String[].class); }); - // String[] - assertThrows(JsonIoException.class, () -> {String[] strings = TestUtil.toObjects(json, String[].class); }); + String json = "[\"foo\", null]"; + + // Explicit String[] + String[] strings = TestUtil.toObjects(json, String[].class); + assert strings.length == 2; + assert strings[0].equals("foo"); + assert strings[1] == null; + + json = "[\"foo\", [null]]"; // Implied Object[] Object[] array = TestUtil.toObjects(json, null); diff --git a/src/test/resources/noTypes/persons.json b/src/test/resources/noTypes/persons.json index 1be4cf83f..3c3f64794 100644 --- a/src/test/resources/noTypes/persons.json +++ b/src/test/resources/noTypes/persons.json @@ -4,7 +4,8 @@ "age": 50, "salary": 225000.0 }, - {"name": "Jane", + { + "name": "Jane", "age": 40, "salary": 314000.0 } diff --git a/user-guide-readOptions.md b/user-guide-readOptions.md index d80a0e7a3..bb87bc256 100644 --- a/user-guide-readOptions.md +++ b/user-guide-readOptions.md @@ -89,7 +89,7 @@ vectors. Set the maximum number of `Class` to `Field` mappings and `Class` to filter mappings. This will allow infrequently used `Class's` to drop from the cache - they will be dynamically added back if not in the cache. Reduces operational memory foot print. > #### `int` lruSize() ->- [ ] Return the LRU size +>- [ ] Return the LRU size. Default is 1000. > #### `ReadOptionsBuilder` lruSize(`int size`) >- [ ] Set the max LRU cache size