Skip to content

Commit cc6bf54

Browse files
committed
- removed unused ClassFactories.
- added ability to specify notCustmoRead/Written classes in notCustomRead.txt and notCustomWritten.txt
1 parent b6428fb commit cc6bf54

22 files changed

+524
-345
lines changed

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Key Features:
1010
- Supports polymorphic types and complex object graphs
1111
- Zero external dependencies (other than java-util)
1212
- Fully compatible with both JPMS and OSGi environments
13-
- Lightweight (`json-io.jar` is 256K, `java-util` is 449K)
13+
- Lightweight (`json-io.jar` is 255K, `java-util` is 450K)
1414
- Compatible with JDK 1.8 through JDK 23
1515
- Extensive configuration options via `ReadOptionsBuilder` and `WriteOptionsBuilder`
1616
- Featured on [json.org](http://json.org)
@@ -31,15 +31,15 @@ ___
3131
To include in your project:
3232
##### Gradle
3333
```groovy
34-
implementation 'com.cedarsoftware:json-io:4.51.0'
34+
implementation 'com.cedarsoftware:json-io:4.52.0'
3535
```
3636

3737
##### Maven
3838
```xml
3939
<dependency>
4040
<groupId>com.cedarsoftware</groupId>
4141
<artifactId>json-io</artifactId>
42-
<version>4.51.0</version>
42+
<version>4.52.0</version>
4343
</dependency>
4444
```
4545
___
@@ -51,18 +51,18 @@ ___
5151
>#### [Revision History](/changelog.md)
5252
5353
## Releases
54-
>### 4.51.0 (current)
55-
>- [ ] **Version**: [4.51.0](https://www.javadoc.io/doc/com.cedarsoftware/json-io/4.51.0/index.html)
54+
>### 4.52.0 (current)
55+
>- [ ] **Version**: [4.52.0](https://www.javadoc.io/doc/com.cedarsoftware/json-io/4.52.0/index.html)
5656
>- [ ] **Bundling**: Both JPMS (Java Platform Module System) and OSGi (Open Service Gateway initiative)
5757
>- [ ] **Maintained**: Fully
5858
>- [ ] **Java Package**: com.cedarsoftware.io
5959
>- [ ] **Java**: JDK1.8+ (Class file 52 format, includes module-info.class - multi-release JAR)
6060
>- [ ] **API**
61-
> - Static methods on [JsonIo](https://www.javadoc.io/doc/com.cedarsoftware/json-io/4.51.0/com/cedarsoftware/io/JsonIo.html): [toJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.51.0/com/cedarsoftware/io/JsonIo.html#toJson(java.lang.Object,com.cedarsoftware.io.WriteOptions)), [toJava()](https://www.javadoc.io/doc/com.cedarsoftware/json-io/latest/com/cedarsoftware/io/JsonIo.html#toJava(com.cedarsoftware.io.JsonObject,com.cedarsoftware.io.ReadOptions)), [formatJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.51.0/com/cedarsoftware/io/JsonIo.html#formatJson(java.lang.String)), [deepCopy()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.51.0/com/cedarsoftware/io/JsonIo.html#deepCopy(java.lang.Object,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions))
61+
> - Static methods on [JsonIo](https://www.javadoc.io/doc/com.cedarsoftware/json-io/4.52.0/com/cedarsoftware/io/JsonIo.html): [toJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.52.0/com/cedarsoftware/io/JsonIo.html#toJson(java.lang.Object,com.cedarsoftware.io.WriteOptions)), [toJava()](https://www.javadoc.io/doc/com.cedarsoftware/json-io/latest/com/cedarsoftware/io/JsonIo.html#toJava(com.cedarsoftware.io.JsonObject,com.cedarsoftware.io.ReadOptions)), [formatJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.52.0/com/cedarsoftware/io/JsonIo.html#formatJson(java.lang.String)), [deepCopy()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.52.0/com/cedarsoftware/io/JsonIo.html#deepCopy(java.lang.Object,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions))
6262
> - Use [ReadOptionsBuilder](/user-guide-readOptions.md) and [WriteOptionsBuilder](/user-guide-writeOptions.md) to configure `JsonIo`
63-
> - Use [JsonReader.ClassFactory](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.51.0/com/cedarsoftware/io/JsonReader.ClassFactory.html) for difficult classes (hard to instantiate & fill)
64-
> - Use [JsonWriter.JsonClassWriter](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.51.0/com/cedarsoftware/io/JsonWriter.JsonClassWriter.html) to customize the output JSON for a particular class
65-
>- [ ] Updates will be 4.52.0, 4.53.0, ...
63+
> - Use [JsonReader.ClassFactory](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.52.0/com/cedarsoftware/io/JsonReader.ClassFactory.html) for difficult classes (hard to instantiate & fill)
64+
> - Use [JsonWriter.JsonClassWriter](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.52.0/com/cedarsoftware/io/JsonWriter.JsonClassWriter.html) to customize the output JSON for a particular class
65+
>- [ ] Updates will be 4.53.0, 4.54.0, ...
6666
>### 4.14.x (supported)
6767
>- [ ] **Version**: [4.14.3](https://www.javadoc.io/doc/com.cedarsoftware/json-io/4.14.3/index.html)
6868
>- [ ] **Bundling**: Both JPMS (Java Platform Module System) and OSGi (Open Service Gateway initiative)

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
### Revision History
2+
#### 4.52.0 CompactMap and CompactSet Enhancements
3+
* `CompactMap` and `CompactSet` JSON formats improved - match historical formats, except when using builder pattern.
4+
* `ByteBuffer and CharBuffer` converstions to/from `Map` added.
5+
* Performance improvements in JSON serialization and deserialization.
6+
* Code simplification related to instance creation for common types.
7+
* Updated [java-util](https://github.com/jdereg/java-util/blob/master/changelog.md) from `3.2.0` to `3.3.0.`
28
#### 4.51.0 CompactMap and CompactSet Enhancements
39
* **JSON Serialization Improvements**
410
* Implemented specialized JSON serialization for `CompactMap` and `CompactSet` with optimized format

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<groupId>com.cedarsoftware</groupId>
66
<artifactId>json-io</artifactId>
77
<packaging>bundle</packaging>
8-
<version>4.51.0</version>
8+
<version>4.52.0</version>
99
<description>Java JSON serialization</description>
1010
<url>https://github.com/jdereg/json-io</url>
1111

@@ -26,7 +26,7 @@
2626
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss.SSSZ</maven.build.timestamp.format>
2727
<!-- remove source encoding warnings from maven output -->
2828
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
29-
<version.java-util>3.2.0</version.java-util>
29+
<version.java-util>3.3.0</version.java-util>
3030
<!-- testing only -->
3131
<version.junit-jupiter-params>5.11.4</version.junit-jupiter-params>
3232
<version.assertj-core>3.27.3</version.assertj-core>

src/main/java/com/cedarsoftware/io/JsonReader.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,6 @@ private boolean isRootArray(Object value) {
326326
* Handles the case where the top-level element is an array (either a real Java array,
327327
* or a JsonObject that’s flagged as an array).
328328
*/
329-
@SuppressWarnings("unchecked")
330329
private Object handleArrayRoot(Type rootType, Object returnValue) {
331330
JsonObject rootObj;
332331

@@ -479,7 +478,6 @@ private <T> void verifyRootType(Type rootType) {
479478
* @return the resolved and, if necessary, converted object graph, or the raw {@code JsonObject}, depending on the mode
480479
* and convertibility.
481480
*/
482-
@SuppressWarnings("unchecked")
483481
private Object handleObjectRoot(Type rootType, JsonObject jsonObj) {
484482
boolean returnJson = readOptions.isReturningJsonObjects();
485483

src/main/java/com/cedarsoftware/io/JsonWriter.java

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
import com.cedarsoftware.io.reflect.Accessor;
2626
import com.cedarsoftware.util.ClassUtilities;
27+
import com.cedarsoftware.util.CompactMap;
28+
import com.cedarsoftware.util.CompactSet;
2729
import com.cedarsoftware.util.FastWriter;
2830

2931
import static com.cedarsoftware.io.JsonValue.ENUM;
@@ -240,9 +242,6 @@ private void tab(Writer output, int delta) throws IOException
240242
*/
241243
public boolean writeUsingCustomWriter(Object o, boolean showType, Writer output) {
242244
Class<?> c = o.getClass();
243-
if (writeOptions.isNotCustomWrittenClass(c)) {
244-
return false;
245-
}
246245

247246
try {
248247
return writeCustom(c, o, !writeOptions.isNeverShowingType() && showType, output);
@@ -251,6 +250,29 @@ public boolean writeUsingCustomWriter(Object o, boolean showType, Writer output)
251250
}
252251
}
253252

253+
private boolean isCustomWrittenClass(Class<?> c, Object o) {
254+
// Exit early if the class is explicitly marked as not-custom-written.
255+
if (writeOptions.isNotCustomWrittenClass(c)) {
256+
return false;
257+
}
258+
259+
// Exit early if there is no custom writer.
260+
if (writeOptions.getCustomWriter(c) == null) {
261+
return false;
262+
}
263+
264+
// Exit early if 'o' is a CompactSet, and it is the default CompactSet.
265+
if ((o instanceof CompactSet) && ((CompactSet<?>) o).isDefaultCompactSet()) {
266+
return false;
267+
}
268+
269+
// Exit early if 'o' is a CompactMap, and it is the default CompactMap.
270+
if ((o instanceof CompactMap) && ((CompactMap<?, ?>) o).isDefaultCompactMap()) {
271+
return false;
272+
}
273+
return true;
274+
}
275+
254276
/**
255277
* Write the passed in array element to the JSON output, if any only if, there is a custom writer
256278
* for the class of the instance 'o'.
@@ -262,17 +284,13 @@ public boolean writeUsingCustomWriter(Object o, boolean showType, Writer output)
262284
*/
263285
public boolean writeArrayElementIfMatching(Class<?> arrayComponentClass, Object o, boolean showType, Writer output)
264286
{
265-
if (!arrayComponentClass.isAssignableFrom(o.getClass()) || writeOptions.isNotCustomWrittenClass(o.getClass()))
266-
{
287+
if (!arrayComponentClass.isAssignableFrom(o.getClass())) {
267288
return false;
268289
}
269290

270-
try
271-
{
291+
try {
272292
return writeCustom(arrayComponentClass, o, showType, output);
273-
}
274-
catch (IOException e)
275-
{
293+
} catch (IOException e) {
276294
throw new JsonIoException("Unable to write custom formatted object as array element:", e);
277295
}
278296
}
@@ -286,14 +304,14 @@ public boolean writeArrayElementIfMatching(Class<?> arrayComponentClass, Object
286304
* @return true if the array element was written, false otherwise.
287305
*/
288306
protected boolean writeCustom(Class<?> clazz, Object o, boolean showType, Writer output) throws IOException {
307+
if (!isCustomWrittenClass(clazz, o)) {
308+
return false;
309+
}
289310
if (writeOptions.isNeverShowingType()) {
290311
showType = false;
291312
}
292-
JsonClassWriter closestWriter = writeOptions.getCustomWriter(o.getClass());
293313

294-
if (closestWriter == null) {
295-
return false;
296-
}
314+
JsonClassWriter closestWriter = writeOptions.getCustomWriter(o.getClass());
297315

298316
if (writeOptionalReference(o)) {
299317
return true;

src/main/java/com/cedarsoftware/io/ReadOptionsBuilder.java

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.regex.Pattern;
2020

2121
import com.cedarsoftware.io.factory.ArrayFactory;
22-
import com.cedarsoftware.io.factory.ConvertableFactory;
2322
import com.cedarsoftware.io.factory.EnumClassFactory;
2423
import com.cedarsoftware.io.factory.ThrowableFactory;
2524
import com.cedarsoftware.io.reflect.Injector;
@@ -31,6 +30,7 @@
3130
import com.cedarsoftware.util.ClassUtilities;
3231
import com.cedarsoftware.util.ClassValueMap;
3332
import com.cedarsoftware.util.ClassValueSet;
33+
import com.cedarsoftware.util.ConcurrentSet;
3434
import com.cedarsoftware.util.Convention;
3535
import com.cedarsoftware.util.StringUtilities;
3636
import com.cedarsoftware.util.convert.CommonValues;
@@ -62,11 +62,13 @@
6262
*/
6363
public class ReadOptionsBuilder {
6464

65+
// The BASE_* Maps are regular ConcurrentHashMap's because they are not constantly searched, otherwise they would be ClassValueMaps.
6566
private static final Map<Class<?>, JsonReader.JsonClassReader> BASE_READERS = new ConcurrentHashMap<>();
6667
private static final Map<Class<?>, JsonReader.ClassFactory> BASE_CLASS_FACTORIES = new ConcurrentHashMap<>();
6768
private static final Map<String, String> BASE_ALIAS_MAPPINGS = new ConcurrentHashMap<>();
6869
private static final Map<Class<?>, Class<?>> BASE_COERCED_TYPES = new ConcurrentHashMap<>();
69-
private static final Set<Class<?>> BASE_NON_REFS = ConcurrentHashMap.newKeySet();
70+
private static final Set<Class<?>> BASE_NON_REFS = new ConcurrentSet<>();
71+
private static final Set<Class<?>> BASE_NOT_CUSTOM_READ = new ConcurrentSet<>();
7072
private static final Map<Class<?>, Map<String, String>> BASE_NONSTANDARD_SETTERS = new ConcurrentHashMap<>();
7173
private static final Map<Class<?>, Set<String>> BASE_NOT_IMPORTED_FIELDS = new ConcurrentHashMap<>();
7274

@@ -84,6 +86,7 @@ public class ReadOptionsBuilder {
8486
loadBaseAliasMappings(ReadOptionsBuilder::addPermanentAlias);
8587
loadBaseCoercedTypes();
8688
loadBaseNonRefs();
89+
loadBaseNotCustomReadClasses();
8790
loadBaseFieldsNotImported();
8891
loadBaseNonStandardSetters();
8992

@@ -118,6 +121,7 @@ public ReadOptionsBuilder() {
118121
options.customReaderClasses.putAll(BASE_READERS);
119122
options.classFactoryMap.putAll(BASE_CLASS_FACTORIES);
120123
options.nonRefClasses.addAll(BASE_NON_REFS);
124+
options.notCustomReadClasses.addAll(BASE_NOT_CUSTOM_READ);
121125
options.excludedFieldNames.putAll(WriteOptionsBuilder.BASE_EXCLUDED_FIELD_NAMES);
122126
options.fieldsNotImported.putAll(BASE_NOT_IMPORTED_FIELDS);
123127
}
@@ -277,6 +281,18 @@ public static void addPermanentNonReferenceableClass(Class<?> clazz) {
277281
BASE_NON_REFS.add(clazz);
278282
}
279283

284+
/**
285+
* Add a class permanently (JVM lifecycle) to the Not-Custom reader list. All new ReadOptions instances created
286+
* will automatically start with this, and you will not need to add it into the ReadOptions through the
287+
* ReadOptionsBuilder each time.
288+
*
289+
* @param clazz Class to be added to the Not-Custom reader list. A class that is on this list, will not use
290+
* a Custom Reader or Class Factory when read from the JSON.
291+
*/
292+
public static void addPermanentNotCustomReadClass(Class<?> clazz) {
293+
BASE_NOT_CUSTOM_READ.add(clazz);
294+
}
295+
280296
/**
281297
* Add a field to a class that should not be imported. Any class's field added here will be excluded when read from
282298
* the JSON. The value will not be set (injected) into the associated Java instance.
@@ -302,19 +318,18 @@ public static void addPermanentNonStandardSetter(Class<?> clazz, String fieldNam
302318
/**
303319
* @return ReadOptions - built options
304320
*/
305-
@SuppressWarnings("unchecked")
306321
public ReadOptions build() {
307322
options.clearCaches();
308323
options.aliasTypeNames = Collections.unmodifiableMap(options.aliasTypeNames);
309-
options.coercedTypes = ((ClassValueMap)options.coercedTypes).unmodifiableView();
324+
options.coercedTypes = ((ClassValueMap<Class<?>>)options.coercedTypes).unmodifiableView();
310325
options.notCustomReadClasses = ((ClassValueSet)options.notCustomReadClasses).unmodifiableView();
311-
options.customReaderClasses = ((ClassValueMap)options.customReaderClasses).unmodifiableView();
312-
options.classFactoryMap = ((ClassValueMap)options.classFactoryMap).unmodifiableView();
326+
options.customReaderClasses = ((ClassValueMap<JsonReader.JsonClassReader>)options.customReaderClasses).unmodifiableView();
327+
options.classFactoryMap = ((ClassValueMap<JsonReader.ClassFactory>)options.classFactoryMap).unmodifiableView();
313328
options.nonRefClasses = ((ClassValueSet)options.nonRefClasses).unmodifiableView();
314329
options.converterOptions.converterOverrides = Collections.unmodifiableMap(options.converterOptions.converterOverrides);
315330
options.converterOptions.customOptions = Collections.unmodifiableMap(options.converterOptions.customOptions);
316-
options.excludedFieldNames = ((ClassValueMap)options.excludedFieldNames).unmodifiableView();
317-
options.fieldsNotImported = ((ClassValueMap)options.fieldsNotImported).unmodifiableView();
331+
options.excludedFieldNames = ((ClassValueMap<Set<String>>)options.excludedFieldNames).unmodifiableView();
332+
options.fieldsNotImported = ((ClassValueMap<Set<String>>)options.fieldsNotImported).unmodifiableView();
318333
options.fieldFilters = Collections.unmodifiableList(options.fieldFilters);
319334
options.injectorFactories = Collections.unmodifiableList(options.injectorFactories);
320335
options.customOptions = Collections.unmodifiableMap(options.customOptions);
@@ -766,9 +781,7 @@ private static void loadBaseClassFactory() {
766781
continue;
767782
}
768783

769-
if (factoryClassName.equalsIgnoreCase("Convertable")) {
770-
addPermanentClassFactory(clazz, new ConvertableFactory<>(clazz));
771-
} else if (factoryClassName.equalsIgnoreCase("ArrayFactory")) {
784+
if (factoryClassName.equalsIgnoreCase("ArrayFactory")) {
772785
addPermanentClassFactory(clazz, new ArrayFactory<>(clazz));
773786
} else {
774787
try {
@@ -1333,10 +1346,30 @@ private static void loadBaseNonRefs() {
13331346
for (String className : set) {
13341347
Class<?> loadedClass = ClassUtilities.forName(className, classLoader);
13351348

1336-
if (loadedClass != null) {
1349+
if (loadedClass == null) {
1350+
System.out.println("Class: " + className + " undefined. Cannot be used as non-referenceable class, listed in config/nonRefs.txt");
1351+
} else {
13371352
addPermanentNonReferenceableClass(loadedClass);
1353+
}
1354+
}
1355+
}
1356+
1357+
/**
1358+
* Load the list of classes that are intended to not (never) be read using a custom reader. This is useful to
1359+
* terminate a "custom reader" inheritance chain. For example, if you have a custom reader for a base class, and
1360+
* you don't want it to be used for a subclass, you can add the subclass to this list.
1361+
*/
1362+
private static void loadBaseNotCustomReadClasses() {
1363+
final Set<String> set = MetaUtils.loadSetDefinition("config/notCustomRead.txt");
1364+
final ClassLoader classLoader = ClassUtilities.getClassLoader(ReadOptionsBuilder.class);
1365+
1366+
for (String className : set) {
1367+
Class<?> loadedClass = ClassUtilities.forName(className, classLoader);
1368+
1369+
if (loadedClass == null) {
1370+
System.out.println("Class: " + className + " undefined. Cannot be used as to turn off custom reading for this class, listed in config/notCustomRead.txt");
13381371
} else {
1339-
throw new JsonIoException("Class: " + className + " is undefined.");
1372+
addPermanentNotCustomReadClass(loadedClass);
13401373
}
13411374
}
13421375
}

0 commit comments

Comments
 (0)