JSONObject序列化时间异常

文章描述了一个在处理JSONObject序列化时遇到的问题,即时间字段被转换为毫秒数而不是保持日期格式。问题源于直接使用JSONObject导致Date类型数据转换。解决方案是创建一个专门的类BalanceForecastDataVo来封装时间字段,并实现Serializable接口,从而确保序列化和反序列化时不丢失日期信息。此外,文章还提到了一个定时任务TimingTask使用StringRedisTemplate进行缓存操作。

JSONObject序列化时间异常

  • 场景:Redis数据缓存
  • 数据格式:Map集合
{
  "min": {
    "windPowerSupply": 381.2,
    "storagePowerSupply": 20,
    "wge2": 385.8,
    "opticalPowerSupply": 0,
    "peakShavingCapacity": 2104.1,
    "thermalPowerSupply": 2564.1,
    "powerSupplyCapacity": 3778.1,
    "loadForecast": 3182.2,
    "powerSupplyMargin": 595.9,
    "allocationReserve": 148,
    "hydraulicPowerSupply": 575,
    "forecastDate": "2023-02-09 10:10:10",
    "peakRegulationMargin": 1078.1
  },
  "max": {
   ...
  },
  "details": [
    ...
  ]
}
  • 问题:JSONObject转换后(时间字段变成了毫秒数)
{
  "min": {
    "windPowerSupply": 381.2,
    "storagePowerSupply": 20,
    "wge2": 385.8,
    "opticalPowerSupply": 0,
    "peakShavingCapacity": 2104.1,
    "thermalPowerSupply": 2564.1,
    "powerSupplyCapacity": 3778.1,
    "loadForecast": 3182.2,
    "powerSupplyMargin": 595.9,
    "allocationReserve": 148,
    "hydraulicPowerSupply": 575,
    "forecastDate": 1675850400000,
    "peakRegulationMargin": 1078.1
  },
  "max": {
    ...
  },
  "details": [
    ...
  ]
}
  • 问题分析

    • Redis格式化的原因(确实存在,但这里不是Redis的问题)

      Redis会格式化会将时间字段转换成毫秒数进行存储,转出时,接收对象可被序列化成功,不会影响数据结构

    • 序列化导致的,时间字段存放在Map集合中,JSONObject直接将时间字段转换成了毫秒数(主要)

      • 转入时,Date类型数据会被转换成毫秒数

      • 转出时,元数据不会被转换成Date类型数据

  • 解决方案

    • 用单独的类对时间字段进行封装,并将类进行序列化,json转换前后不会受到影响

序列化类:BalanceForecastDataVo.class

@Data
public class BalanceForecastDataVo implements Serializable {
    private List<PmBalancedForecastDetailVo> chart;
    private List<PmBalancedForecastDetailVo> details;
    private PmBalancedForecastDetailVo max;
    private PmBalancedForecastDetailVo min;
}

定时任务:TimingTask.class

@Component
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class TimingTask {
    private final IPmBalancedForecastService iPmBalancedForecastService;
    private final StringRedisTemplate stringRedisTemplate;
	private void makeCachePhyc() {
        BalanceForecastDataVo totalNew = iPmBalancedForecastService.getTotalNew();
        stringRedisTemplate.opsForValue()
            .set(PowerGenerationGroup.PHYC_ZL, JSONObject.toJSONString(totalNew), 60, TimeUnit.SECONDS);
    }
}

目标方法:getTotalNew

public BalanceForecastDataVo getTotalNew() { 
	BalanceForecastDataVo balanceForecastDataVo = new BalanceForecastDataVo();
    String redisData = stringRedisTemplate.opsForValue().get(PowerGenerationGroup.PHYC_ZL);
    if(NullCheckUtils.isNotNull(redisData)) {
        balanceForecastDataVo = JSONArray.parseObject(redisData, BalanceForecastDataVo.class);
        return balanceForecastDataVo;
    }
}
  • 其他序列化方式,尝试过,但是未找到靠谱方案
<think> 我们参考用户问题和引用内容,用户想了解JSONObject.parseObject方法的异常处理方式。 根据之前的回答,我们总结了主要的异常类型和处理建议。现在需要结合引用内容进一步说明。 引用[2]中提到了两种JSON库对null的处理差异: - net.sf.json.JSONObject.fromObjectJSON中的null值转换为字符串"null" - com.alibaba.fastjson.JSONObject.parseObject 将JSON中的null值转换为Java中的null 因此,在异常处理中,我们还需要注意不同库对null值的处理差异,避免因为字符串"null"和Java null的混淆导致空指针异常。 另外,引用[3]提到了当JSON中存在多个可能映射到同一个字段的键(如"id"和"_id")时,可以使用@JSONField注解来解决映射问题。如果映射错误,可能会导致解析后的对象字段为null,进而可能引发空指针异常。 结合之前的异常类型,我们补充以下几点: 1. 空指针异常(NullPointerException): 除了传入的JSON字符串为null会抛出异常外,在解析后使用对象时,如果字段为null并且没有正确处理,也可能导致空指针异常。 例如: User user = JSONObject.parseObject(jsonStr, User.class); String name = user.getName().toLowerCase(); // 如果name为null,这里会抛NullPointerException 因此,建议: - 对解析后的对象字段进行空值检查 - 使用Optional类来避免空指针 2. 类型映射异常(如ClassCastException): 引用[3]中提到,当JSON中的键与Java字段映射错误时,可能导致字段值不正确(如本应映射到_id字段的值映射到了id字段,或者反之)。这可能导致类型转换错误。 3. JSON格式异常JsonParseException): 引用[1]中使用了JSON.toJSONStringWithDateFormat方法,这提示我们日期格式转换也可能引发异常。如果日期字符串不符合指定的格式,解析时可能抛出异常。 综合以上,我们给出详细的异常处理方式: ### 异常处理方式 1. **空指针异常处理**: - 检查传入的JSON字符串是否为null或空字符串 - 使用安全访问方式处理解析后对象的字段 ```java if (StringUtils.isBlank(jsonStr)) { // 处理空字符串情况,如返回默认对象或抛出业务异常 throw new IllegalArgumentException("JSON字符串不能为空"); } try { User user = JSONObject.parseObject(jsonStr, User.class); // 使用Optional避免空指针 String name = Optional.ofNullable(user.getName()).orElse("default"); } catch (Exception e) { // 记录日志并处理 } ``` 2. **JSON格式异常处理**: - 使用try-catch捕获JsonParseException - 对日期等特殊格式进行预处理 ```java try { // 如果日期格式固定,可以提前转换 // 例如,将日期字符串统一格式化成指定格式 // 但通常更建议在注解中指定格式,如@JSONField(format="yyyy-MM-dd HH:mm:ss") User user = JSONObject.parseObject(jsonStr, User.class); } catch (JsonParseException e) { logger.error("JSON解析失败: {}", jsonStr, e); // 可以尝试使用更宽松的解析模式,如Feature.IgnoreNotMatch User user = JSONObject.parseObject(jsonStr, User.class, Feature.IgnoreNotMatch); } ``` 3. **类型映射异常处理**: - 使用@JSONField注解明确字段映射关系 - 捕获ClassCastException并处理 ```java public class User { @JSONField(name="_id", alternateNames={"id"}) private String id; // ...其他字段 } ``` 如果解析后使用字段时发生类型转换异常,需要检查字段类型是否匹配。 4. **ASM反序列化错误处理**: - 如果遇到ASM相关错误,可以关闭ASM特性,使用JavaBeanDeserializer - 通过ParserConfig设置 ```java ParserConfig.getGlobalInstance().setAsmEnable(false); // 关闭ASM ``` ### 通用异常处理模板 ```java public static <T> T parseJson(String jsonStr, Class<T> clazz) { if (StringUtils.isBlank(jsonStr)) { throw new IllegalArgumentException("JSON字符串不能为空"); } try { // 可以在这里添加特殊字符的清理逻辑 String cleanedJson = jsonStr.replace("\u0000", ""); // 示例:移除空字符 return JSONObject.parseObject(cleanedJson, clazz); } catch (JsonParseException e) { // JSON格式错误 logger.error("JSON格式错误: {}", jsonStr, e); throw new BusinessException("JSON格式错误"); } catch (Exception e) { // 其他异常,如ASM异常、类型不匹配等 logger.error("JSON解析异常", e); // 尝试关闭ASM再次解析 ParserConfig.getGlobalInstance().setAsmEnable(false); try { return JSONObject.parseObject(jsonStr, clazz); } catch (Exception ex) { logger.error("第二次解析失败", ex); throw new BusinessException("JSON解析失败"); } } } ``` ### 注意事项 - **版本问题**:FastJSON 1.x版本存在一些安全漏洞和稳定性问题(如空指针导致JVM崩溃),建议升级到FastJSON 2.x[^2]。 - **特殊字符**:对于JSON字符串中可能包含的特殊字符(如未转义的控制字符),需要在解析前进行清理。 - **日期格式**:使用`@JSONField`注解指定日期格式,或者使用`JSON.toJSONStringWithDateFormat`方法统一日期格式[^1]。 ### 不同版本FastJSON异常处理差异 | 异常场景 | FastJSON 1.x | FastJSON 2.x | |------------------|-------------------------------|-------------------------------| | **空指针异常** | 可能导致JVM崩溃 | 优化为抛出标准异常 | | **ASM支持** | 默认开启,易出错 | 优化ASM,减少错误 | | **日期解析** | 格式错误抛异常 | 提供更多日期解析配置选项 | §§相关问题§§ 1. 如何在FastJSON中处理日期格式解析异常? 2. FastJSON 2.x 在空指针处理上做了哪些优化? 3. 如何安全地处理JSON字符串中的特殊字符以预防解析异常? </think>### `JSONObject.parseObject` 异常处理详解 以下是针对 `JSONObject.parseObject` 方法的异常处理方式及优化建议,结合引用内容分析: --- ### 一、核心异常类型及处理方案 1. **`NullPointerException`(空指针异常)** **触发场景**: - 传入 JSON 字符串为 `null`(如 `JSONObject.parseObject(null)`)[^2] - 解析后未做空值判断直接操作字段(如 `userInfo.getString("anull").equals(...)`) **解决方案**: ```java // 1. 显式检查空值 if (StringUtils.isNotBlank(jsonStr)) { JSONObject userInfo = JSONObject.parseObject(jsonStr); // 2. 使用安全访问方式 String value = userInfo.getString("anull"); if (value != null) { // 业务逻辑 } } ``` 2. **`JsonParseException`(JSON 格式错误)** **触发场景**: - JSON 格式不规范(如属性名未用双引号 `{'name':'xiaoji'}`) - 特殊字符未转义(如 `/`、`\u0000` 等控制字符) - 数据类型不匹配(如将字符串解析为整数) **解决方案**: ```java try { // 预处理特殊字符 String sanitizedJson = jsonStr .replace("\\/", "/") .replace("\u0000", ""); // 移除空字符 JSONObject.parseObject(sanitizedJson); } catch (JsonParseException e) { logger.error("JSON格式错误: {}", sanitizedJson, e); // 回退方案:使用宽松解析模式 JSONObject.parseObject(sanitizedJson, Feature.IgnoreNotMatch); } ``` 3. **映射错误(字段名冲突)** **触发场景**: - JSON 中存在多个同名字段(如 `id` 和 `_id`)导致映射混乱[^3] **解决方案**: ```java public class User { @JSONField(name = "_id", alternateNames = { "id" }) // 明确映射关系 private String userId; // 其他字段... } ``` --- ### 二、通用异常处理模板 ```java public static <T> T safeParse(String jsonStr, Class<T> targetClass) { if (StringUtils.isBlank(jsonStr)) { return null; // 或抛自定义业务异常 } try { // 1. 特殊字符清理 String cleanedJson = jsonStr .replace("\u0000", "") .replace("\\/", "/"); // 2. 带容错配置的解析 return JSON.parseObject( cleanedJson, targetClass, Feature.IgnoreNotMatch, // 忽略不匹配字段 Feature.AllowArbitraryCommas // 允许多余逗号 ); } catch (JsonParseException e) { logger.error("JSON解析失败: {}", jsonStr, e); // 3. 回退到更宽松的解析 return JSON.parseObject(jsonStr, targetClass, Feature.IgnoreAutoType); } catch (Exception e) { logger.error("系统异常", e); throw new BusinessException("解析失败"); } } ``` --- ### 三、版本升级建议(FastJSON 2.x) FastJSON 2.x 针对异常处理的优化: 1. **空指针安全性** - 旧版 `parseObject(null)` 会导致 JVM 崩溃,新版改为抛出标准异常[^2]。 2. **增强容错能力** - 支持 `Feature.IgnoreCheckClose` 忽略未闭合的 JSON 结构。 3. **ASM 优化** - 修复泛型类型解析问题(如 `List<MyClass>` 的反序列化错误)[^3]。 --- ### 四、跨库异常差异对比 | 异常场景 | FastJSON | Jackson | Gson | |------------------|-------------------|-------------------|------------------| | **空值处理** | `getString("key")` 返回 `null` [^2] | `get("key").asText()` 返回空字符串 | `get("key").getAsString()` 抛异常 | | **格式错误** | `JsonParseException` | `JsonParseException` | `JsonSyntaxException` | | **类型不匹配** | `ClassCastException` | `InvalidFormatException` | `IllegalStateException` | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值