From 432840be786e3e9369b14afb1f20def3376c3861 Mon Sep 17 00:00:00 2001 From: Looly Date: Mon, 2 Dec 2019 18:21:10 +0800 Subject: [PATCH] fix LocalDateTime not support bug --- CHANGELOG.md | 1 + .../core/convert/impl/PrimitiveConverter.java | 87 ++++++++++++------- .../java/cn/hutool/json/InternalJSONUtil.java | 25 ++++-- .../main/java/cn/hutool/json/JSONUtil.java | 7 +- .../test/java/cn/hutool/json/Isse644Test.java | 32 +++++++ .../java/cn/hutool/json/JSONObjectTest.java | 13 ++- 6 files changed, 120 insertions(+), 45 deletions(-) create mode 100644 hutool-json/src/test/java/cn/hutool/json/Isse644Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 1444c680a..e2d0c4d0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ * 【core 】 修复scale方法透明无效问题(issue#I15L5S@Gitee) * 【extra】 修复exec返回无效(issue#I15L5S@Gitee) * 【cron】 修复CronPattern注释(pr#646@Github) +* 【json】 修复LocalDateTime等JDK8时间对象不被支持的问题(issue#644@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/PrimitiveConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/PrimitiveConverter.java index 13218ac0c..ee3a95f31 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/PrimitiveConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/PrimitiveConverter.java @@ -1,41 +1,46 @@ package cn.hutool.core.convert.impl; import cn.hutool.core.convert.AbstractConverter; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; +import java.time.temporal.TemporalAccessor; +import java.util.Calendar; +import java.util.Date; + /** * 原始类型转换器
* 支持类型为:
* - * - * @author Looly * + * @author Looly */ public class PrimitiveConverter extends AbstractConverter { private static final long serialVersionUID = 1L; - + private Class targetType; /** * 构造
+ * * @param clazz 需要转换的原始 * @throws IllegalArgumentException 传入的转换类型非原始类型时抛出 */ public PrimitiveConverter(Class clazz) { - if(null == clazz){ + if (null == clazz) { throw new NullPointerException("PrimitiveConverter not allow null target type!"); - }else if(false == clazz.isPrimitive()){ + } else if (false == clazz.isPrimitive()) { throw new IllegalArgumentException("[" + clazz + "] is not a primitive class!"); } this.targetType = clazz; @@ -47,80 +52,95 @@ public class PrimitiveConverter extends AbstractConverter { if (byte.class == this.targetType) { if (value instanceof Number) { return ((Number) value).byteValue(); - } else if(value instanceof Boolean) { - return BooleanUtil.toByte((Boolean)value); + } else if (value instanceof Boolean) { + return BooleanUtil.toByte((Boolean) value); } final String valueStr = convertToStr(value); if (StrUtil.isBlank(valueStr)) { return 0; } return Byte.parseByte(valueStr); - + } else if (short.class == this.targetType) { if (value instanceof Number) { return ((Number) value).shortValue(); - } else if(value instanceof Boolean) { - return BooleanUtil.toShort((Boolean)value); + } else if (value instanceof Boolean) { + return BooleanUtil.toShort((Boolean) value); } final String valueStr = convertToStr(value); if (StrUtil.isBlank(valueStr)) { return 0; } return Short.parseShort(valueStr); - + } else if (int.class == this.targetType) { if (value instanceof Number) { return ((Number) value).intValue(); - } else if(value instanceof Boolean) { - return BooleanUtil.toInt((Boolean)value); + } else if (value instanceof Boolean) { + return BooleanUtil.toInt((Boolean) value); + } else if (value instanceof Date) { + return ((Date) value).getTime(); + } else if (value instanceof Calendar) { + return ((Calendar) value).getTimeInMillis(); + } else if (value instanceof TemporalAccessor) { + return DateUtil.toInstant((TemporalAccessor) value).toEpochMilli(); } + final String valueStr = convertToStr(value); if (StrUtil.isBlank(valueStr)) { return 0; } return NumberUtil.parseInt(valueStr); - + } else if (long.class == this.targetType) { if (value instanceof Number) { return ((Number) value).longValue(); - } else if(value instanceof Boolean) { - return BooleanUtil.toLong((Boolean)value); + } else if (value instanceof Boolean) { + return BooleanUtil.toLong((Boolean) value); + } else if (value instanceof Date) { + return ((Date) value).getTime(); + } else if (value instanceof Calendar) { + return ((Calendar) value).getTimeInMillis(); + } else if (value instanceof TemporalAccessor) { + return DateUtil.toInstant((TemporalAccessor) value).toEpochMilli(); } + final String valueStr = convertToStr(value); if (StrUtil.isBlank(valueStr)) { return 0; } return NumberUtil.parseLong(valueStr); - + } else if (float.class == this.targetType) { if (value instanceof Number) { return ((Number) value).floatValue(); - } else if(value instanceof Boolean) { - return BooleanUtil.toFloat((Boolean)value); + } else if (value instanceof Boolean) { + return BooleanUtil.toFloat((Boolean) value); } final String valueStr = convertToStr(value); if (StrUtil.isBlank(valueStr)) { return 0; } return Float.parseFloat(valueStr); - + } else if (double.class == this.targetType) { if (value instanceof Number) { return ((Number) value).doubleValue(); - } else if(value instanceof Boolean) { - return BooleanUtil.toDouble((Boolean)value); + } else if (value instanceof Boolean) { + return BooleanUtil.toDouble((Boolean) value); } final String valueStr = convertToStr(value); if (StrUtil.isBlank(valueStr)) { return 0; } return Double.parseDouble(valueStr); - + } else if (char.class == this.targetType) { - if(value instanceof Character){ - return ((Character)value).charValue(); - } else if(value instanceof Boolean) { - return BooleanUtil.toChar((Boolean)value); + if (value instanceof Character) { + //noinspection UnnecessaryUnboxing + return ((Character) value).charValue(); + } else if (value instanceof Boolean) { + return BooleanUtil.toChar((Boolean) value); } final String valueStr = convertToStr(value); if (StrUtil.isBlank(valueStr)) { @@ -128,8 +148,9 @@ public class PrimitiveConverter extends AbstractConverter { } return valueStr.charAt(0); } else if (boolean.class == this.targetType) { - if(value instanceof Boolean){ - return ((Boolean)value).booleanValue(); + if (value instanceof Boolean) { + //noinspection UnnecessaryUnboxing + return ((Boolean) value).booleanValue(); } String valueStr = convertToStr(value); return BooleanUtil.toBoolean(valueStr); @@ -139,12 +160,12 @@ public class PrimitiveConverter extends AbstractConverter { } return 0; } - + @Override protected String convertToStr(Object value) { return StrUtil.trim(super.convertToStr(value)); } - + @Override @SuppressWarnings("unchecked") public Class getTargetType() { diff --git a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java index 5e1a13c12..a1b50f084 100644 --- a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java @@ -2,6 +2,7 @@ package cn.hutool.json; import java.io.IOException; import java.io.Writer; +import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Collection; import java.util.Date; @@ -43,12 +44,12 @@ final class InternalJSONUtil { } else if (value instanceof JSON) { ((JSON) value).write(writer, indentFactor, indent); } else if (value instanceof Map) { - new JSONObject((Map) value).write(writer, indentFactor, indent); + new JSONObject(value).write(writer, indentFactor, indent); } else if (value instanceof Iterable || value instanceof Iterator || value.getClass().isArray()) { new JSONArray(value).write(writer, indentFactor, indent); } else if (value instanceof Number) { writer.write(NumberUtil.toStr((Number) value)); - } else if (value instanceof Date || value instanceof Calendar) { + } else if (value instanceof Date || value instanceof Calendar || value instanceof TemporalAccessor) { final String format = (null == config) ? null : config.getDateFormat(); writer.write(formatDate(value, format)); } else if (value instanceof Boolean) { @@ -138,7 +139,6 @@ final class InternalJSONUtil { * @return A simple JSON value. */ protected static Object stringToValue(String string) { - Double d; if (null == string || "null".equalsIgnoreCase(string)) { return JSONNull.NULL; } @@ -158,8 +158,8 @@ final class InternalJSONUtil { if ((b >= '0' && b <= '9') || b == '-') { try { if (string.indexOf('.') > -1 || string.indexOf('e') > -1 || string.indexOf('E') > -1) { - d = Double.valueOf(string); - if (!d.isInfinite() && !d.isNaN()) { + double d = Double.parseDouble(string); + if (false == Double.isInfinite(d) && false == Double.isNaN(d)) { return d; } } else { @@ -228,12 +228,21 @@ final class InternalJSONUtil { */ private static String formatDate(Object dateObj, String format) { if (StrUtil.isNotBlank(format)) { - final Date date = (dateObj instanceof Date) ? (Date) dateObj : ((Calendar) dateObj).getTime(); //用户定义了日期格式 - return JSONUtil.quote(DateUtil.format(date, format)); + return JSONUtil.quote(DateUtil.format(Convert.toDate(dateObj), format)); } //默认使用时间戳 - return String.valueOf((dateObj instanceof Date) ? ((Date) dateObj).getTime() : ((Calendar) dateObj).getTimeInMillis()); + long timeMillis; + if(dateObj instanceof TemporalAccessor){ + timeMillis = DateUtil.toInstant((TemporalAccessor)dateObj).toEpochMilli(); + } else if(dateObj instanceof Date){ + timeMillis = ((Date) dateObj).getTime(); + } else if(dateObj instanceof Calendar){ + timeMillis = ((Calendar) dateObj).getTimeInMillis(); + } else{ + throw new UnsupportedOperationException("Unsupported Date type: " + dateObj.getClass()); + } + return String.valueOf(timeMillis); } } diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java b/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java index e94ff2c75..25a3e5bd6 100644 --- a/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java +++ b/hutool-json/src/main/java/cn/hutool/json/JSONUtil.java @@ -6,6 +6,8 @@ import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Type; import java.nio.charset.Charset; +import java.time.LocalDateTime; +import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Collection; import java.util.Date; @@ -650,7 +652,10 @@ public final class JSONUtil { } // 日期类型原样保存,便于格式化 - if (object instanceof Date || object instanceof Calendar) { + if (object instanceof Date + || object instanceof Calendar + || object instanceof TemporalAccessor + ) { return object; } // 枚举类保存其字符串形式(4.0.2新增) diff --git a/hutool-json/src/test/java/cn/hutool/json/Isse644Test.java b/hutool-json/src/test/java/cn/hutool/json/Isse644Test.java new file mode 100644 index 000000000..3b59e410d --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/Isse644Test.java @@ -0,0 +1,32 @@ +package cn.hutool.json; + +import lombok.Data; +import org.junit.Assert; +import org.junit.Test; + +import java.time.LocalDateTime; + +/** + * 问题反馈对象中有JDK8日期对象时转换失败,5.0.7修复 + */ +public class Isse644Test { + + @Test + public void toBeanTest(){ + final BeanWithDate beanWithDate = new BeanWithDate(); + beanWithDate.setDate(LocalDateTime.now()); + + final JSONObject jsonObject = JSONUtil.parseObj(beanWithDate); + + BeanWithDate beanWithDate2 = JSONUtil.toBean(jsonObject, BeanWithDate.class); + Assert.assertEquals(beanWithDate.getDate(), beanWithDate2.getDate()); + + beanWithDate2 = JSONUtil.toBean(jsonObject.toString(), BeanWithDate.class); + Assert.assertEquals(beanWithDate.getDate(), beanWithDate2.getDate()); + } + + @Data + static class BeanWithDate{ + private LocalDateTime date; + } +} diff --git a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java index d5db03c2b..5ca532c84 100644 --- a/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/JSONObjectTest.java @@ -3,6 +3,7 @@ package cn.hutool.json; import java.math.BigDecimal; import java.util.Date; import java.util.List; +import java.util.Objects; import org.junit.Assert; import org.junit.Ignore; @@ -58,8 +59,8 @@ public class JSONObjectTest { */ @Test public void toStringTest3() { - JSONObject json = JSONUtil.createObj()// - .put("dateTime", DateUtil.parse("2019-05-02 22:12:01"))// + JSONObject json = Objects.requireNonNull(JSONUtil.createObj()// + .put("dateTime", DateUtil.parse("2019-05-02 22:12:01")))// .setDateFormat(DatePattern.NORM_DATE_PATTERN); Assert.assertEquals("{\"dateTime\":\"2019-05-02\"}", json.toString()); } @@ -67,9 +68,10 @@ public class JSONObjectTest { @Test public void toStringWithDateTest() { JSONObject json = JSONUtil.createObj().put("date", DateUtil.parse("2019-05-08 19:18:21")); + assert json != null; Assert.assertEquals("{\"date\":1557314301000}", json.toString()); - json = JSONUtil.createObj().put("date", DateUtil.parse("2019-05-08 19:18:21")).setDateFormat(DatePattern.NORM_DATE_PATTERN); + json = Objects.requireNonNull(JSONUtil.createObj().put("date", DateUtil.parse("2019-05-08 19:18:21"))).setDateFormat(DatePattern.NORM_DATE_PATTERN); Assert.assertEquals("{\"date\":\"2019-05-08\"}", json.toString()); } @@ -131,6 +133,7 @@ public class JSONObjectTest { Console.log(json2); } + @SuppressWarnings("ConstantConditions") @Test public void toBeanTest() { JSONObject subJson = JSONUtil.createObj().put("value1", "strValue1").put("value2", "234"); @@ -149,6 +152,7 @@ public class JSONObjectTest { Assert.assertEquals(TestEnum.TYPE_A, bean.getTestEnum()); } + @SuppressWarnings("ConstantConditions") @Test public void toBeanNullStrTest() { JSONObject json = JSONUtil.createObj()// @@ -217,6 +221,7 @@ public class JSONObjectTest { /** * 在JSON转Bean过程中,Bean中字段如果为父类定义的泛型类型,则应正确转换,此方法用于测试这类情况 */ + @SuppressWarnings("ConstantConditions") @Test public void toBeanTest6() { JSONObject json = JSONUtil.createObj().put("targetUrl", "http://test.com").put("success", "true").put("result", JSONUtil.createObj().put("token", "tokenTest").put("userId", "测试用户1")); @@ -271,6 +276,7 @@ public class JSONObjectTest { Assert.assertEquals(bean.toString(), bean2.toString()); } + @SuppressWarnings("ConstantConditions") @Test public void parseBeanTest3() { JSONObject json = JSONUtil.createObj().put("code", 22).put("data", "{\"jobId\": \"abc\", \"videoUrl\": \"http://a.com/a.mp4\"}"); @@ -310,6 +316,7 @@ public class JSONObjectTest { Assert.assertEquals(DateUtil.parse("2018-10-25"), bean.getDate()); } + @SuppressWarnings("ConstantConditions") @Test public void beanTransTest3() { JSONObject userAJson = JSONUtil.createObj().put("a", "AValue").put("name", "nameValue").put("date", "08:00:00");