Skip to content

Commit

Permalink
Time and Date classes compressed down to single String field
Browse files Browse the repository at this point in the history
  • Loading branch information
jdereg committed Feb 2, 2025
1 parent f92b187 commit d1010e1
Show file tree
Hide file tree
Showing 36 changed files with 248 additions and 420 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Key Features:
- Supports polymorphic types and complex object graphs
- Zero external dependencies (other than java-util)
- Fully compatible with both JPMS and OSGi environments
- Lightweight (224K for json-io.jar, 405K for java-util)
- Lightweight (237K for json-io.jar, 405K for java-util)
- Compatible with JDK 1.8 through JDK 23
- Extensive configuration options via ReadOptions and WriteOptions
- Featured on [json.org](http://json.org)
Expand All @@ -31,15 +31,15 @@ ___
To include in your project:
##### Gradle
```groovy
implementation 'com.cedarsoftware:json-io:4.33.0'
implementation 'com.cedarsoftware:json-io:4.34.0'
```

##### Maven
```xml
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>json-io</artifactId>
<version>4.33.0</version>
<version>4.34.0</version>
</dependency>
```
___
Expand All @@ -57,23 +57,23 @@ ___
>- [ ] **Java Package**: com.cedarsoftware.io
>- [ ] **Java**: JDK17+ (Class file 61 format, includes module-info.class - multi-release JAR)
>- [ ] **API**
> - Static methods on [JsonIo](https://www.javadoc.io/doc/com.cedarsoftware/json-io/latest/com/cedarsoftware/io/JsonIo.html): [toJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonIo.html#toJson(java.lang.Object,com.cedarsoftware.io.WriteOptions)), [toObjects()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonIo.html#toObjects(java.lang.String,com.cedarsoftware.io.ReadOptions,java.lang.Class)), [formatJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonIo.html#formatJson(java.lang.String,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions)), [deepCopy()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonIo.html#deepCopy(java.lang.Object,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions))
> - Static methods on [JsonIo](https://www.javadoc.io/doc/com.cedarsoftware/json-io/latest/com/cedarsoftware/io/JsonIo.html): [toJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonIo.html#toJson(java.lang.Object,com.cedarsoftware.io.WriteOptions)), [toObjects()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonIo.html#toObjects(java.lang.String,com.cedarsoftware.io.ReadOptions,java.lang.Class)), [formatJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonIo.html#formatJson(java.lang.String,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions)), [deepCopy()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonIo.html#deepCopy(java.lang.Object,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions))
> - Use [ReadOptionsBuilder](/user-guide-readOptions.md) and [WriteOptionsBuilder](/user-guide-writeOptions.md) to configure `JsonIo`
> - Use [JsonReader.ClassFactory](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonReader.ClassFactory.html) for difficult classes (hard to instantiate & fill)
> - Use [JsonWriter.JsonClassWriter](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonWriter.JsonClassWriter.html) to customize the output JSON for a particular class
> - Use [JsonReader.ClassFactory](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonReader.ClassFactory.html) for difficult classes (hard to instantiate & fill)
> - Use [JsonWriter.JsonClassWriter](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonWriter.JsonClassWriter.html) to customize the output JSON for a particular class
>- [ ] Updates will be 5.1.0, 5.2.0, ...
>### 4.33.0 (current)
>- [ ] **Version**: [4.33.0](https://www.javadoc.io/doc/com.cedarsoftware/json-io/4.33.0/index.html)
>### 4.34.0 (current)
>- [ ] **Version**: [4.34.0](https://www.javadoc.io/doc/com.cedarsoftware/json-io/4.34.0/index.html)
>- [ ] **Bundling**: Both JPMS (Java Platform Module System) and OSGi (Open Service Gateway initiative)
>- [ ] **Maintained**: Fully
>- [ ] **Java Package**: com.cedarsoftware.io
>- [ ] **Java**: JDK1.8+ (Class file 52 format, includes module-info.class - multi-release JAR)
>- [ ] **API**
> - Static methods on [JsonIo](https://www.javadoc.io/doc/com.cedarsoftware/json-io/latest/com/cedarsoftware/io/JsonIo.html): [toJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonIo.html#toJson(java.lang.Object,com.cedarsoftware.io.WriteOptions)), [toObjects()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonIo.html#toObjects(java.lang.String,com.cedarsoftware.io.ReadOptions,java.lang.Class)), [formatJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonIo.html#formatJson(java.lang.String,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions)), [deepCopy()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonIo.html#deepCopy(java.lang.Object,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions))
> - Static methods on [JsonIo](https://www.javadoc.io/doc/com.cedarsoftware/json-io/latest/com/cedarsoftware/io/JsonIo.html): [toJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonIo.html#toJson(java.lang.Object,com.cedarsoftware.io.WriteOptions)), [toObjects()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonIo.html#toObjects(java.lang.String,com.cedarsoftware.io.ReadOptions,java.lang.Class)), [formatJson()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonIo.html#formatJson(java.lang.String,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions)), [deepCopy()](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonIo.html#deepCopy(java.lang.Object,com.cedarsoftware.io.ReadOptions,com.cedarsoftware.io.WriteOptions))
> - Use [ReadOptionsBuilder](/user-guide-readOptions.md) and [WriteOptionsBuilder](/user-guide-writeOptions.md) to configure `JsonIo`
> - Use [JsonReader.ClassFactory](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonReader.ClassFactory.html) for difficult classes (hard to instantiate & fill)
> - Use [JsonWriter.JsonClassWriter](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.33.0/com/cedarsoftware/io/JsonWriter.JsonClassWriter.html) to customize the output JSON for a particular class
>- [ ] Updates will be 4.34.0, 4.35.0, ...
> - Use [JsonReader.ClassFactory](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonReader.ClassFactory.html) for difficult classes (hard to instantiate & fill)
> - Use [JsonWriter.JsonClassWriter](https://www.javadoc.io/static/com.cedarsoftware/json-io/4.34.0/com/cedarsoftware/io/JsonWriter.JsonClassWriter.html) to customize the output JSON for a particular class
>- [ ] Updates will be 4.35.0, 4.36.0, ...
>### 4.14.x (supported)
>- [ ] **Version**: [4.14.3](https://www.javadoc.io/doc/com.cedarsoftware/json-io/4.14.3/index.html)
>- [ ] **Bundling**: Both JPMS (Java Platform Module System) and OSGi (Open Service Gateway initiative)
Expand Down
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
### Revision History
#### 4.34.0
* Improved Date and java.sql.Date handling within Converters.
#### 4.33.0
* New custom `ClassFactory` classes are easier to write:
* See [examples](user-guide.md#classfactory-and-customwriter-examples)
Expand Down
5 changes: 2 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>com.cedarsoftware</groupId>
<artifactId>json-io</artifactId>
<packaging>bundle</packaging>
<version>4.33.0</version>
<version>4.34.0</version>
<description>Java JSON serialization</description>
<url>https://github.com/jdereg/json-io</url>

Expand All @@ -26,8 +26,7 @@
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss.SSSZ</maven.build.timestamp.format>
<!-- remove source encoding warnings from maven output -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<version.java-util>3.0.2</version.java-util>
<version.java-util>3.0.3</version.java-util>
<!-- testing only -->
<version.junit-jupiter-params>5.11.4</version.junit-jupiter-params>
<version.assertj-core>3.27.2</version.assertj-core>
Expand Down
8 changes: 2 additions & 6 deletions src/main/java/com/cedarsoftware/io/JsonIo.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
Expand Down Expand Up @@ -381,11 +380,8 @@ public static WriteOptionsBuilder getWriteOptionsBuilder(Map<String, Object> opt
WriteOptionsBuilder builder = new WriteOptionsBuilder();

Object dateFormat = optionalArgs.get(DATE_FORMAT);
if (dateFormat instanceof String)
{
builder.dateTimeFormat((String) dateFormat);
} else if (dateFormat instanceof SimpleDateFormat) {
builder.dateTimeFormat(((SimpleDateFormat) dateFormat).toPattern());
if (dateFormat != null) {
builder.isoDateFormat();
}

Boolean showType = com.cedarsoftware.util.Converter.convert(optionalArgs.get(TYPE), Boolean.class);
Expand Down
29 changes: 4 additions & 25 deletions src/main/java/com/cedarsoftware/io/WriteOptionsBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -636,23 +636,14 @@ public WriteOptionsBuilder addExcludedFields(Map<Class<?>, Collection<String>> e
}

/**
* Change the date-time format to the ISO date format: "yyyy-MM-dd". This is for java.util.Data and
* java.sql.Date.
* Change the date-time format to the ISO date format: "yyyy-MM-ddThh:mm:ss.SSSZ". This is for java.util.Date and
* java.sql.Date. The fractional sections are omitted if millis are 0.
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder isoDateFormat() {
return dateTimeFormat(ISO_DATE_FORMAT);
}

/**
* Change the date-time format to the ISO date-time format: "yyyy-MM-dd'T'HH:mm:ss" (default). This is
* for java.util.Date and java.sql.Date.
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder isoDateTimeFormat() {
return dateTimeFormat(ISO_DATE_TIME_FORMAT);
addCustomWrittenClass(Date.class, new Writers.DateWriter());
return this;
}

/**
Expand All @@ -666,18 +657,6 @@ public WriteOptionsBuilder longDateFormat() {
addCustomWrittenClass(Date.class, new Writers.DateAsLongWriter());
return this;
}

/**
* Change the date-time format to the passed in format. The format pattens can be found in the Java Doc
* for the java.time.format.DateTimeFormatter class. There are many constants you can use, as well as
* the definition of how to construct your own patterns. This is for java.util.Date and java.sql.Date.
*
* @return WriteOptionsBuilder for chained access.
*/
public WriteOptionsBuilder dateTimeFormat(String format) {
addCustomWrittenClass(Date.class, new Writers.DateWriter(format));
return this;
}

/**
* This option permits adding non-standard accessors (used when writing JSON) that access properties from objects,
Expand Down
77 changes: 46 additions & 31 deletions src/main/java/com/cedarsoftware/io/Writers.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
Expand All @@ -20,8 +19,6 @@
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.SignStyle;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.UUID;
Expand Down Expand Up @@ -206,40 +203,61 @@ public String extractString(Object o) {

public static class CalendarWriter implements JsonWriter.JsonClassWriter {
@Override
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {
String formatted = Converter.convert(o, String.class);
JsonWriter.writeBasicString(output, formatted);
}

public void write(Object obj, boolean showType, Writer output, WriterContext context) throws IOException {
Calendar cal = (Calendar) obj;
// TODO: shouldn't this be the one inside the WriterContext? and shouldn't there be a back up of parseDate() here?
dateFormat.get().setTimeZone(cal.getTimeZone());
output.write("\"time\":\"");
output.write(dateFormat.get().format(cal.getTime()));
output.write("\",\"zone\":\"");
output.write(cal.getTimeZone().getID());
output.write('"');
if (showType) {
JsonWriter.writeBasicString(output, "calendar");
output.write(':');
}

writePrimitiveForm(obj, output, context);
}

public boolean hasPrimitiveForm(WriterContext context) {
return true;
}
}

public static class DateAsLongWriter extends PrimitiveValueWriter {
@Override
public String extractString(Object o) {
return Long.toString(((Date) o).getTime());
if (o instanceof java.sql.Date) {
// Just use the date's built-in toString - it's already in JDBC format
return o.toString();
} else {
return Long.toString(((java.util.Date) o).getTime());
}
}
}

public static class DateWriter extends PrimitiveUtf8StringWriter {
// could change to DateFormatter.ofPattern to keep from creating new objects
private final String dateFormat;

public DateWriter(String format) {
this.dateFormat = format;

public static class DateWriter implements JsonWriter.JsonClassWriter {
@Override
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {
String formatted = Converter.convert(o, String.class);
JsonWriter.writeBasicString(output, formatted);
}

public String extractString(Object o) {
Date date = (Date) o;
return new SimpleDateFormat(dateFormat).format(date);
public void write(Object obj, boolean showType, Writer output, WriterContext context) throws IOException {
if (showType) {
String key;
if (obj instanceof java.sql.Date) {
key = "sqlDate";
} else {
key = "date";
}
JsonWriter.writeBasicString(output, key);
output.write(':');
}

writePrimitiveForm(obj, output, context);
}

String getDateFormat() {
return dateFormat;
public boolean hasPrimitiveForm(WriterContext context) {
return true;
}
}

Expand All @@ -255,9 +273,6 @@ public LocalDateAsLong() {
}

public void writePrimitiveForm(Object o, Writer output, WriterContext writerContext) throws IOException {

//TODO: Change to using converter and having the writeOptions provide a zoneId;
//TODO: If we're going to provide a LocalDateAsLong we should also provide a LocalDateTimeAsLong
LocalDate localDate = (LocalDate) o;
ZonedDateTime zonedDateTime = localDate.atStartOfDay(zoneId);

Expand Down Expand Up @@ -358,15 +373,15 @@ public OffsetDateTimeWriter() {
}

public static class TimestampWriter implements JsonWriter.JsonClassWriter {
@Override
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {
Timestamp timestamp = (Timestamp) o;
String ts = Converter.convert(timestamp, String.class);
JsonWriter.writeBasicString(output, ts);
String formatted = Converter.convert(o, String.class);
JsonWriter.writeBasicString(output, formatted);
}

public void write(Object obj, boolean showType, Writer output, WriterContext context) throws IOException {
if (showType) {
JsonWriter.writeBasicString(output, VALUE);
JsonWriter.writeBasicString(output, "timestamp");
output.write(':');
}

Expand Down
51 changes: 0 additions & 51 deletions src/main/java/com/cedarsoftware/io/factory/TimestampFactory.java

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/resources/config/classFactory.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ java.nio.HeapCharBuffer = com.cedarsoftware.io.factory.CharBufferFactory

# java.sql
java.sql.Date = Convertable
java.sql.Timestamp = com.cedarsoftware.io.factory.TimestampFactory
java.sql.Timestamp = Convertable

# java.time
java.time.Duration = Convertable
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/config/customWriters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ java.net.URI = com.cedarsoftware.io.Writers$PrimitiveUtf8StringWriter
java.nio.ByteBuffer = com.cedarsoftware.io.writers.ByteBufferWriter
java.nio.CharBuffer = com.cedarsoftware.io.writers.CharBufferWriter

java.sql.Date = com.cedarsoftware.io.Writers$DateAsLongWriter
java.sql.Date = com.cedarsoftware.io.Writers$DateWriter
java.sql.Timestamp = com.cedarsoftware.io.Writers$TimestampWriter

java.time.Duration = com.cedarsoftware.io.writers.DurationWriter
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/com/cedarsoftware/io/ArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ void testReconsitutute_withCalendars_whenArrayTypeIsCalendar() {

@Test
void testReconstitute_withGregorianCalendars_whenArrayTypeIsCalendar() {
GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance(TimeZone.getTimeZone("GMT-05:00"));
GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
GregorianCalendar[] calendarz = new GregorianCalendar[]{cal, cal};
testReconstituteArrayHelper(calendarz);
}
Expand Down
Loading

0 comments on commit d1010e1

Please sign in to comment.