Skip to content

Commit 19aac20

Browse files
committed
Added support for Pattern and Currency
1 parent d5f8399 commit 19aac20

File tree

13 files changed

+80
-65
lines changed

13 files changed

+80
-65
lines changed

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

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import java.time.format.DateTimeFormatterBuilder;
2323
import java.time.format.SignStyle;
2424
import java.time.temporal.TemporalAccessor;
25+
import java.util.Currency;
2526
import java.util.Locale;
2627
import java.util.TimeZone;
2728
import java.util.UUID;
29+
import java.util.regex.Pattern;
2830

2931
import com.cedarsoftware.util.Converter;
3032

@@ -224,36 +226,23 @@ public boolean hasPrimitiveForm(WriterContext context) {
224226
}
225227
}
226228

227-
public static class DateAsLongWriter extends DateWriter {
229+
public static class DateWriter implements JsonWriter.JsonClassWriter {
228230
@Override
229231
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {
230-
String formatted;
231232
if (o instanceof java.sql.Date) {
232-
// Just use the date's built-in toString - it's already in JDBC format
233-
formatted = o.toString();
233+
// Write just the date portion - no time, no timezone
234+
String formatted = ((java.sql.Date) o).toLocalDate().toString();
234235
JsonWriter.writeBasicString(output, formatted);
235236
} else {
236-
formatted = Long.toString(((java.util.Date) o).getTime());
237-
output.write(formatted);
237+
// Regular Date uses the converter's string format
238+
String formatted = Converter.convert(o, String.class);
239+
JsonWriter.writeBasicString(output, formatted);
238240
}
239241
}
240-
}
241-
242-
public static class DateWriter implements JsonWriter.JsonClassWriter {
243-
@Override
244-
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {
245-
String formatted = Converter.convert(o, String.class);
246-
JsonWriter.writeBasicString(output, formatted);
247-
}
248242

249243
public void write(Object obj, boolean showType, Writer output, WriterContext context) throws IOException {
250244
if (showType) {
251-
String key;
252-
if (obj instanceof java.sql.Date) {
253-
key = "sqlDate";
254-
} else {
255-
key = "date";
256-
}
245+
String key = (obj instanceof java.sql.Date) ? "sqlDate" : "date";
257246
JsonWriter.writeBasicString(output, key);
258247
output.write(':');
259248
}
@@ -266,6 +255,20 @@ public boolean hasPrimitiveForm(WriterContext context) {
266255
}
267256
}
268257

258+
public static class DateAsLongWriter extends DateWriter {
259+
@Override
260+
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {
261+
if (o instanceof java.sql.Date) {
262+
// Same pure date format for sql.Date in both writers
263+
String formatted = ((java.sql.Date) o).toLocalDate().toString();
264+
JsonWriter.writeBasicString(output, formatted);
265+
} else {
266+
// Regular Date uses milliseconds
267+
output.write(Long.toString(((java.util.Date) o).getTime()));
268+
}
269+
}
270+
}
271+
269272
public static class LocalDateAsLong extends PrimitiveTypeWriter {
270273
private final ZoneId zoneId;
271274

@@ -484,10 +487,9 @@ protected void writePrimitiveForm(ZoneOffset offset, Writer output) throws IOExc
484487
public static class DurationWriter implements JsonWriter.JsonClassWriter {
485488
public void write(Object obj, boolean showType, Writer output, WriterContext context) throws IOException {
486489
Duration duration = (Duration) obj;
487-
output.write("\"seconds\":");
488-
output.write("" + duration.getSeconds());
489-
output.write(",\"nanos\":");
490-
output.write("" + duration.getNano());
490+
output.write("\"duration\":\"");
491+
output.write(duration.toString());
492+
output.write("\"");
491493
}
492494
}
493495

@@ -558,6 +560,22 @@ public void writePrimitiveForm(Object o, Writer output, WriterContext context) t
558560
}
559561
}
560562

563+
public static class PatternWriter extends PrimitiveValueWriter {
564+
@Override
565+
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {
566+
Pattern pattern = (Pattern) o;
567+
JsonWriter.writeJsonUtf8String(output, pattern.pattern());
568+
}
569+
}
570+
571+
public static class CurrencyWriter extends PrimitiveValueWriter {
572+
@Override
573+
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {
574+
Currency currency = (Currency) o;
575+
JsonWriter.writeJsonUtf8String(output, currency.getCurrencyCode());
576+
}
577+
}
578+
561579
public static class BigDecimalWriter extends PrimitiveValueWriter {
562580
@Override
563581
public void writePrimitiveForm(Object o, Writer output, WriterContext context) throws IOException {

src/main/resources/config/classFactory.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@ java.math.BigInteger = Convertable
3030
java.util.concurrent.atomic.AtomicBoolean = Convertable
3131
java.util.concurrent.atomic.AtomicInteger = Convertable
3232
java.util.concurrent.atomic.AtomicLong = Convertable
33+
java.util.Currency = Convertable
3334
java.util.Date = Convertable
3435
java.util.Calendar = Convertable
3536
java.util.GregorianCalendar = Convertable
3637
java.util.Locale = Convertable
3738
java.util.UUID = Convertable
3839
java.util.TimeZone = Convertable
3940

41+
java.util.regex.Pattern = Convertable
42+
4043
# java.lang
4144
java.lang.Class = Convertable
4245
java.lang.String = Convertable

src/main/resources/config/customWriters.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,15 @@ java.util.Calendar = com.cedarsoftware.io.Writers$CalendarWriter
5353
java.util.concurrent.atomic.AtomicBoolean = com.cedarsoftware.io.Writers$PrimitiveValueWriter
5454
java.util.concurrent.atomic.AtomicInteger = com.cedarsoftware.io.Writers$PrimitiveValueWriter
5555
java.util.concurrent.atomic.AtomicLong = com.cedarsoftware.io.Writers$PrimitiveValueWriter
56+
java.util.Currency = com.cedarsoftware.io.Writers$CurrencyWriter
5657
java.util.Date = com.cedarsoftware.io.Writers$DateAsLongWriter
5758
java.util.GregorianCalendar = com.cedarsoftware.io.Writers$CalendarWriter
5859
java.util.Locale = com.cedarsoftware.io.Writers$LocaleWriter
5960
java.util.TimeZone = com.cedarsoftware.io.Writers$TimeZoneWriter
6061
java.util.UUID = com.cedarsoftware.io.Writers$UUIDWriter
6162

63+
java.util.regex.Pattern = com.cedarsoftware.io.Writers$PatternWriter
64+
6265
sun.util.calendar.ZoneInfo = com.cedarsoftware.io.Writers$TimeZoneWriter
6366

6467
# CompactMap

src/test/java/com/cedarsoftware/io/ClassTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ void testNoClassNameValue()
7777
String json = "{\"@type\":\"class\"}";
7878
assertThatThrownBy(() -> TestUtil.toObjects(json, null))
7979
.isInstanceOf(JsonIoException.class)
80-
.hasMessageContaining("Map to 'Class' the map must include: [value] or [_v] as keys with associated values");
80+
.hasMessageContaining("Map to 'Class' the map must include: [class], [value], or [_v] as key with associated value");
8181

8282
}
8383

src/test/java/com/cedarsoftware/io/DatesTest.java

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import java.time.Instant;
66
import java.time.LocalDate;
77
import java.time.ZoneId;
8-
import java.time.ZoneOffset;
98
import java.time.ZonedDateTime;
109
import java.util.Calendar;
1110
import java.util.Date;
@@ -407,17 +406,18 @@ void testSqlDate()
407406
@Test
408407
void testSqlDate2() {
409408
// Given
410-
long now = 1703043551033L; // This represents the full epoch in UTC.
411-
// Construct our expected objects (for direct comparison)
409+
long now = 1703043551033L; // full epoch in UTC.
410+
// Construct expected objects:
412411
Date expectedUtilDate = new Date(now);
413-
// For java.sql.Date, create it as a literal date (interpreting 'now' in UTC):
412+
413+
// Compute expected LocalDate using the same zone as your converter (e.g., Asia/Tokyo)
414414
LocalDate expectedLD = Instant.ofEpochMilli(now)
415-
.atZone(ZoneOffset.UTC)
415+
.atZone(ZoneId.systemDefault())
416416
.toLocalDate();
417417
java.sql.Date expectedSqlDate = java.sql.Date.valueOf(expectedLD);
418-
// For Timestamp, we expect the complete value to be included.
418+
419+
// For Timestamp, set up as before
419420
Timestamp expectedTimestamp = new Timestamp(now);
420-
// (If your Timestamp conversion uses separate fields for nanos, it should result in nanos=33000000.)
421421
expectedTimestamp.setNanos(33000000); // ensuring the fractional part is set
422422

423423
String json = "{\"@type\":\"[Ljava.util.Date;\",\"@items\":[1703043551033,{\"@type\":\"java.sql.Date\", \"sqlDate\":1703043551033},{\"@type\":\"java.sql.Timestamp\",\"epochMillis\":\"1703043551000\"}]}";
@@ -429,23 +429,18 @@ void testSqlDate2() {
429429
// Then
430430
assertEquals(3, dates2.length);
431431

432-
// For a plain java.util.Date, simply compare the underlying epoch:
432+
// For plain java.util.Date
433433
assertEquals(expectedUtilDate.getTime(), dates2[0].getTime());
434434

435-
// For a java.sql.Date, compare by their literal date string (or equivalently compare LocalDate).
436-
// (This avoids any unintended time zone arithmetic.)
437-
assertEquals(expectedSqlDate.toString(), dates2[1].toString());
438-
439-
// Optionally, you could also convert to LocalDate and compare:
435+
// For java.sql.Date, compare by string (or LocalDate)
436+
assertEquals(expectedSqlDate, dates2[1]);
440437
LocalDate ldFromActual = Instant.ofEpochMilli(dates2[1].getTime())
441-
.atZone(ZoneOffset.UTC)
438+
.atZone(ZoneId.systemDefault())
442439
.toLocalDate();
443440
assertEquals(expectedLD, ldFromActual);
444441

445-
// For the Timestamp:
442+
// For Timestamp:
446443
Timestamp stamp = (Timestamp) dates2[2];
447-
// The JSON provides "epochMillis": "1703043551000" and nanos: 33000000,
448-
// so the full value should be 1703043551000 + 33 = 1703043551033.
449444
assertEquals(1703043551000L, stamp.getTime());
450445
}
451446

src/test/java/com/cedarsoftware/io/InstantTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ void testOldFormats(String fileName, Instant expected) {
132132
void testOldFormatWithNothing() {
133133
assertThatThrownBy(() -> TestUtil.toObjects(loadJson("old-format-missing-fields.json"), Instant.class))
134134
.isInstanceOf(JsonIoException.class)
135-
.hasMessageContaining("Map to 'Instant' the map must include: [instant], [value], or [_v] as keys with associated values");
135+
.hasMessageContaining("Map to 'Instant' the map must include: [instant], [value], or [_v] as key with associated value");
136136
}
137137

138138
private String loadJson(String fileName) {

src/test/java/com/cedarsoftware/io/LocaleTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ public void testLocale()
4949
assertEquals(locale, us);
5050

5151
Throwable e = assertThrows(Exception.class, () -> { TestUtil.toObjects("{\"@type\":\"java.util.Locale\"}", null); });
52-
assertTrue(e.getMessage().contains("convert from Map to 'Locale' the map must include: [language, country (optional), script (optional), variant (optional)], [value], or [_v] as keys with associated values"));
52+
assertTrue(e.getMessage().contains("Map to 'Locale' the map must include: [locale], [value], or [_v] as key with associated value"));
5353

54-
json = "{\"@type\":\"java.util.Locale\",\"language\":\"en\"}";
54+
json = "{\"@type\":\"java.util.Locale\",\"locale\":\"en\"}";
5555
locale = TestUtil.toObjects(json, null);
5656
assertEquals("en", locale.getLanguage());
5757
assertEquals("", locale.getCountry());
5858
assertEquals("", locale.getVariant());
5959

60-
json = "{\"@type\":\"java.util.Locale\",\"language\":\"en\",\"country\":\"US\"}";
60+
json = "{\"@type\":\"java.util.Locale\",\"locale\":\"en-US\"}";
6161
locale = TestUtil.toObjects(json, null);
6262
assertEquals("en", locale.getLanguage());
6363
assertEquals("US", locale.getCountry());

src/test/java/com/cedarsoftware/io/PrimitivesTest.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,48 +83,48 @@ void testEmptyPrimitives()
8383

8484
assertThatThrownBy(() -> TestUtil.toObjects(json, null))
8585
.isInstanceOf(JsonIoException.class)
86-
.hasMessageContaining("To convert from Map to 'Byte' the map must include: [value] or [_v] as keys with associated values");
86+
.hasMessageContaining("To convert from Map to 'Byte' the map must include: [value] or [_v] as key with associated value");
8787

8888
final String json1 = "{\"@type\":\"short\"}";
8989

9090
assertThatThrownBy(() -> TestUtil.toObjects(json1, null))
9191
.isInstanceOf(JsonIoException.class)
92-
.hasMessageContaining("convert from Map to 'Short' the map must include: [value] or [_v] as keys with associated values");
92+
.hasMessageContaining("convert from Map to 'Short' the map must include: [value] or [_v] as key with associated value");
9393

9494
final String json2 = "{\"@type\":\"int\"}";
9595
assertThatThrownBy(() -> TestUtil.toObjects(json2, null))
9696
.isInstanceOf(JsonIoException.class)
97-
.hasMessageContaining("convert from Map to 'Integer' the map must include: [value] or [_v] as keys with associated values");
97+
.hasMessageContaining("convert from Map to 'Integer' the map must include: [value] or [_v] as key with associated value");
9898

9999
final String json3 = "{\"@type\":\"long\"}";
100100
assertThatThrownBy(() -> TestUtil.toObjects(json3, null))
101101
.isInstanceOf(JsonIoException.class)
102-
.hasMessageContaining("convert from Map to 'Long' the map must include: [value] or [_v] as keys with associated values");
102+
.hasMessageContaining("convert from Map to 'Long' the map must include: [value] or [_v] as key with associated value");
103103

104104
final String json4 = "{\"@type\":\"float\"}";
105105
assertThatThrownBy(() -> TestUtil.toObjects(json4, null))
106106
.isInstanceOf(JsonIoException.class)
107-
.hasMessageContaining("convert from Map to 'Float' the map must include: [value] or [_v] as keys with associated values");
107+
.hasMessageContaining("convert from Map to 'Float' the map must include: [value] or [_v] as key with associated value");
108108

109109
final String json5 = "{\"@type\":\"double\"}";
110110
assertThatThrownBy(() -> TestUtil.toObjects(json5, null))
111111
.isInstanceOf(JsonIoException.class)
112-
.hasMessageContaining("convert from Map to 'Double' the map must include: [value] or [_v] as keys with associated values");
112+
.hasMessageContaining("convert from Map to 'Double' the map must include: [value] or [_v] as key with associated value");
113113

114114
final String json6 = "{\"@type\":\"char\"}";
115115
assertThatThrownBy(() -> TestUtil.toObjects(json6, null))
116116
.isInstanceOf(JsonIoException.class)
117-
.hasMessageContaining("convert from Map to 'char' the map must include: [value] or [_v] as keys with associated values");
117+
.hasMessageContaining("convert from Map to 'char' the map must include: [value] or [_v] as key with associated value");
118118

119119
final String json7 = "{\"@type\":\"boolean\"}";
120120
assertThatThrownBy(() -> TestUtil.toObjects(json7, null))
121121
.isInstanceOf(JsonIoException.class)
122-
.hasMessageContaining("convert from Map to 'Boolean' the map must include: [value] or [_v] as keys with associated values");
122+
.hasMessageContaining("convert from Map to 'Boolean' the map must include: [value] or [_v] as key with associated value");
123123

124124
final String json8 = "{\"@type\":\"string\"}";
125125
assertThatThrownBy(() -> TestUtil.toObjects(json8, null))
126126
.isInstanceOf(JsonIoException.class)
127-
.hasMessageContaining("convert from Map to 'String' the map must include: [value] or [_v] as keys with associated values");
127+
.hasMessageContaining("convert from Map to 'String' the map must include: [value] or [_v] as key with associated value");
128128
}
129129

130130
@Test

src/test/java/com/cedarsoftware/io/UUIDTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ public void testAssignUUID()
5050

5151
String json2 = "{\"@type\":\"" + TestUUIDFields.class.getName() + "\", \"internals\": {\"@type\": \"java.util.UUID\", \"leastSigBits\":-7929886640328317609}}";
5252
thrown = assertThrows(JsonIoException.class, () -> { TestUtil.toObjects(json2, null); });
53-
assert thrown.getMessage().contains("Map to 'UUID' the map must include: [UUID], [mostSigBits, leastSigBits], [value], or [_v] as keys with associated values");
53+
assert thrown.getMessage().contains("Map to 'UUID' the map must include: [UUID], [value], [_v], or [mostSigBits, leastSigBits] as key with associated value");
5454

5555
String json3 = "{\"@type\":\"" + TestUUIDFields.class.getName() + "\", \"internals\": {\"@type\": \"java.util.UUID\", \"mostSigBits\":7280309849777586861}}";
5656
thrown = assertThrows(JsonIoException.class, () -> { TestUtil.toObjects(json3, null); });
57-
assert thrown.getMessage().contains("Map to 'UUID' the map must include: [UUID], [mostSigBits, leastSigBits], [value], or [_v] as keys with associated values");
57+
assert thrown.getMessage().contains("Map to 'UUID' the map must include: [UUID], [value], [_v], or [mostSigBits, leastSigBits] as key with associated value");
5858
}
5959

6060
@Test
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
22
"@type": "java.time.Duration",
3-
"seconds": 420,
4-
"nanos": null
3+
"duration": 420000
54
}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
22
"@type": "java.time.Duration",
3-
"seconds": 25200,
4-
"nanos": 2000
3+
"duration": 25200.000002
54
}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
22
"@type": "java.time.Duration",
3-
"seconds": 777600,
4-
"nanos": null
3+
"duration": 777600000
54
}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
22
"@type": "java.time.Duration",
3-
"seconds": 9000,
4-
"nanos": 9000
3+
"duration": "9000.000009"
54
}

0 commit comments

Comments
 (0)