org.json.JSONObject实例化时自动移除BigDecimal末尾零的问题
如何避免JSONObject解析时移除数值末尾的零?
这个问题的核心原因在于org.json库的JSONObject会自动将JSON中的数值类型解析为Java的Number子类(比如Double或Integer),而这些类型在toString时会自动去掉末尾无意义的零——比如473935.00会被解析为Double类型的473935.0,最终输出时就变成了473935;585538.30会被解析为585538.3,输出时末尾的零就消失了。
下面给你几种可行的解决方案,根据你的需求选择:
方案1:将数值以字符串形式存储(推荐,适合需要严格保留格式的场景)
如果你的业务需要严格保留小数位数(比如金额场景),最稳妥的方式是让API返回时就把这些数值以字符串类型返回,比如:
{ "id": "4d27afb18bfa4eb5917beee9aaddfa76", "hedgeId": 205598, "sellCurrency": "USD", "buyCurrency": "EUR", "buyAmount": "473935.00", "sellAmount": "585538.30" }
这样JSONObject解析后,buyAmount和sellAmount会被保留为字符串类型,末尾的零自然不会丢失。
如果API无法修改返回格式,你可以在解析前手动将数值替换为字符串(用正则表达式),但这种方式要注意边界情况,比如避免误改其他数值:
// 简单的正则替换,将带两位小数的数值转换为字符串 String modifiedJson = thatStringAbove.replaceAll("(\\d+\\.\\d{2})", "\"$1\""); JSONObject foo = new JSONObject(modifiedJson); // 此时foo中的buyAmount和sellAmount是字符串,保留了末尾零
方案2:使用更灵活的JSON库(比如Gson或Jackson)自定义序列化规则
org.json库的灵活性较差,如果你能切换到Gson或Jackson,可以通过自定义序列化逻辑强制保留小数位数。
用Gson实现:
- 定义一个自定义序列化器,指定保留两位小数:
import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializer; import java.lang.reflect.Type; import java.math.BigDecimal; import java.text.DecimalFormat; public class FixedDecimalSerializer implements JsonSerializer<BigDecimal> { private final DecimalFormat decimalFormat; public FixedDecimalSerializer() { decimalFormat = new DecimalFormat("#.00"); decimalFormat.setMinimumFractionDigits(2); // 强制保留两位小数 decimalFormat.setMaximumFractionDigits(2); decimalFormat.setGroupingUsed(false); // 不使用千分位分隔符 } @Override public JsonElement serialize(BigDecimal value, Type type, JsonSerializationContext context) { return new JsonPrimitive(decimalFormat.format(value)); } }
- 初始化Gson并使用该序列化器:
import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.math.BigDecimal; // 假设你有对应的实体类HedgeTransaction,字段类型用BigDecimal HedgeTransaction transaction = new Gson().fromJson(thatStringAbove, HedgeTransaction.class); Gson gsonWithFixedDecimals = new GsonBuilder() .registerTypeAdapter(BigDecimal.class, new FixedDecimalSerializer()) .create(); String formattedJson = gsonWithFixedDecimals.toJson(transaction); // formattedJson中的buyAmount会是"473935.00",sellAmount是"585538.30"
用Jackson实现:
- 自定义序列化器并注册到ObjectMapper:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import java.io.IOException; import java.math.BigDecimal; import java.text.DecimalFormat; public class FixedDecimalJacksonSerializer extends JsonSerializer<BigDecimal> { private final DecimalFormat decimalFormat = new DecimalFormat("#.00"); @Override public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(decimalFormat.format(value)); } } // 初始化ObjectMapper ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(BigDecimal.class, new FixedDecimalJacksonSerializer()); mapper.registerModule(module); // 解析并序列化 HedgeTransaction transaction = mapper.readValue(thatStringAbove, HedgeTransaction.class); String formattedJson = mapper.writeValueAsString(transaction);
方案3:在org.json的JSONObject中手动格式化数值
如果必须使用org.json库,可以在解析后手动将数值格式化为带两位小数的字符串,再放回JSONObject:
import org.json.JSONObject; import java.math.BigDecimal; import java.text.DecimalFormat; JSONObject foo = new JSONObject(thatStringAbove); DecimalFormat df = new DecimalFormat("#.00"); // 处理buyAmount BigDecimal buyAmount = foo.getBigDecimal("buyAmount"); foo.put("buyAmount", df.format(buyAmount)); // 处理sellAmount BigDecimal sellAmount = foo.getBigDecimal("sellAmount"); foo.put("sellAmount", df.format(sellAmount)); // 此时foo.toString()会保留末尾的零
这种方式需要逐个处理需要保留格式的字段,适合字段数量较少的场景。
内容的提问来源于stack exchange,提问作者iamkenos
相关产品推荐
相关产品推荐

