Jackson学习笔记(三)

概述

使用jackson annotations简化和增强的json解析与生成。

Jackson-2.x通用annotations列表:https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations

Jackson-1.x通用annotations列表:http://wiki.fasterxml.com/JacksonAnnotations


准备工作

基于JDK1.7,依赖Jackson框架核心类库:

jackson-core-2.5.3.jar
jackson-annotations-2.5.3.jar
jackson-databind-2.5.3.jar


Jackson - Annotations

想要了解更多内容,请查看annotations列表。下面只列出一些常用的Json注解。

@JsonProperty

它关联json字段到java属性。可以标记属性,也可以用来标记属性的getter/setter方法。当标记属性时,可以对属性字段重命名。当标记方法时,可以把json字段关联到java属性的getter或setter方法。

@JsonCreator

json反序列化为java对象时,该注解用于定义构造函数。当从json创建java时,@JsonCreator注解的构造函数被会调用,如果没有@JsonCreator注解,则默认调用java类的无参构造函数,此时,如果java类中只有有参构造函数,而无默认的无参构造函数,在反序列化时会抛出这样的异常:com.fasterxml.jackson.databind.JsonMappingException,所以,当我们不使用@JsonCreator指定反序列化的构造函数,而又在java类中重载了构造函数时,一定要记得编写类的无参构造函数。

@JsonAnyGetter@JsonAnySetter

用于标记类方法,设置和读取json字段作为键值对存储到map中,这两个注解标记的方法不会处理任何java类中已经定义过的属性变量,只对java中未定义的json字段作处理。

@JsonIgnoreProperties@JsonIgnore

用于标记属性,在json与java之间相互转化时,将忽略被此注解标记的属性。@JsonIgnoreProperties是类级别注解,可以忽略多个属性,@JsonIgnore用来标注单个属性。

@JsonTypeInfo@JsonSubTypes

用于维持java类的子类信息,将子类对象类型信息嵌入到json中,以便反序列化创建具体的对象。


Example

下面通过例子来演示注解的使用

example.1

读取company.json,反序列化json,创建java对象,并遍历信息

company.json

{
  "name" : "Oracle",
  "HQ" : "California",
  "birthDate" : "1977-01-01",
  "departments" : [ {
    "name" : "development",
    "employee_number" : 5000,
    "projectManager" : "jack",
    "product" : "oracle_db"
  }, {
    "name" : "test",
    "employee_number" : 500,
    "projectManager" : "rose",
    "product" : "oracle_test"
  } ]
}
Company.java

package com.jackson.json.annotation;

import java.util.Date;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Company {
	private String name;
	@JsonProperty("HQ")   //java属性headquarters序列化到json字段的名称为HQ
	private String headquarters;
	private Department[] departments;
	@JsonIgnore     	//在序列化与反序列化时,忽略birthDate属性
	private Date birthDate;

	public Date getBirthDate() {
		return birthDate;
	}

	@JsonCreator
	public Company(@JsonProperty("name") String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public String getHeadquarters() {
		return headquarters;
	}

	public Department[] getDepartments() {
		return departments;
	}

	public void setDepartments(Department[] departments) {
		this.departments = departments;
	}

}

Department.java

package com.jackson.json.annotation;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Department {
	private String name;
	private String pm;
	private Map<String, Object> otherProperties = new HashMap<String, Object>(); //otherProperties用来存放Department中未定义的json字段
	
	@JsonCreator   //指定json反序列化创建Department对象时调用此构造函数
	public Department(@JsonProperty("name") String name){
		this.name = name;
	}
	
	@JsonProperty("projectManager")  //将company.json中projectManager字段关联到getPm方法
	public String getPm() {
		return pm;
	}

	public String getName() {
		return name;
	}

	public Object get(String key) {
		return otherProperties.get(key);
	}

	@JsonAnyGetter    //得到所有Department中未定义的json字段的
	public Map<String, Object> any() {
		return otherProperties;
	}

	@JsonAnySetter
	public void set(String key, Object value) {
		otherProperties.put(key, value);
	}

}

DeserializationExample.java

package com.jackson.json.annotation;

import java.io.File;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

public class DeserializationExample {
	public static void main(String[] args) throws Exception {
		ObjectMapper mapper = new ObjectMapper();
		mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); //禁止未知属性打断反序列化
		
		Company company = mapper.readValue(new File("company_back.json"), Company.class);
		System.out.print("company_name:"+company.getName()+"\t");
		System.out.print("headquarters:"+company.getHeadquarters()+"\t");
		System.out.println("birthDate:"+company.getBirthDate()); //birthDate被标记为@JsonIgnore,所以此处得到的值应该为null
		
		Department[] departments = company.getDepartments();
		
		for (Department department : departments) {
			System.out.print("department_name:" + department.getName()+"\t");
			System.out.print("employee_number:" + department.getPm()+"\t");
			//Department中未定义的字段product,employee_number
			System.out.print("product:"+department.get("product")+"\t"); 
			System.out.println("projectManager:"+department.get("employee_number"));
		}
	}

}

程序运行控制台打印结果如下:

company_name:Oracle	headquarters:California	birthDate:null
department_name:development	employee_number:jack	product:oracle_db	projectManager:5000
department_name:test	employee_number:rose	product:oracle_test	projectManager:500

example.2

下面例子演示,当java对象中包含List<Object>属性时,如何序列化与反序列化。

当java对象中含List<Object>时,如果Object一个抽象类或接口,这里就会出现java多态的现象,比如,List<Animal>,Animal是个抽象类,并且有多个子类时,由于List中保存的Animal没有明确指向具体的子类或实现类,json反序列化java对象时就会抛出提示:Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException:Can not construct instance of Animal, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information

@JsonTypeInfo与@JsonSubTypes就是解决此类问题,通过注解,可以在序列化时,保存具体的类型信息到json中,当json反序列到java对象时,就可以根据具体类型信息创建正确的java对象。

Zoo.java

package com.jackson.json.databinding.list;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Zoo {
	public String name;
	public String city;
	public List<Animal> animals;
	
	@JsonCreator
    public Zoo(@JsonProperty("name") String name, @JsonProperty("city") String city) {
        this.name = name;
        this.city = city;
    }
	
	public void setAnimals(List<Animal> animals) {
		this.animals = animals;
	}

	@Override
	public String toString() {
		return "Zoo [name=" + name + ", city=" + city + ", animals=" + animals
				+ "]";
	}
	
}
Animal.java

package com.jackson.json.databinding.list;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;

@JsonTypeInfo(use=Id.CLASS,include=As.PROPERTY,property="@class")
@JsonSubTypes({@Type(value=Lion.class,name="lion"),@Type(value=Elephant.class,name="elephant")})
public abstract class Animal {
	String name;
	String type;
}
Lion.java

package com.jackson.json.databinding.list;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Lion extends Animal {
	
	private String name;
	
	@JsonCreator
    public Lion(@JsonProperty("name") String name) {
        this.name = name;
    }

	public String getName() {
		return name;
	}
	
	public String getType() {
		return "carnivorous";
	}

	@Override
	public String toString() {
		return "Lion [name=" + name + ", getName()=" + getName()
				+ ", getType()=" + getType() + "]";
	}
	
}
Elephant.java

package com.jackson.json.databinding.list;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Elephant extends Animal {
	private String name;

	@JsonCreator
    public Elephant(@JsonProperty("name") String name) {
        this.name = name;
    }
	
	public String getName() {
		return name;
	}
	
	public String getType() {
		return "herbivorous";
	}

	@Override
	public String toString() {
		return "Elephant [getName()=" + getName() + ", getType()=" + getType()
				+ "]";
	}
	
}

SerializeExmaple.java

package com.jackson.json.databinding.list;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class SerializeExample {
	public static void main(String[] args) throws Exception {
		Zoo zoo = new Zoo("SH Wild Park", "ShangHai");
		Lion lion = new Lion("Samba");
		Elephant elephant = new Elephant("Manny");
		List<Animal> animals = new ArrayList<Animal>();
		animals.add(lion);
		animals.add(elephant);
		zoo.setAnimals(animals);
		
		ObjectMapper mapper = new ObjectMapper();
		mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
		mapper.writeValue(new File("zoo.json"), zoo);
	}

}
生成zoo.json内容如下:

{
  "name" : "SH Wild Park",
  "city" : "ShangHai",
  "animals" : [ {
    "@class" : "com.jackson.json.databinding.list.Lion",
    "name" : "Samba",
    "type" : "carnivorous"
  }, {
    "@class" : "com.jackson.json.databinding.list.Elephant",
    "name" : "Manny",
    "type" : "herbivorous"
  } ]
}

反序列化,DeserializeExmaple.java

package com.jackson.json.databinding.list;

import java.io.File;

import com.fasterxml.jackson.databind.ObjectMapper;

public class DeserializeExample {

	public static void main(String[] args) throws Exception {
		ObjectMapper mapper = new ObjectMapper();
		Zoo zoo = mapper.readValue(new File("zoo.json"), Zoo.class);
		System.out.println(zoo);
	}
}

这里对反序列化后的Zoo对象不作详细遍历了,只打印toString看一下,结果如下,正确创建了Lion和Elephant对象
Zoo [name=SH Wild Park, city=ShangHai, animals=[Lion [name=Samba, getName()=Samba, getType()=carnivorous], Elephant [getName()=Manny, getType()=herbivorous]]]


我们还可以用另一个种方法来替代JsonSubTypes注释,现在,我们对Animal.java类稍作修改,隐去@JsonSubTypes注解,保留@JsonInfoType注解.这一次,我们直接序列化List<Animal>

SerializeExample2.java

package com.jackson.json.databinding.list;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class SerializeExample {
	public static void main(String[] args) throws Exception {
		Zoo zoo = new Zoo("SH Wild Park", "ShangHai");
		Lion lion = new Lion("Samba");
		Elephant elephant = new Elephant("Manny");
		List<Animal> animals = new ArrayList<Animal>();
		animals.add(lion);
		animals.add(elephant);
		zoo.setAnimals(animals);
		
		ObjectMapper mapper = new ObjectMapper();
		mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
		mapper.writerFor(new TypeReference<List<Animal>>() {
		}).writeValue(new File("animal.json"), animals);
		
	}

}
生成 animal.json,内容如下:

[ {
  "@class" : "com.jackson.json.databinding.list.Lion",
  "name" : "Samba",
  "type" : "carnivorous"
}, {
  "@class" : "com.jackson.json.databinding.list.Elephant",
  "name" : "Manny",
  "type" : "herbivorous"
} ]

总结

当采用数据绑定(DataBinding)方式处理json时,适当的使用Jackson Annotations可以帮助我们更好的解决问题,特别体现在序列化List时,解决多态的问题,以保证反序化到java对象时的正确性。@JsonAnyGetter,@JsonAnySetter,@JsonProperty都是很常用的注解,可以帮助我们更简洁的处理java对象与json之间的相互转化。

  • 12
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值