diff --git a/hutool-json/pom.xml b/hutool-json/pom.xml
index 46833bc17..440f846db 100755
--- a/hutool-json/pom.xml
+++ b/hutool-json/pom.xml
@@ -33,6 +33,7 @@
1.78.1
0.12.6
+ 2.17.2
@@ -52,9 +53,15 @@
com.fasterxml.jackson.core
jackson-databind
- 2.17.2
+ ${jackson.version}
true
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson.version}
+ test
+
com.google.code.gson
gson
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/FastJSON2Engine.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/FastJSON2Engine.java
index ab715ea4e..30b87a19c 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/FastJSON2Engine.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/FastJSON2Engine.java
@@ -94,6 +94,8 @@ public class FastJSON2Engine extends AbstractJSONEngine {
protected void initEngine() {
if(null == this.readerContext){
this.readerContext = JSONFactory.createReadContext();
+ final String dateFormat = ObjUtil.defaultIfNull(this.config, JSONEngineConfig::getDateFormat, "millis");
+ this.readerContext.setDateFormat(ObjUtil.defaultIfNull(dateFormat, "millis"));
}
if(null == this.writerContext){
final List features = ListUtil.of();
@@ -101,6 +103,10 @@ public class FastJSON2Engine extends AbstractJSONEngine {
features.add(JSONWriter.Feature.PrettyFormat);
}
this.writerContext = JSONFactory.createWriteContext(features.toArray(new JSONWriter.Feature[0]));
+
+ // 自定义配置
+ final String dateFormat = ObjUtil.defaultIfNull(this.config, JSONEngineConfig::getDateFormat, "millis");
+ this.writerContext.setDateFormat(ObjUtil.defaultIfNull(dateFormat, "millis"));
}
}
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/GsonEngine.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/GsonEngine.java
index 0f1d0276a..49fdc42a7 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/GsonEngine.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/GsonEngine.java
@@ -16,15 +16,18 @@
package org.dromara.hutool.json.engine;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
+import com.google.gson.*;
+import org.dromara.hutool.core.date.TimeUtil;
import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.JSONException;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.util.Date;
/**
* Gson引擎实现
@@ -32,7 +35,7 @@ import java.lang.reflect.Type;
* @author Looly
* @since 6.0.0
*/
-public class GsonEngine extends AbstractJSONEngine{
+public class GsonEngine extends AbstractJSONEngine {
private Gson gson;
@@ -55,10 +58,10 @@ public class GsonEngine extends AbstractJSONEngine{
@Override
public T deserialize(final Reader reader, final Object type) {
initEngine();
- if(type instanceof Class){
- return gson.fromJson(reader, (Class)type);
- } else if(type instanceof Type){
- return gson.fromJson(reader, (Type)type);
+ if (type instanceof Class) {
+ return gson.fromJson(reader, (Class) type);
+ } else if (type instanceof Type) {
+ return gson.fromJson(reader, (Type) type);
}
throw new JSONException("Unsupported type: {}", type.getClass());
@@ -71,14 +74,28 @@ public class GsonEngine extends AbstractJSONEngine{
@Override
protected void initEngine() {
- if(null != this.gson){
+ if (null != this.gson) {
return;
}
final GsonBuilder builder = new GsonBuilder();
- if(ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false)){
+ if (ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false)) {
builder.setPrettyPrinting();
}
+ final String dateFormat = ObjUtil.apply(this.config, JSONEngineConfig::getDateFormat);
+ if (StrUtil.isNotEmpty(dateFormat)) {
+ builder.setDateFormat(dateFormat);
+ builder.registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (json, typeOfT, context) -> TimeUtil.parse(json.getAsString(), dateFormat));
+ builder.registerTypeAdapter(LocalDateTime.class, (JsonSerializer) (date, type, jsonSerializationContext) -> new JsonPrimitive(TimeUtil.format(date, dateFormat)));
+ } else {
+ // 无自定义格式,则默认输出时间戳
+ // https://stackoverflow.com/questions/41979086/how-to-serialize-date-to-long-using-gson
+ builder.registerTypeAdapter(Date.class, (JsonDeserializer) (json, typeOfT, context) -> new Date(json.getAsJsonPrimitive().getAsLong()));
+ builder.registerTypeAdapter(Date.class, (JsonSerializer) (date, type, jsonSerializationContext) -> new JsonPrimitive(date.getTime()));
+ builder.registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (json, typeOfT, context) -> TimeUtil.of(json.getAsJsonPrimitive().getAsLong()));
+ builder.registerTypeAdapter(LocalDateTime.class, (JsonSerializer) (date, type, jsonSerializationContext) -> new JsonPrimitive(TimeUtil.toEpochMilli(date)));
+ }
+
this.gson = builder.create();
}
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/HutoolJSONEngine.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/HutoolJSONEngine.java
index 4f30b301c..632efe43c 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/HutoolJSONEngine.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/HutoolJSONEngine.java
@@ -60,6 +60,12 @@ public class HutoolJSONEngine extends AbstractJSONEngine {
return;
}
- hutoolSJONConfig = JSONConfig.of();
+ // 自定义配置
+ final JSONConfig hutoolSJONConfig = JSONConfig.of();
+ if(null != this.config){
+ hutoolSJONConfig.setDateFormat(this.config.getDateFormat());
+ }
+
+ this.hutoolSJONConfig = hutoolSJONConfig;
}
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/JSONEngineConfig.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/JSONEngineConfig.java
index f3639138e..f83990786 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/JSONEngineConfig.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/JSONEngineConfig.java
@@ -40,6 +40,10 @@ public class JSONEngineConfig implements Serializable {
* 是否格式化输出
*/
private boolean prettyPrint;
+ /**
+ * 日期格式,null表示默认的时间戳
+ */
+ private String dateFormat;
/**
* 获取是否启用格式化输出
@@ -60,4 +64,25 @@ public class JSONEngineConfig implements Serializable {
this.prettyPrint = prettyPrint;
return this;
}
+
+ /**
+ * 日期格式,null表示默认的时间戳
+ *
+ * @return 日期格式,null表示默认的时间戳
+ */
+ public String getDateFormat() {
+ return dateFormat;
+ }
+
+ /**
+ * 设置日期格式,null表示默认的时间戳
+ * 此方法设置的日期格式仅对转换为JSON字符串有效,对解析JSON为bean无效。
+ *
+ * @param dateFormat 日期格式,null表示默认的时间戳
+ * @return this
+ */
+ public JSONEngineConfig setDateFormat(final String dateFormat) {
+ this.dateFormat = dateFormat;
+ return this;
+ }
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/JSONEngineFactory.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/JSONEngineFactory.java
index 4bf314962..bbf02e663 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/JSONEngineFactory.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/JSONEngineFactory.java
@@ -51,6 +51,9 @@ public class JSONEngineFactory {
if(StrUtil.equalsIgnoreCase("fastjson", engineName)){
engineName = "FastJSON2";
}
+ if(StrUtil.equalsIgnoreCase("hutool", engineName)){
+ engineName = "HutoolJSON";
+ }
if (!StrUtil.endWithIgnoreCase(engineName, "Engine")) {
engineName = engineName + "Engine";
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/engine/JacksonEngine.java b/hutool-json/src/main/java/org/dromara/hutool/json/engine/JacksonEngine.java
index ec45794d6..7b94f96ff 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/engine/JacksonEngine.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/engine/JacksonEngine.java
@@ -19,10 +19,14 @@ package org.dromara.hutool.json.engine;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
+import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.reflect.ConstructorUtil;
+import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.json.JSONException;
@@ -48,6 +52,16 @@ public class JacksonEngine extends AbstractJSONEngine {
Assert.notNull(ObjectMapper.class);
}
+ /**
+ * 获取Jackson的{@link ObjectMapper}对象
+ *
+ * @return {@link ObjectMapper}对象
+ */
+ public ObjectMapper getMapper() {
+ initEngine();
+ return mapper;
+ }
+
@Override
public void serialize(final Object bean, final Writer writer) {
initEngine();
@@ -84,7 +98,7 @@ public class JacksonEngine extends AbstractJSONEngine {
@Override
protected void initEngine() {
- if(null != this.mapper){
+ if (null != this.mapper) {
return;
}
@@ -99,10 +113,36 @@ public class JacksonEngine extends AbstractJSONEngine {
);
// 自定义配置
- if(ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false)){
+ if (ObjUtil.defaultIfNull(this.config, JSONEngineConfig::isPrettyPrint, false)) {
mapper.enable(SerializationFeature.INDENT_OUTPUT);
}
+ final String dateFormat = ObjUtil.apply(this.config, JSONEngineConfig::getDateFormat);
+ if(StrUtil.isNotEmpty(dateFormat)){
+ mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+ mapper.setDateFormat(DateUtil.newSimpleFormat(dateFormat));
+ }
+
+ // 支持Java8+日期格式
+ registerModule(mapper, "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule");
this.mapper = mapper;
}
+
+ /**
+ * 注册模块
+ *
+ * @param mapper Jackson的{@link ObjectMapper}对象
+ * @param moduleClass 模块类名
+ */
+ @SuppressWarnings("SameParameterValue")
+ private void registerModule(final ObjectMapper mapper, final String moduleClass) {
+ final Class> aClass;
+ try {
+ aClass = Class.forName(moduleClass);
+ } catch (final ClassNotFoundException ignore) {
+ //用户未引入JSR310,则跳过不加载模块
+ return;
+ }
+ mapper.registerModule((Module) ConstructorUtil.newInstance(aClass));
+ }
}
diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/engine/BeanWithDate.java b/hutool-json/src/test/java/org/dromara/hutool/json/engine/BeanWithDate.java
new file mode 100644
index 000000000..c41ae2a56
--- /dev/null
+++ b/hutool-json/src/test/java/org/dromara/hutool/json/engine/BeanWithDate.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.json.engine;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Data
+@AllArgsConstructor
+public class BeanWithDate {
+ private Date date1;
+ private LocalDateTime date2;
+}
diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/engine/FastJSONTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/engine/FastJSONTest.java
index fea9da10d..de02063a2 100644
--- a/hutool-json/src/test/java/org/dromara/hutool/json/engine/FastJSONTest.java
+++ b/hutool-json/src/test/java/org/dromara/hutool/json/engine/FastJSONTest.java
@@ -1,5 +1,8 @@
package org.dromara.hutool.json.engine;
+import org.dromara.hutool.core.date.DateTime;
+import org.dromara.hutool.core.date.DateUtil;
+import org.dromara.hutool.core.date.TimeUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -20,4 +23,17 @@ public class FastJSONTest {
" \"gender\":true\n" +
"}", jsonString);
}
+
+ @Test
+ void writeDateFormatTest() {
+ final DateTime date = DateUtil.parse("2024-01-01 01:12:21");
+ final BeanWithDate bean = new BeanWithDate(date, TimeUtil.of(date));
+ final JSONEngine engine = JSONEngineFactory.createEngine("fastjson");
+
+ final String jsonString = engine.toJsonString(bean);
+ Assertions.assertEquals("{\"date1\":1704042741000,\"date2\":1704042741000}", jsonString);
+
+ engine.init(JSONEngineConfig.of().setDateFormat("yyyy-MM-dd HH:mm:ss"));
+ Assertions.assertEquals("{\"date1\":\"2024-01-01 01:12:21\",\"date2\":\"2024-01-01 01:12:21\"}", engine.toJsonString(bean));
+ }
}
diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/engine/GsonTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/engine/GsonTest.java
index 74d575498..42f4db406 100644
--- a/hutool-json/src/test/java/org/dromara/hutool/json/engine/GsonTest.java
+++ b/hutool-json/src/test/java/org/dromara/hutool/json/engine/GsonTest.java
@@ -1,5 +1,8 @@
package org.dromara.hutool.json.engine;
+import org.dromara.hutool.core.date.DateTime;
+import org.dromara.hutool.core.date.DateUtil;
+import org.dromara.hutool.core.date.TimeUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -21,4 +24,17 @@ public class GsonTest {
" \"gender\": true\n" +
"}", jsonString);
}
+
+ @Test
+ void writeDateFormatTest() {
+ final DateTime date = DateUtil.parse("2024-01-01 01:12:21");
+ final BeanWithDate bean = new BeanWithDate(date, TimeUtil.of(date));
+ final JSONEngine engine = JSONEngineFactory.createEngine("gson");
+
+ final String jsonString = engine.toJsonString(bean);
+ Assertions.assertEquals("{\"date1\":1704042741000,\"date2\":1704042741000}", jsonString);
+
+ engine.init(JSONEngineConfig.of().setDateFormat("yyyy-MM-dd HH:mm:ss"));
+ Assertions.assertEquals("{\"date1\":\"2024-01-01 01:12:21\",\"date2\":\"2024-01-01 01:12:21\"}", engine.toJsonString(bean));
+ }
}
diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/engine/HutoolJSONTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/engine/HutoolJSONTest.java
index be50e5668..ab13bb9b3 100644
--- a/hutool-json/src/test/java/org/dromara/hutool/json/engine/HutoolJSONTest.java
+++ b/hutool-json/src/test/java/org/dromara/hutool/json/engine/HutoolJSONTest.java
@@ -1,5 +1,8 @@
package org.dromara.hutool.json.engine;
+import org.dromara.hutool.core.date.DateTime;
+import org.dromara.hutool.core.date.DateUtil;
+import org.dromara.hutool.core.date.TimeUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -17,4 +20,17 @@ public class HutoolJSONTest {
" \"gender\": true\n" +
"}", jsonString);
}
+
+ @Test
+ void writeDateFormatTest() {
+ final DateTime date = DateUtil.parse("2024-01-01 01:12:21");
+ final BeanWithDate bean = new BeanWithDate(date, TimeUtil.of(date));
+ final JSONEngine engine = JSONEngineFactory.createEngine("hutool");
+
+ final String jsonString = engine.toJsonString(bean);
+ Assertions.assertEquals("{\"date1\":1704042741000,\"date2\":1704042741000}", jsonString);
+
+ engine.init(JSONEngineConfig.of().setDateFormat("yyyy-MM-dd HH:mm:ss"));
+ Assertions.assertEquals("{\"date1\":\"2024-01-01 01:12:21\",\"date2\":\"2024-01-01 01:12:21\"}", engine.toJsonString(bean));
+ }
}
diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/engine/JacksonTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/engine/JacksonTest.java
index ecab696f4..bfe327ecc 100644
--- a/hutool-json/src/test/java/org/dromara/hutool/json/engine/JacksonTest.java
+++ b/hutool-json/src/test/java/org/dromara/hutool/json/engine/JacksonTest.java
@@ -1,5 +1,8 @@
package org.dromara.hutool.json.engine;
+import org.dromara.hutool.core.date.DateTime;
+import org.dromara.hutool.core.date.DateUtil;
+import org.dromara.hutool.core.date.TimeUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -23,4 +26,18 @@ public class JacksonTest {
" \"gender\" : true\n" +
"}", jsonString);
}
+
+ @Test
+ void writeDateFormatTest() {
+ final DateTime date = DateUtil.parse("2024-01-01 01:12:21");
+ final BeanWithDate bean = new BeanWithDate(date, TimeUtil.of(date));
+ final JSONEngine engine = JSONEngineFactory.createEngine("jackson");
+
+ final String jsonString = engine.toJsonString(bean);
+ Assertions.assertEquals("{\"date1\":1704042741000,\"date2\":[2024,1,1,1,12,21]}", jsonString);
+
+ //TODO LocalDateTime的格式化未解决
+ engine.init(JSONEngineConfig.of().setDateFormat("yyyy-MM-dd HH:mm:ss"));
+ Assertions.assertEquals("{\"date1\":\"2024-01-01 01:12:21\",\"date2\":\"2024-01-01T01:12:21\"}", engine.toJsonString(bean));
+ }
}