Skip to content

Commit

Permalink
JsonParser - resolving [] after close array ] is processed. Working t…
Browse files Browse the repository at this point in the history
…oward eliminating 2nd pass through graph.
  • Loading branch information
jdereg committed Oct 25, 2024
1 parent db3c717 commit c6f00f8
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src/main/java/com/cedarsoftware/io/JsonIo.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ public static <T> T toObjects(InputStream in, ReadOptions readOptions, Class<T>
throw new JsonIoException(e);
}
finally {
if (readOptions.isCloseStream()) {
if (jr != null) {
if (jr != null) {
if (readOptions.isCloseStream()) {
jr.close();
}
}
Expand Down
19 changes: 3 additions & 16 deletions src/main/java/com/cedarsoftware/io/JsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/cedarsoftware/io/JsonReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ public JsonReader(ReadOptions readOptions) {
public <T> T readObject(Class<T> rootType) {
T returnValue;
try {
// if (readOptions.isReturningJsonObjects()) {
// rootType = null;
// }
returnValue = (T) parser.readValue(rootType);
if (returnValue == null) {
return null; // easy, done.
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/com/cedarsoftware/io/ObjectResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public void traverseFields(final JsonObject jsonObj)
handleMissingField(jsonObj, rhs, key);
} //else no handler so ignore.
}
jsonObj.setFinished();
}

/**
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -425,6 +429,7 @@ protected void traverseArray(final JsonObject jsonObj)
}
}
}
jsonObj.setFinished();
jsonObj.clear();
}

Expand Down
21 changes: 19 additions & 2 deletions src/main/java/com/cedarsoftware/io/Resolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ public <T> T traverseJsonObject(JsonObject root) {
}
visited.put(jsonObj, null);
traverseSpecificType(jsonObj);
// jsonObj.setFinished();
}
return (T) root.getTarget();
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -689,4 +690,20 @@ Object createJavaFromJson(Object root) {
return root;
}
}

Object resolveArray(Class<?> suggestedType, List<Object> 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;
}
}
17 changes: 13 additions & 4 deletions src/test/java/com/cedarsoftware/io/NoTypeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/test/resources/noTypes/persons.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"age": 50,
"salary": 225000.0
},
{"name": "Jane",
{
"name": "Jane",
"age": 40,
"salary": 314000.0
}
Expand Down
2 changes: 1 addition & 1 deletion user-guide-readOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c6f00f8

Please sign in to comment.