Gson 解析JSON示例

时间:2020-02-23 14:41:23  来源:igfitidea点击:

在上一篇文章中,我们研究了Java JSON API,您可以很容易地看出使用它并不方便。
无论您是否必须将JSON转换为Java Object,都需要编写大量与JSON结构紧密结合的代码。

Gson

我开始寻找其他可以自行完成转换的JSON解析器API,并找到了有关Google Gson的信息。
Gson是开放源代码,在处理JSON和Java方面非常常用。
Gson使用Java Reflection提供简单的方法来将JSON转换为Java,反之亦然。

Gson Maven

您可以从Google代码下载Gson jar文件,或者,如果您使用的是maven,则只需添加其依赖项,如下所示。

<dependencies>
  <!--  Gson dependency -->
  <dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.2.4</version>
  </dependency>
</dependencies>

Gson是非常强大的API,它支持Java泛型。
如果对象字段名称与json中的相同,则Gson API还支持将JSON转换为Java Object。
如果要为Java bean和json使用不同的名称,则可以使用@SerializedName java批注并在JSON和Java类中映射变量。

Gson例子

让我们看一个复杂的Gson示例,该示例涉及JSON中的嵌套对象和数组,并将其映射到类型为List,Map,Array等的Java bean属性。

employee.txt

{
"empID": 100,
"name": "David",
"permanent": false,
"address": {
  "street": "BTM 1st Stage",
  "city": "Bangalore",
  "zipcode": 560100
},
"phoneNumbers": [
  123456,
  987654
],
"role": "Manager",
"cities": [
  "Los Angeles",
  "New York"
],
"properties": {
  "age": "28 years",
  "salary": "1000 Rs"
}
}

让我们创建java bean类,将JSON转换为Java Object。

Employee.java

package com.theitroad.json.model;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import com.google.gson.annotations.SerializedName;

public class Employee {

	@SerializedName("empID")
	private int id;
	private String name;
	private boolean permanent;
	private Address address;
	private long[] phoneNumbers;
	private String role;
	private List<String> cities;
	private Map<String, String> properties;
	
	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 boolean isPermanent() {
		return permanent;
	}
	public void setPermanent(boolean permanent) {
		this.permanent = permanent;
	}
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	public long[] getPhoneNumbers() {
		return phoneNumbers;
	}
	public void setPhoneNumbers(long[] phoneNumbers) {
		this.phoneNumbers = phoneNumbers;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	
	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append("* Employee Details *\n");
		sb.append("ID="+getId()+"\n");
		sb.append("Name="+getName()+"\n");
		sb.append("Permanent="+isPermanent()+"\n");
		sb.append("Role="+getRole()+"\n");
		sb.append("Phone Numbers="+Arrays.toString(getPhoneNumbers())+"\n");
		sb.append("Address="+getAddress()+"\n");
		sb.append("Cities="+Arrays.toString(getCities().toArray())+"\n");
		sb.append("Properties="+getProperties()+"\n");
		sb.append("*");
		
		return sb.toString();
	}
	public List<String> getCities() {
		return cities;
	}
	public void setCities(List<String> cities) {
		this.cities = cities;
	}
	public Map<String, String> getProperties() {
		return properties;
	}
	public void setProperties(Map<String, String> properties) {
		this.properties = properties;
	}
}

Address.java

package com.theitroad.json.model;

public class Address {
	
	private String street;
	private String city;
	private int zipcode;
	
	public String getStreet() {
		return street;
	}
	public void setStreet(String street) {
		this.street = street;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public int getZipcode() {
		return zipcode;
	}
	public void setZipcode(int zipcode) {
		this.zipcode = zipcode;
	}
	
	@Override
	public String toString(){
		return getStreet() + ", "+getCity()+", "+getZipcode();
	}
}

这是显示Gson示例以解析JSON的Java程序。

EmployeeGsonExample.java

package com.theitroad.json.gson;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.theitroad.json.model.Address;
import com.theitroad.json.model.Employee;

public class EmployeeGsonExample {

	public static void main(String[] args) throws IOException {
		Employee emp = createEmployee();

		//Get Gson object
		Gson gson = new GsonBuilder().setPrettyPrinting().create();

		//read JSON file data as String
		String fileData = new String(Files.readAllBytes(Paths
				.get("employee.txt")));

		//parse json string to object
		Employee emp1 = gson.fromJson(fileData, Employee.class);

		//print object data
		System.out.println("\n\nEmployee Object\n\n" + emp1);

		//create JSON String from Object
		String jsonEmp = gson.toJson(emp);
		System.out.print(jsonEmp);

	}

	public static Employee createEmployee() {

		Employee emp = new Employee();
		emp.setId(100);
		emp.setName("David");
		emp.setPermanent(false);
		emp.setPhoneNumbers(new long[] { 123456, 987654 });
		emp.setRole("Manager");

		Address add = new Address();
		add.setCity("Bangalore");
		add.setStreet("BTM 1st Stage");
		add.setZipcode(560100);
		emp.setAddress(add);

		List<String> cities = new ArrayList<String>();
		cities.add("Los Angeles");
		cities.add("New York");
		emp.setCities(cities);

		Map<String, String> props = new HashMap<String, String>();
		props.put("salary", "1000 Rs");
		props.put("age", "28 years");
		emp.setProperties(props);

		return emp;
	}
}

Gson是公开用于转换的方法fromfromJson()和toJson()的主要类。
对于默认实现,我们可以直接创建此对象,也可以使用GsonBuilder类提供转换的有用选项,例如漂亮的打印,字段命名约定,排除字段,日期格式等。

当您在gson示例程序上方运行时,我们将为java对象获得以下输出。

Employee Object

* Employee Details *
ID=100
Name=David
Permanent=false
Role=Manager
Phone Numbers=[123456, 987654]
Address=BTM 1st Stage, Bangalore, 560100
Cities=[Los Angeles, New York]
Properties={age=28 years, salary=1000 Rs}
*

您会看到使用Gson多么容易,这就是为什么Gson是用于JSON解析的非常流行的Java API的原因。

上面的JSON解析的Gson示例被称为对象模型,因为整个JSON会立即转换为对象。
在大多数情况下,这对我们来说已经足够了,但是如果JSON确实非常庞大并且我们不想一次将所有内容都存储在内存中,那么Gson也会提供Streaming API。

Gson示例使用流API解析JSON

让我们看一下Gson示例,在该示例中,我们将使用Streaming API将json转换为java对象。

EmployeeGsonReader.java

package com.theitroad.json.gson;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.theitroad.json.model.Address;
import com.theitroad.json.model.Employee;

public class EmployeeGsonReader {

	public static void main(String[] args) throws IOException {
		InputStream is = new FileInputStream("employee.txt");
		InputStreamReader isr = new InputStreamReader(is);
		
		//create JsonReader object
		JsonReader reader = new JsonReader(isr);
		
		//create objects
		Employee emp = new Employee();
		Address add = new Address();
		emp.setAddress(add);
		List<Long> phoneNums = new ArrayList<Long>();
		emp.setCities(new ArrayList<String>());
		emp.setProperties(new HashMap<String, String>());
		String key = null;
		boolean insidePropertiesObj=false;
		
		key = parseJSON(reader, emp, phoneNums, key, insidePropertiesObj);
		
		
		long[] nums = new long[phoneNums.size()];
		int index = 0;
		for(Long l :phoneNums){
			nums[index++] = l;
		}
		emp.setPhoneNumbers(nums);
		
		reader.close();
		//print employee object
		System.out.println("Employee Object\n\n"+emp);
	}
	
	

	private static String parseJSON(JsonReader reader, Employee emp,
			List<Long> phoneNums, String key, boolean insidePropertiesObj) throws IOException {
		
		//loop to read all tokens
				while(reader.hasNext()){
					//get next token
					JsonToken token = reader.peek();
					
					switch(token){
					case BEGIN_OBJECT:
						reader.beginObject();
						if("address".equals(key) || "properties".equals(key)){
							while(reader.hasNext()){
							parseJSON(reader, emp,phoneNums, key, insidePropertiesObj);
							}
							reader.endObject();
						}
						break;
					case END_OBJECT:
						reader.endObject();
						if(insidePropertiesObj) insidePropertiesObj=false;
						break;
					case BEGIN_ARRAY:
						reader.beginArray();
						if("phoneNumbers".equals(key) || "cities".equals(key)){
							while(reader.hasNext()){
								parseJSON(reader, emp,phoneNums, key, insidePropertiesObj);
								}
							reader.endArray();
						}
						break;
					case END_ARRAY:
						reader.endArray();
						break;
					case NAME:
						key = reader.nextName();
						if("properties".equals(key)) insidePropertiesObj=true;
						break;
					case BOOLEAN:
						if("permanent".equals(key)) emp.setPermanent(reader.nextBoolean());
						else{
							System.out.println("Unknown item found with key="+key);
							//skip value to ignore it
							reader.skipValue();
						}
						break;
					case NUMBER:
						if("empID".equals(key)) emp.setId(reader.nextInt());
						else if("phoneNumbers".equals(key)) phoneNums.add(reader.nextLong());
						else if("zipcode".equals(key)) emp.getAddress().setZipcode(reader.nextInt());
						else {
							System.out.println("Unknown item found with key="+key);
							//skip value to ignore it
							reader.skipValue();
						}
						break;
					case STRING:
						setStringValues(emp, key, reader.nextString(), insidePropertiesObj);
						break;
					case NULL:
						System.out.println("Null value for key"+key);
						reader.nextNull();
						break;
					case END_DOCUMENT:
						System.out.println("End of Document Reached");
						break;
					default:
						System.out.println("This part will never execute");
						break;
						
					}
				}
				return key;
	}

	private static void setStringValues(Employee emp, String key,
			String value, boolean insidePropertiesObj) {
		if("name".equals(key)) emp.setName(value);
		else if("role".equals(key)) emp.setRole(value);
		else if("cities".equals(key)) emp.getCities().add(value);
		else if ("street".equals(key)) emp.getAddress().setStreet(value);
		else if("city".equals(key)) emp.getAddress().setCity(value);
		else{
			//add to emp properties map
			if(insidePropertiesObj){
				emp.getProperties().put(key, value);
			}else{
				System.out.println("Unknown data found with key="+key+" value="+value);
			}
			
		}
	}

}

由于JSON是一种递归语言,因此我们需要为数组和嵌套对象递归调用解析方法。
JsonToken是JsonReadernext()方法返回的Java枚举,我们可以将其与条件逻辑一起使用或者切换case语句进行转换。

从上面的代码中,您可以了解到这不是一个简单的实现,并且如果JSON确实很复杂,那么此代码将很难维护。
除非没有办法使用基于对象的模型,否则请避免使用它。

将对象写入文件的Gson示例

让我们看看如何使用Gson Streaming API编写Employee对象。

EmployeeGsonWriter.java

package com.theitroad.json.gson;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Set;

import com.google.gson.stream.JsonWriter;
import com.theitroad.json.model.Employee;

public class EmployeeGsonWriter {

	public static void main(String[] args) throws IOException {
		Employee emp = EmployeeGsonExample.createEmployee();
		
		//writing on console, we can initialize with FileOutputStream to write to file
		OutputStreamWriter out = new OutputStreamWriter(System.out);
		JsonWriter writer = new JsonWriter(out);
		//set indentation for pretty print
		writer.setIndent("\t");
		//start writing
		writer.beginObject(); //{
		writer.name("id").value(emp.getId()); //"id": 123
		writer.name("name").value(emp.getName()); //"name": "David"
		writer.name("permanent").value(emp.isPermanent()); //"permanent": false
		writer.name("address").beginObject(); //"address": {
			writer.name("street").value(emp.getAddress().getStreet()); //"street": "BTM 1st Stage"
			writer.name("city").value(emp.getAddress().getCity()); //"city": "Bangalore"
			writer.name("zipcode").value(emp.getAddress().getZipcode()); //"zipcode": 560100
			writer.endObject(); //}
		writer.name("phoneNumbers").beginArray(); //"phoneNumbers": [
			for(long num : emp.getPhoneNumbers()) writer.value(num); //123456,987654
			writer.endArray(); //]
		writer.name("role").value(emp.getRole()); //"role": "Manager"
		writer.name("cities").beginArray(); //"cities": [
			for(String c : emp.getCities()) writer.value(c); //"Los Angeles","New York"
			writer.endArray(); //]
		writer.name("properties").beginObject(); //"properties": {
			Set<String> keySet = emp.getProperties().keySet();
			for(String key : keySet) writer.name("key").value(emp.getProperties().get(key));//"age": "28 years","salary": "1000 Rs"
			writer.endObject(); //}
		writer.endObject(); //}
		
		writer.flush();
		
		//close writer
		writer.close();
		
	}

}

与使用Gson流API进行解析相比,将Java对象转换为JSON相对容易。
默认情况下,JsonWriter以紧凑形式编写json,但我们可以设置缩进以进行漂亮的打印。