Jackson是基于Java平台的一套数据处理工具,被称为“最好的Java Json解析器”。使用Json需要添加三个核心类库:
jackson-core-x.x.x.jar、jackson-annotations-x.x.x.jar和jackson-databind-bind-x.x.x.jar。
Jackson提供了三种可选的Json处理方法:流式API(Streaming API) 、树模型(Tree Model)、数据绑定(Data Binding)。从使用角度来看,比较一下这三种处理Json的方式的特性:
Streaming API:是效率最高的处理方式(开销低、读写速度快,但程序编写复杂度高)
Tree Model:是最灵活的处理方式
Data Binding:是最常用的处理方式。
下面介绍一下Jackson常用注解内容,并且例子中采用DataBinding处理Json:
- @JsonIgnore:作用在字段或者方法上,用来完全忽略被注解的字段以及方法对应的属性。不管@JsonIgnore注解在setters上还是getters上都会忽略对应的属性。示例代码如下:
//测试类<pre name="code" class="java">public class TestPOJO{ private int id; @JsonIgnore private String name; private int count; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCount() { return count; } @JsonIgnore public void setCount(int count) { this.count = count; } @Override public String toString() { return "TestPOJO [id=" + id + ", name=" + name + ", count=" + count + "]"; } }
public class jsonIgnoreTest { @Test public void jsonIgnoreTest() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setId(111); testPOJO.setName("myName"); testPOJO.setCount(22); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"id\":111}",jsonStr); System.out.println(jsonStr); String jsonStr2 = "{\"id\":111,\"name\":\"myName\",\"count\":22}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2, TestPOJO.class); System.out.println(testPOJO2); Assert.assertEquals(111,testPOJO2.getId()); Assert.assertNull(testPOJO2.getName()); Assert.assertEquals(0,testPOJO2.getCount()); } }
- @JsonProperty:作用在字段或方法上,用来对属性的序列化/反序列化,可以用来避免遗漏属性,同时提供对属性名称重命名。示例代码如下:
public class TestPOJOJsonProperty { @JsonProperty//注意这里必须得有该注解,因为没有提供对应的getId和setId函数,而是其他的getter和setter,防止遗漏该属性 private int id; @JsonProperty("first_name") private String firstName; public int wahaha() { return id; } public void wahaha(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } }<pre name="code" class="java">public class JsonPropertyTest { @Test public void jsonPropertyTest() throws Exception { TestPOJOJsonProperty testPOJO = new TestPOJOJsonProperty(); testPOJO.wahaha(111); testPOJO.setFirstName("myName"); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); System.out.println(jsonStr); Assert.assertEquals("{\"id\":111,\"first_name\":\"myName\"}",jsonStr); String jsonStr2 = "{\"id\":111,\"first_name\":\"myName\"}"; TestPOJOJsonProperty testPOJO2 = objectMapper.readValue(jsonStr2, TestPOJOJsonProperty.class); Assert.assertEquals(111, testPOJO2.wahaha()); Assert.assertEquals("myName", testPOJO2.getFirstName()); } }
- @JsonIgnoreProperties:作用在类上,用来说明有些属性在序列化/反序列化时需要忽略掉,可以将它看做是@JsonIgnore的批量操作,但它的功能比@JsonIgnore要强,比如一个类是代理类,我们无法将将@JsonIgnore标记在属性或方法上,此时便可用@JsonIgnoreProperties标注在类声明上,
它还有一个重要的功能是作用在反序列化时解析字段时过滤一些未知的属性,
否则通常情况下解析到我们定义的类不认识的属性便会抛出异常。
可以注明是想要忽略的属性列表如@JsonIgnoreProperties({"name","age","title"}),
也可以注明过滤掉未知的属性如@JsonIgnoreProperties(ignoreUnknown=true)。示例代码如下:@JsonIgnoreProperties({"name","age","title"}) public class TestPOJOJsonIgnoreProperties { private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } //测试类 public class JsonIgnorePropertiesTest { @Test() public void JsonIgnoreProperties() throws Exception { TestPOJOJsonIgnoreProperties testPOJO = new TestPOJOJsonIgnoreProperties(); testPOJO.setId(111); testPOJO.setName("myName"); testPOJO.setAge(22); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); Assert.assertEquals("{\"id\":111}",jsonStr);//name和age被忽略掉了 String jsonStr2 = "{\"id\":111,\"name\":\"myName\",\"age\":22,\"title\":\"myTitle\"}"; TestPOJOJsonIgnoreProperties testPOJO2 = objectMapper.readValue(jsonStr2, TestPOJOJsonIgnoreProperties.class); Assert.assertEquals(111, testPOJO2.getId()); Assert.assertNull(testPOJO2.getName()); Assert.assertEquals(0,testPOJO2.getAge()); String jsonStr3 = "{\"id\":111,\"name\":\"myName\",\"count\":33}";//这里有个未知的count属性,反序列化会报错 objectMapper.readValue(jsonStr3, TestPOJOJsonIgnoreProperties.class); } }
- @JsonUnwrapped作用在属性或方法上,用来将子Json对象的属性添加到封闭的Json对象,来个例子一目了然。
@Test public void jsonUnwrapped() throws Exception { TestPOJO testPOJO = new TestPOJO(); testPOJO.setId(111); TestName testName = new TestName(); testName.setFirstName("张"); testName.setSecondName("三"); testPOJO.setName(testName); ObjectMapper objectMapper = new ObjectMapper(); String jsonStr = objectMapper.writeValueAsString(testPOJO); //如果没有@JsonUnwrapped,序列化后将为{"id":111,"name":{"firstName":"张","secondName":"三"}} //因为在name属性上加了@JsonUnwrapped,所以name的子属性firstName和secondName将不会包含在name中。 Assert.assertEquals("{\"id\":111,\"firstName\":\"张\",\"secondName\":\"三\"}",jsonStr); String jsonStr2 = "{\"id\":111,\"firstName\":\"张\",\"secondName\":\"三\"}"; TestPOJO testPOJO2 = objectMapper.readValue(jsonStr2,TestPOJO.class); Assert.assertEquals(111,testPOJO2.getId()); Assert.assertEquals("张",testPOJO2.getName().getFirstName()); Assert.assertEquals("三",testPOJO2.getName().getSecondName()); } public static class TestPOJO{ private int id; @JsonUnwrapped private TestName name; //getters、setters省略 } public static class TestName{ private String firstName; private String secondName; //getters、setters省略 }
- @JsonIdentityInfo作用于类或者属性上,被用来在序列化/反序列化时为该对象或字段添加一个对象识别码,通常用来解决循环嵌套问题。对象识别码可以是虚拟的,即
存在在JSON中,但不是POJO的一部分,这种情况下我们可以这样使用注解:(没有该注释的话,对象转Json会因为循环嵌套报错)
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class,property = "@id")
对象识别码也可以是真实存在的,即以对象的属性为识别码,通常这种情况下我们一般以id属性为识别码,使用如下:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
举例如下:
@Test
public void jsonIdentityInfo() throws Exception {
Parent parent = new Parent();
parent.setName("jack");
Child child = new Child();
child.setName("mike");
Child[] children = new Child[]{child};
parent.setChildren(children);
child.setParent(parent);
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(parent);
Assert.assertEquals("{\"@id\":1,\"name\":\"jack\",\"children\":[{\"name\":\"mike\",\"parent\":1}]}",jsonStr);
}
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class,property = "@id")
public static class Parent{
private String name;
private Child[] children;
//getters、setters省略
}
public static class Child{
private String name;
private Parent parent;
//getters、setters省略
}
6.@JsonNaming作用:它可以让你定制属性命名策略,作用和前面提到的@JsonProperty的重命名属性名称相同。
比如你有一个JSON串{"in_reply_to_user_id":"abc123"},需要反序列化为POJO,POJO一般情况下则需要如此写
但这显然不符合JAVA的编码规范,你可以用@JsonProperty,比如
这样就符合规范了,可是如果POJO里有很多属性,给每个属性都要加上@JsonProperty是多么繁重的工作,这里就需要用到@JsonNaming了,
public class TestPOJO{
private String in_reply_to_user_id;
public String getIn_reply_to_user_id() {
return in_reply_to_user_id;
}
public void setIn_reply_to_user_id(String in_reply_to_user_id) {
this.in_reply_to_user_id = in_reply_to_user_id;
}
}
但这显然不符合JAVA的编码规范,你可以用@JsonProperty,比如
public class TestPOJO{
@JsonProperty("in_reply_to_user_id")
private String inReplyToUserId;
public String getInReplyToUserId() {
return inReplyToUserId;
}
public void setInReplyToUserId(String inReplyToUserId) {
this.inReplyToUserId = inReplyToUserId;
}
}
这样就符合规范了,可是如果POJO里有很多属性,给每个属性都要加上@JsonProperty是多么繁重的工作,这里就需要用到@JsonNaming了,
它不仅能制定统一的命名规则,还能任意按自己想要的方式定制
@JsonNaming使用了jackson已经实现的PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy,它可以将大写转换为小写并添加下划线。
@Test
public void jsonNaming() throws Exception{
String jsonStr = "{\"in_reply_to_user_id\":\"abc123\"}";
ObjectMapper objectMapper = new ObjectMapper();
TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
Assert.assertEquals("abc123",testPOJO.getInReplyToUserId());
TestPOJO testPOJO2 = new TestPOJO();
testPOJO2.setInReplyToUserId("abc123");
String jsonStr2 = objectMapper.writeValueAsString(testPOJO2);
Assert.assertEquals(jsonStr,jsonStr2);
}
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public static class TestPOJO{
private String inReplyToUserId;
public String getInReplyToUserId() {
return inReplyToUserId;
}
public void setInReplyToUserId(String inReplyToUserId) {
this.inReplyToUserId = inReplyToUserId;
}
}
@JsonNaming使用了jackson已经实现的PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy,它可以将大写转换为小写并添加下划线。
你可以自定义,必须继承类PropertyNamingStrategy,建议继承PropertyNamingStrategyBase,
我们自己实现一个类似LowerCaseWithUnderscoresStrategy的策略,只是将下划线改为破折号
@Test
public void jsonNaming() throws Exception{
String jsonStr = "{\"in-reply-to-user-id\":\"abc123\"}";
ObjectMapper objectMapper = new ObjectMapper();
TestPOJO testPOJO = objectMapper.readValue(jsonStr,TestPOJO.class);
Assert.assertEquals("abc123", testPOJO.getInReplyToUserId());
TestPOJO testPOJO2 = new TestPOJO();
testPOJO2.setInReplyToUserId("abc123");
String jsonStr2 = objectMapper.writeValueAsString(testPOJO2);
Assert.assertEquals(jsonStr, jsonStr2);
}
@JsonNaming(MyPropertyNamingStrategy.class)
public static class TestPOJO{
private String inReplyToUserId;
public String getInReplyToUserId() {
return inReplyToUserId;
}
public void setInReplyToUserId(String inReplyToUserId) {
this.inReplyToUserId = inReplyToUserId;
}
}
public static class MyPropertyNamingStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase {
@Override
public String translate(String input) {
if (input == null) return input; // garbage in, garbage out
int length = input.length();
StringBuilder result = new StringBuilder(length * 2);
int resultLength = 0;
boolean wasPrevTranslated = false;
for (int i = 0; i < length; i++)
{
char c = input.charAt(i);
if (i > 0 || c != '-') // skip first starting underscore
{
if (Character.isUpperCase(c))
{
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '-')
{
result.append('-');
resultLength++;
}
c = Character.toLowerCase(c);
wasPrevTranslated = true;
}
else
{
wasPrevTranslated = false;
}
result.append(c);
resultLength++;
}
}
return resultLength > 0 ? result.toString() : input;
}
}
如果你想让自己定制的策略对所有解析都实现,除了对每个具体的实体类对应的位置加上@JsonNaming外你还可以如下做全局配置
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(new MyPropertyNamingStrategy());