将Map转成对应实体

📅 发布时间:2026/7/5 3:11:30 👁️ 浏览次数:
将Map转成对应实体
将Map转成对应实体但存在字段类型对不上比如map里面是字符串存在非数字字符但实体是doublemap不需要和实体一一对应map中有的字段实体没有则不会转换也不会报错dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.83/version /dependencyUser user JSON.parseObject(jsonString, User.class);处理Map或JSON字符串中字段类型与Java实体类不匹配的问题比如字符串包含非数字字符却需要转为double类型这里有几种实用的解决方案。我先通过一个表格来汇总它们解决方案适用场景实现方式自定义反序列化器需要精细控制转换逻辑如清洗数据、处理特殊格式实现ObjectDeserializer接口并在字段上使用JSONField(deserializeUsing ...)注解。全局类型转换配置统一处理特定类型如项目中所有String到Double的转换使用SerializeConfig全局注册特定类型的转换器。注解与宽松模式字段格式基本规范只需忽略个别无关字段或简单适配使用JSONField注解指定格式配置ParserConfig启用宽松模式。1、自定义反序列化器当字符串包含非数字字符如货币符号¥100.5、单位123.45cm或千位分隔符1,234.5时推荐使用自定义反序列化器。这让你能完全掌控转换逻辑。实现自定义反序列化器创建一个类实现ObjectDeserializer接口。在deserialze方法中编写清洗和转换逻辑。注意引入的是import com.alibaba.fastjson.parser.DefaultJSONParser;import com.alibaba.fastjson.parser.DefaultJSONParser; import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; import com.alibaba.fastjson.JSONToken; import java.lang.reflect.Type; public class CustomDoubleDeserializer implements ObjectDeserializer { Override public T T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { // 1. 首先将值作为字符串读取 String originalValue parser.parseObject(String.class); if (originalValue null || originalValue.trim().isEmpty()) { return null; } try { // 2. 清洗字符串移除非数字字符保留小数点、负号 String cleanNumberString originalValue.replaceAll([^\\\\d.-], ); // 3. 转换为 Double // 此步骤会自动处理整数如 123和小数如 123.45 return (T) Double.valueOf(cleanNumberString); } catch (NumberFormatException e) { System.err.println(字符串转换Double失败: originalValue); return null; } } Override public int getFastMatchToken() { // 修改此处返回 LITERAL_FLOAT 以同时匹配整数和小数 return JSONToken.LITERAL_FLOAT; } }1.1、匹配策略与令牌选择在 FastJSON 中词法分析器会将 JSON 中的数字转换为特定的令牌。您可以通过getFastMatchToken()方法告知解析器当前反序列化器希望处理哪种类型的令牌。为了实现匹配整数和小数的目标通常有以下两种策略其核心区别如下表所示策略getFastMatchToken()返回值优点缺点策略一精确匹配JSONToken.LITERAL_INT目标准确性能最佳。对于纯整数输入的场景最高效。如果 JSON 中明确是浮点数如123.45可能无法优先匹配。策略二稳健匹配JSONToken.LITERAL_FLOAT兼容性最好能确保覆盖所有数值整数和小数。性能可能有极细微损耗因为浮点令牌的匹配范围更广。2、注解使用在实体类字段上应用使用JSONField注解指定使用这个自定义反序列化器。public class User { private String name; JSONField(deserializeUsing CustomDoubleDeserializer.class) private Double salary; // 使用Double包装类型以更好地处理null // 省略 getter 和 setter }3、考虑全局类型转换配置 -(没生效)如果项目中有大量同类转换需求全局配置更高效。通过SerializeConfig在全局层面为特定类型如String到Double注册一个通用的转换器。这样Fastjson 在遇到类型不匹配时会尝试使用这个全局转换器。import com.alibaba.fastjson.parser.ParserConfig; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; Configuration public class FastJsonConfig { /** * 使用 PostConstruct 在Bean初始化后执行 */ PostConstruct public void initFastJsonGlobalConfig() { // 关闭ASM以解决复杂对象反序列化问题 // 当反序列化的目标对象属性超过一定数量如32个时FastJSON默认使用的ASM技术可能无法正确处理自定义反序列化器这时会报错 ParserConfig.getGlobalInstance().setAsmEnable(false); ParserConfig.getGlobalInstance().putDeserializer(Double.class, new CustomDoubleDeserializer()); System.out.println(全局Double类型反序列化器已注册。); } /** * 或者实现 ApplicationRunner 接口在应用完全启动后执行 */ // Override // public void run(ApplicationArguments args) throws Exception { // ParserConfig.getGlobalInstance().putDeserializer(Double.class, new CustomDoubleDeserializer()); // } }3.1、全局配置失效您遇到的全局自定义反序列化器配置未生效的问题通常与配置的加载时机、作用范围或环境冲突有关。下面是一个系统的排查指南。1、排查步骤与解决方案排查方向关键检查点建议操作1. 配置加载时机确保PostConstruct方法在任何JSON反序列化操作之前执行。在init()方法起始处添加日志确认其是否在应用启动初期被调用。检查是否有其他组件在Spring容器初始化阶段就进行了反序列化。2. 配置类加载确保FastJsonConfig类被Spring组件扫描到。确认类位于Spring Boot的主应用类SpringBootApplication所在包或其子包下。如不在使用ComponentScan显式指定包路径。3. 版本与兼容性不同版本的Fastjson在处理全局反序列化器时可能存在差异或Bug。尝试升级Fastjson到最新稳定版本如1.2.83或更高以确保稳定性。4. JSON数据结构确认您的JSON字符串中对应字段的类型。如果JSON中salary字段的值是数字如{salary: 15000.5}Fastjson可能会使用内置的Double解析器。请确保测试数据中该字段是字符串如{salary: 15,000.50元}以触发您的自定义逻辑。5. 配置覆盖项目中其他地方是否重置了ParserConfig的全局实例。检查代码中是否有其他地方调用了ParserConfig.getGlobalInstance().setXXX或创建了新的ParserConfig实例这可能会覆盖您的配置。6. 依赖冲突项目中是否存在多个版本的Fastjson。使用mvn dependency:tree或 Gradle的依赖树命令检查是否有依赖引入了不同版本的Fastjson解决冲突。2、进阶排查类加载器隔离问题在Spring Boot打包成可执行JAR即fat jar运行时可能会遇到一个更深层次的问题类加载器隔离。问题根源Spring Boot使用自定义的LaunchedURLClassLoader来加载BOOT-INF/classes和BOOT-INF/lib下的类。而您通过Configuration定义的配置和反序列化器都在这个类加载器中。但某些情况下例如在Agent中或通过特定方式引用的Fastjson核心类可能由系统类加载器AppClassLoader加载。这会导致ParserConfig.getGlobalInstance()在不同的类加载器视角下可能不是同一个实例造成配置看似生效实则无效。解决方案如果上述常规排查均无效可以尝试一种更直接的方式在每次反序列化时显式指定配置。// 创建一个新的、独立的配置实例 ParserConfig config new ParserConfig(); config.putDeserializer(Double.class, new CustomDoubleDeserializer()); // 在解析时使用这个配置 User user JSON.parseObject(jsonString, User.class, config);这种方式虽然不如全局配置方便但它确保了配置的绝对有效性避免了复杂的类加载环境干扰。3、总结全局配置未生效通常按以下顺序排查确认配置类被正确加载加日志最直接。检查JSON数据格式是否为字符串。检查依赖是否存在版本冲突。若仍无法解决考虑是否是类加载器隔离问题并尝试使用显式传入配置的方式。希望这些步骤能帮助您定位并解决问题。如果方便可以分享您的Spring Boot主类位置和部分日志以便更精确地分析。4、尝试注解与宽松模式对于更简单的情况可以尝试以下方法使用JSONField格式化如果字符串是标准数字格式如日期、数字可用JSONField注解的format属性简单指定格式。启用宽松模式通过配置ParserConfig的setAutoTypeSupport(true)等方法让 Fastjson 以更宽松的方式解析尝试自动类型转换。在 FastJSON 中使用JSONField注解和通过ParserConfig进行全局注册是两种不同的自定义反序列化器配置方式。它们各有特点可以单独使用也可以组合使用但需要理解其优先级和生效范围。下表清晰地展示了两者的核心区别特性JSONField注解方式ParserConfig全局注册方式作用范围字段级别仅对特定注解字段生效类型级别对该类型如Double的所有字段生效配置位置直接定义在实体类的字段上在配置类中集中管理如PostConstruct方法侵入性较强需修改实体类代码较弱不修改实体类配置与代码分离维护性分散在各个实体类中不利于统一修改配置集中方便统一管理和修改优先级更高字段级别配置优先相对较低1、如何选择和使用可以单独使用注解如果你的自定义逻辑只针对某个特定类中的个别字段并且你希望这种配置明确地与这个字段绑定那么单独使用JSONField注解是简洁直观的选择。优点配置精准一目了然。缺点如果多个类的多个字段都需要同样的处理逻辑就需要在每个字段上重复注解不利于维护。可以单独使用全局配置当你需要为某种数据类型如所有Date类型字段、所有Double类型字段统一应用自定义的反序列化逻辑时全局配置是更优的选择。优点一劳永逸一次注册全局生效。非常适合处理通用的数据清洗、格式转换等场景。注意点确保配置代码如FastJsonConfig类的init方法在任何 JSON 反序列化操作之前执行。可以结合使用注意优先级两种方式可以共存。当同时存在时FastJSON 会按照优先级来决定使用哪个反序列化器字段级别的JSONField注解 全局ParserConfig注册的类型反序列化器。应用场景你可以为Double类型设置一个全局的、通用的反序列化器。如果某个特定字段需要特殊处理可以再在这个字段上使用JSONField指定一个不同的反序列化器它将覆盖全局配置。2、注解可能“失效”的排查点你之前遇到的JSONField注解“似乎没有生效”的情况除了反序列化器本身的实现细节外还可能源于一些特定场景。例如有报告指出当对象被放入JSONArray后再通过JSONArray.getObject(index, Class)方法取出时注解信息可能会在某些 FastJSON 版本的处理流程中被忽略导致自定义反序列化器未被调用。在这种情况下使用全局配置往往是更可靠的选择。