基础入门
注意,本文用到的Jackson版本为2.2.3。 Java Object和json之间的转换(Object -> json为serialize,反之为deserialize)是通过ObjectMapper类来实现,最简单是实现如下:
//被转换的对象
public class Person {
String firstname;
String lastname;
String phone;
List<Address> addresses = new ArrayList<Address>();
//这里省略setter、getter
public Address addNewAddress() {
Address address = new Address();
addresses.add(address);
return address;
}
public String getFullName() {
return firstname+"/"+lastname;
}
static class Address{
String street;
String city;
Integer number;
//这里省略setter、getter
}
}
public class JacksonTest {
public static void main(String[] args) {
try {
Person person = new Person();
person.setFirstname("dennis");
person.setLastname("lau");
person.setPhone("123456789");
Person.Address address = person.addNewAddress();
address.setCity("shenzhen");
address.setStreet("renming road");
address.setNumber(1234);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(person);
System.out.println(json);
}catch (Exception e) {
e.printStackTrace(System.out);
}
}
}
输出:{"firstname":"dennis","lastname":"lau","phone":"123456789","addresses":[{"street":"renming road","city":"shenzhen","number":1234}],"fullName":"dennis/lau"}
使用默认构站器的ObjectMapper在默认配置下将会序列化含有getter的值(不一定有真实相对应的变量成员,如Person的getFullName),如果我们只想序列化类对应的field,有两个解决办法:
1.使用Jackson Annotations
使用Jackson Annotation的@JsonIgnore,将该注释加在不想序列化的getter方法,就能达到在序列化时忽略该field的效果:
//其余配置 保持不变
@JsonIgnore public String getFullName() { return firstname+"/"+lastname; }
输出:{"firstname":"dennis","lastname":"lau","phone":"123456789","addresses":[{"street":"renming road","city":"shenzhen","number":1234}]} //对比未加注释,这里的fullName不存在了。
2.更改MapperFeature的配置
更改ObjectMapper实例的MapperFeature的AUTO_DETECT_GETTERS为disbale,这样序列化时所有的getter都不会被检测(如果filed的访问范围为public,还是可见的,能够被序列化为json的属性,否则都不会被序列化),再在需要序列化的field对应的getter方法加上Annotation @JsonProperty(因为一般情况下field都是private或者package可见)。
public class Person { String firstname; String lastname; String phone; List<Address> addresses = new ArrayList<Address>(); @JsonProperty public String getFirstname() { return firstname; } @JsonProperty public String getLastname() { return lastname; } @JsonProperty public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @JsonProperty public List<Address> getAddresses() { return addresses; } //省略了setter public String getFullName() { return firstname+"/"+lastname; } public class JacksonTest { public static void main(String[] args) { try { Person person = new Person(); person.setFirstname("dennis"); person.setLastname("lau"); person.setPhone("123456789"); Person.Address address = person.addNewAddress(); address.setCity("shenzhen"); address.setStreet("renming road"); address.setNumber(1234); ObjectMapper objectMapper = new ObjectMapper();
//序列化之前,disable getter的自动检测,默认情况为使能的。 objectMapper.disable(MapperFeature.AUTO_DETECT_GETTERS); String json = objectMapper.writeValueAsString(person); System.out.println(json); }catch (Exception e) { e.printStackTrace(System.out); } } }
输出:{"firstname":"dennis","lastname":"lau","phone":"123456789"}
Java Object和json相互转换过程中,保留时区信息
默认情况下,Jackson对Date类型序列化为TimeStamp形式(为long类型的数字)
long now = System.currentTimeMillis(); Date date = new Date(now); ObjectMapper objectMapper = new ObjectMapper(); String s = objectMapper.writeValueAsString(date); System.out.println(now); System.out.println(s);
输出:1449391557113, 1449391557113
TimeStamps形式可读性很差,并且对于带有时区的时间,并不能保留时区信息,造成同样的时间,经过序列化,再反序列化后时间改变了。为了实现转换保留时区信息,需要自定义
JsonSerializer和JsonDeserializer:
public class CustomCalendarSerializer extends JsonSerializer<Calendar> {
//序列化时,使用自定义的SimpleDateFormat来格式化时间 private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd\'T\'HH:mm:ss.SSSZ"); @Override public void serialize(Calendar calendar, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { sdf.setCalendar(calendar);//时区信息保留在Calendar里面,Date类型是没有时区信息的哦 jsonGenerator.writeString(sdf.format(calendar.getTime())); } } public class CustomCalenderDeserializer extends JsonDeserializer<Calendar> {
//反序列化时,为了方便,使用org.joda.time.format.DateTimeFormatter,它能够根据预设的时间格式(注意这里的格式要和序列化时的一致),还原出时间,并可以得到GregorianCalendar
private static DateTimeFormatter stf = DateTimeFormat.forPattern("yyyy-MM-dd\'T\'HH:mm:ss.SSSZ");
@Override
public Calendar deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String date = jsonParser.getText();
return stf.withOffsetParsed().parseDateTime(date).toGregorianCalendar();
}
}
现在看看如何使用自定义的序列化及反序列化对象:
public class Test { Calendar calendar; String desc;
//使用Annotation来使用自定义serializer, @JsonSerialize(using = CustomCalendarSerializer.class) public Calendar getCalendar() { return calendar; }
//使用Annotation来使用自定义Deserializer @JsonDeserialize(using = CustomCalenderDeserializer.class) public void setCalendar(Calendar calendar) { this.calendar = calendar; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } public class JacksonTest { public static void main(String[] args) { try { String date = "2000-10-01T10:10:10.123"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd\'T\'HH:mm:ss.SSS"); //创建被转换的对象calendar,有时区信息 Calendar calendar = new GregorianCalendar(); calendar.setTime(sdf.parse(date)); TimeZone timeZone = TimeZone.getTimeZone("GMT-8:00"); calendar.setTimeZone(timeZone); Test test = new Test(); test.setCalendar(calendar); test.setDesc("test date with timezone to json"); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(test); System.out.println(json); Test restore = objectMapper.readValue(json, Test.class); System.out.print(restore.getCalendar().getTimeZone().getDisplayName()); }catch (Exception e) { e.printStackTrace(System.out); } } }
输出:
before: 970366210123
before: GMT-08:00
{"calendar":"2000-09-30T18:10:10.123-0800","desc":"test date with timezone to json"}
after: 970366210123
after: GMT-08:00
使用了自定义格式的serializer,json的时间格式可读性就很好了,并且转换前后时间及时区都没有改变