Json数据格式化问题_json数据格式化问题怎么解决
一 业务背景
需要将已保存的json字符串获取,并提取其中的某个字段按“,”拼接为字符串。
二 代码
2.1 实体
public class LangDataLimitDTO implements Serializable {
/** 一级限制类别id */
private Integer typeId;
/** 一级限制类别名称 */
private String typeName;
public LangDataLimitDTO(Integer typeId, String typeName){
this.typeId = typeId;
this.typeName = typeName;
}
2.2 格式转化代码
public String getLangLimitStr(){
if(StringUtils.isEmpty(languageLimitJson)){
return null;
}
List<LangDataLimitDTO> langDataLimitList = JsonUtils.fromJSON(languageLimitJson, new TypeReference<List<LangDataLimitDTO>>(){});
if(CollectionUtils.isEmpty(langDataLimitList)){
return null;
}
StringBuilder sb = new StringBuilder();
for(LangDataLimitDTO langDataLimitDTO : langDataLimitList){
sb.append(langDataLimitDTO.getTypeName()).append(",");
}
return StringUtils.removeEnd(sb.toString(), ",");
}
2.3 工具
/**
*
* 描述:将json字符串转成指定T类型的对象
* 示例:formJSON(jsonString,new MyTypeReference()<List<Map<String,Object>>>)
* @created 2014-4-7 上午10:04:20
* @since v1.0.0
* @param jsonString
* @param typeRef
* @return T
*/
@SuppressWarnings("unchecked")
public static final <T> T fromJSON(String jsonString, TypeReference<T> typeRef) {
if (null == jsonString)
return null;
ByteArrayInputStream bais = new ByteArrayInputStream(jsonString.getBytes());
try {
ObjectInput in = serialization.deserialize(bais);
return (T) in.readObject(typeRef);
} catch (Exception e) {
throw new RuntimeException("Json deserial error.", e);
} finally {
try {
if (null != bais) {
bais.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.4 序列化方式
private static final Serialization serialization = new JacksonSerialization();
三 问题
当使用getLangLimitStr()转化数据时,传入的合法的json串数据,返回的格式化对象为空的问题。
3.1 TypeReference分析
TypeReference为jackson包提供的用于处理泛型类型信息的工具,主要用于反序列化时明确指定目标类型,确保数据转换的准确性。
3.2 原因
1、反射实例化需求
Jackson默认通过反射调用无参构造器创建对象实例,再通过setter方法或字段注入填充属性。若类中仅存在有参构造器而无无参构造器,反射将无法实例化对象,导致反序列化失败。
2、框架设计约束
Jackson的ObjectMapper在反序列化时遵循标准JavaBean规范,要求目标类具备无参构造器以实现通用化处理。这种设计简化了反序列化流程,使其无需预知具体构造逻辑即可重建对象。
3、兼容性考量
无参构造器是Java序列化机制(如java.io.Serializable)和多数ORM框架(如Hibernate)的共同要求。Jackson保持这一约束可确保与其他技术栈的兼容性。
3.3 默认生成问题
默认无参构造方法的行为规则
1、自动生成条件
当类中未显式定义任何构造方法时,Java编译器会自动生成一个默认的无参构造方法(访问修饰符与类相同)。
public class User {} // 编译器会自动添加 public User() {}
2、手动定义覆盖
若类中已定义任意构造方法(包括有参构造),编译器不再自动生成无参构造方法。此时若需无参构造,必须显式声明
public class User {
private String name;
public User(String name) { this.name = name; }
public User() {} // 必须手动添加
}
四 解决
4.1 添加无参构造
public class LangDataLimitDTO implements Serializable {
/** 一级限制类别id */
private Integer typeId;
/** 一级限制类别名称 */
private String typeName;
public LangDataLimitDTO(){
}
public LangDataLimitDTO(Integer typeId, String typeName){
this.typeId = typeId;
this.typeName = typeName;
}
4.2 实现反序列化器
public class ResponseDeserializer extends JsonDeserializer<Response> {
@Override
public Response deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
Map<String, Map<String, String>> map = jsonParser.readValueAs(Map.class);
Map<String, String> headerMap = map.get("headerMap");
Map<String, String> apiMap = map.get("apiMap");
return new Response(headerMap, apiMap);
}
}
将反序列化器注册到 ObjectMapper 中:
public class ObjectMapperUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
SimpleModule simpleModule = new SimpleModule("custom");
simpleModule.addDeserializer(Response.class, new ResponseDeserializer());
objectMapper.registerModule(simpleModule);
}
public static ObjectMapper getObjectMapper() {
return objectMapper;
}
/**
*
* @param object
* @return
*/
public static String writeAsPrettyString(Object object) {
try {
return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}