定制key-field的关系
- name的映射定制:
@JsonProperty
,其value指定是序列化后的key名称。比如:一些系统的产生的id格式为'_id'。
设置忽略
- 直接对属性设置:
@JsonIgnore public int internalValue;
- 对类进行设置:
@JsonIgnoreProperties({ "age" })
,此设置可以完成两个目标:序列化时忽略age,反序列化时出现age不会抛出异常。 - 针对反序列化时,出现的未知key,可以使用
@JsonIgnoreProperties(ignoreUnknown=true)
,避免异常的出现。 - 针对某一个类,让其成为被忽略的类(是被组合的类),可以该类上使用
@JsonIgnoreType
- 当对象的属性为“空”时,如果不希望在序列化时出现null,0,[]的情况,可以使用
@JsonInclude
,它的典型取值:JsonInclude.Include.NON_NULL
表示null,@JsonInclude(JsonInclude.Include.NON_EMPTY)
,表示null和集合为空,@JsonInclude(JsonInclude.Include.NON_DEFAULT)
,表示为赋初值的情况(null,0,0.0,空集合,false);可以将此注解应用于Field
格式的设置
- 日期时间格式:默认会将java.util.Date转化成为GMT的整型数值,可以使用
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss",timezone = "GMT+8")
,作用于序列化及反序列化。 - 当需要更加高级的格式设置时,可以使用
JsonSerializer,JsonDeserializer
,详细案例:
//pojo Customer.class中
@JsonSerialize(using = CurrencySerializer.class)
@JsonDeserialize(using = CurrencyDeSerializer.class)
private double salary;
//序列化时使用
public class CurrencySerializer extends JsonSerializer {
@Override
public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
NumberFormat nf=NumberFormat.getCurrencyInstance();
gen.writeString(nf.format(value));
}
}
//反序列化时使用
public class CurrencyDeSerializer extends JsonDeserializer {
@Override
public Double deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
NumberFormat nf=NumberFormat.getCurrencyInstance();
double m=0.0;
try {
m=nf.parse(p.getText()).doubleValue();
} catch (ParseException e) {
e.printStackTrace();
}
return m;
}
}
循环引用所带来的问题:
- StackOverFlow:在双方分别使用:
@JsonManagedReference, @JsonBackReferenc
- 相同对象的识别需求:多个对象都有着相同的引用时,都面临着以下问题:
- 序列化时,会生成冗余的数据
[city:"bj",customer:{id:1},city:"sh",customer:{id:1}]
- 反序列化时,会产生两个内容相同的对象。
- 序列化时,会生成冗余的数据
此时可以使用@JsonIdentityInfo
,解决此问题,方法如下:
@JsonIdentityInfo(generator =ObjectIdGenerators.IntSequenceGenerator.class
,property = "@id")
public class Customer {}
此时序列化后的对象,格式如下:
[{"city":"bj","customer":{"@id":1,"id":1,"cname":"john"},{"city":"sh","customer":1}]
对序列化后,customer对象为同一个。
反序列化陷阱:型别擦除
- jackson会把json集合中的数据,看作是HashMap看待(实际类型被擦除)
- 此时需要在解析时,重新铸型,方法如下:
ObjectMapper mapper = new ObjectMapper();
JavaType listAddressType= mapper.getTypeFactory() .constructCollectionType(List.class,Address.class);
List list=mapper.readValue(new File("data.txt"), listAddressType);
- 型别不会擦除的情况:
public class Album {
private String title;
private Set musics = new HashSet<>();
}
此情况下,无需要铸型,可以直接反序列化成功:
Album album=mapper.readValue(new File("album1.json"),Album.class);
- 当你使用了泛型类的设计情况:
public class Album {
private String title;
private Set musics = new HashSet<>();
}
面对以下的数据:
{"title":"resolved","musics":[{"name":"rocking"},{"name":"goodBye"}]}
jackson将无法知道,name是哪个类型数据中的field,可以采用以下形式,对其进行指引:
//错误的方式,型别擦除
//Album album=mapper.readValue(file,Album.class);
//可以使用TypeReference进行重新铸型
Album album=mapper.readValue(file,new TypeReference>(){});
“运行时”类型,还是“声明”类型
- 在序列化一个Filed时,默认会“根据它的runtime类型”进行,但如果我们只想要其某个父类的类型,或者其“声明时类型”,此时可以:
//@JsonSerialize(as=Music.class) 默认是实际类型
@JsonSerialize(typing = JsonSerialize.Typing.STATIC)
private Music music;//music可能会是子类Song
此时,只会将Music中的成员进行序列化,而忽略子类型的成员。
- 在“反序列”时,默认只会按照“声明类型”进行。
private Music music;//music可能会是子类Song
此时进行解析,music中多余的key-value会导致异常(也可进行Ignore)。如果想按照子类型进行解析,此时可以:
@JsonDeserialize(as = Song.class)
private Music music;//music可能会是子类Song