diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONConfig.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONConfig.java index 70297df6f..af02fdf44 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONConfig.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONConfig.java @@ -15,6 +15,7 @@ package org.dromara.hutool.json; import org.dromara.hutool.core.comparator.CompareUtil; import org.dromara.hutool.core.convert.Converter; import org.dromara.hutool.json.convert.JSONConverter; +import org.dromara.hutool.json.writer.NumberWriteMode; import java.io.Serializable; import java.util.Comparator; @@ -64,6 +65,10 @@ public class JSONConfig implements Serializable { * 自定义的类型转换器,用于在getXXX操作中自动转换类型 */ private Converter converter = JSONConverter.of(this); + /** + * Number写出模式 + */ + private NumberWriteMode numberWriteMode = NumberWriteMode.NORMAL; /** * 创建默认的配置项 @@ -257,6 +262,7 @@ public class JSONConfig implements Serializable { /** * 获取自定义的类型转换器,用于在序列化、反序列化操作中实现对象类型转换 + * * @return 转换器 */ public Converter getConverter() { @@ -265,9 +271,33 @@ public class JSONConfig implements Serializable { /** * 设置自定义的类型转换器,用于在序列化、反序列化操作中实现对象类型转换 + * * @param converter 转换器 */ public void setConverter(final Converter converter) { this.converter = converter; } + + /** + * 获取Number写出模式 + * + * @return Number写出模式 + * @since 6.0.0 + */ + public NumberWriteMode getNumberWriteMode() { + return numberWriteMode; + } + + /** + * 设置数字写出模式
+ * 考虑到在JS或其他环境中,Number超过一定长度会丢失精度,因此针对Number类型值,可选写出规则 + * + * @param numberWriteMode Number写出模式 + * @return this + * @since 6.0.0 + */ + public JSONConfig setNumberWriteMode(final NumberWriteMode numberWriteMode) { + this.numberWriteMode = numberWriteMode; + return this; + } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/NumberValueWriter.java b/hutool-json/src/main/java/org/dromara/hutool/json/writer/NumberValueWriter.java index 0b2336bcf..9fde4cc57 100644 --- a/hutool-json/src/main/java/org/dromara/hutool/json/writer/NumberValueWriter.java +++ b/hutool-json/src/main/java/org/dromara/hutool/json/writer/NumberValueWriter.java @@ -22,6 +22,12 @@ import org.dromara.hutool.json.JSONConfig; * @since 6.0.0 */ public class NumberValueWriter implements JSONValueWriter { + + /** + * JS中表示的数字最大值 + */ + private static final long JS_MAX_NUMBER = 9007199254740992L; + /** * 单例对象 */ @@ -39,7 +45,24 @@ public class NumberValueWriter implements JSONValueWriter { public void write(final JSONWriter writer, final Number number) { final JSONConfig config = writer.getConfig(); // since 5.6.2可配置是否去除末尾多余0,例如如果为true,5.0返回5 - final boolean isStripTrailingZeros = null == config || config.isStripTrailingZeros(); - writer.writeRaw(NumberUtil.toStr(number, isStripTrailingZeros)); + final boolean isStripTrailingZeros = (null == config) || config.isStripTrailingZeros(); + final String numberStr = NumberUtil.toStr(number, isStripTrailingZeros); + + final NumberWriteMode numberWriteMode = (null == config) ? NumberWriteMode.NORMAL : config.getNumberWriteMode(); + switch (numberWriteMode){ + case JS: + if(number.longValue() > JS_MAX_NUMBER){ + writer.writeQuoteStrValue(numberStr); + } else{ + writer.writeRaw(numberStr); + } + break; + case STRING: + writer.writeQuoteStrValue(numberStr); + break; + default: + writer.writeRaw(numberStr); + break; + } } } diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/writer/NumberWriteMode.java b/hutool-json/src/main/java/org/dromara/hutool/json/writer/NumberWriteMode.java new file mode 100644 index 000000000..067eb21d3 --- /dev/null +++ b/hutool-json/src/main/java/org/dromara/hutool/json/writer/NumberWriteMode.java @@ -0,0 +1,35 @@ +/* + * 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.writer; + +/** + * Long写出模式
+ * 考虑到在JS或其他环境中,Long超过一定长度会丢失精度,因此针对Long类型值,可选写出规则 + * + * @author Looly + * @since 6.0.0 + */ +public enum NumberWriteMode { + /** + * 一般模式,所有Long值写出为普通数字,如{"value": 123456789} + */ + NORMAL, + /** + * 浏览器中Javascript兼容模式,此模式下,如果Long输出长度大于 + */ + JS, + /** + * 所有Long类型的数字均转为字符串形式,如{"value": "123456789"} + */ + STRING +} diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/writer/Issue3541Test.java b/hutool-json/src/test/java/org/dromara/hutool/json/writer/Issue3541Test.java new file mode 100644 index 000000000..9bbb05bef --- /dev/null +++ b/hutool-json/src/test/java/org/dromara/hutool/json/writer/Issue3541Test.java @@ -0,0 +1,60 @@ +package org.dromara.hutool.json.writer; + +import lombok.Data; +import org.dromara.hutool.json.JSONConfig; +import org.dromara.hutool.json.JSONUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class Issue3541Test { + @Test + void writeNumberJSTest() { + final Demo demo = new Demo(); + // 超出长度 + demo.setId(1227690722069581409L); + demo.setName("hutool"); + final String jsonStr = JSONUtil.toJsonStr(demo, JSONConfig.of().setNumberWriteMode(NumberWriteMode.JS)); + Assertions.assertEquals("{\"id\":\"1227690722069581409\",\"name\":\"hutool\"}", jsonStr); + + // 未超出长度 + demo.setId(1227690722069581L); + final String jsonStr2 = JSONUtil.toJsonStr(demo, JSONConfig.of().setNumberWriteMode(NumberWriteMode.JS)); + Assertions.assertEquals("{\"id\":1227690722069581,\"name\":\"hutool\"}", jsonStr2); + } + + @Test + void writeNumberStringTest() { + final Demo demo = new Demo(); + // 超出长度 + demo.setId(1227690722069581409L); + demo.setName("hutool"); + final String jsonStr = JSONUtil.toJsonStr(demo, JSONConfig.of().setNumberWriteMode(NumberWriteMode.STRING)); + Assertions.assertEquals("{\"id\":\"1227690722069581409\",\"name\":\"hutool\"}", jsonStr); + + // 未超出长度 + demo.setId(1227690722069581L); + final String jsonStr2 = JSONUtil.toJsonStr(demo, JSONConfig.of().setNumberWriteMode(NumberWriteMode.STRING)); + Assertions.assertEquals("{\"id\":\"1227690722069581\",\"name\":\"hutool\"}", jsonStr2); + } + + @Test + void writeNumberNormalTest() { + final Demo demo = new Demo(); + // 超出长度 + demo.setId(1227690722069581409L); + demo.setName("hutool"); + final String jsonStr = JSONUtil.toJsonStr(demo, JSONConfig.of().setNumberWriteMode(NumberWriteMode.NORMAL)); + Assertions.assertEquals("{\"id\":1227690722069581409,\"name\":\"hutool\"}", jsonStr); + + // 未超出长度 + demo.setId(1227690722069581L); + final String jsonStr2 = JSONUtil.toJsonStr(demo, JSONConfig.of().setNumberWriteMode(NumberWriteMode.NORMAL)); + Assertions.assertEquals("{\"id\":1227690722069581,\"name\":\"hutool\"}", jsonStr2); + } + + @Data + public static class Demo { + private Long id; + private String name; + } +}