/* * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.gson.functional; import static org.junit.Assert.assertEquals; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import java.lang.reflect.Type; import org.junit.Test; /** * Test that the hierarchy adapter works when subtypes are used. */ public final class TypeHierarchyAdapterTest { @Test public void testTypeHierarchy() { Manager andy = new Manager(); andy.userid = "andy"; andy.startDate = 2005; andy.minions = new Employee[] { new Employee("inder", 2007), new Employee("joel", 2006), new Employee("jesse", 2006), }; CEO eric = new CEO(); eric.userid = "eric"; eric.startDate = 2001; eric.assistant = new Employee("jerome", 2006); eric.minions = new Employee[] { new Employee("larry", 1998), new Employee("sergey", 1998), andy, }; Gson gson = new GsonBuilder() .registerTypeHierarchyAdapter(Employee.class, new EmployeeAdapter()) .setPrettyPrinting() .create(); Company company = new Company(); company.ceo = eric; String json = gson.toJson(company, Company.class); assertEquals("{\n" + " \"ceo\": {\n" + " \"userid\": \"eric\",\n" + " \"startDate\": 2001,\n" + " \"minions\": [\n" + " {\n" + " \"userid\": \"larry\",\n" + " \"startDate\": 1998\n" + " },\n" + " {\n" + " \"userid\": \"sergey\",\n" + " \"startDate\": 1998\n" + " },\n" + " {\n" + " \"userid\": \"andy\",\n" + " \"startDate\": 2005,\n" + " \"minions\": [\n" + " {\n" + " \"userid\": \"inder\",\n" + " \"startDate\": 2007\n" + " },\n" + " {\n" + " \"userid\": \"joel\",\n" + " \"startDate\": 2006\n" + " },\n" + " {\n" + " \"userid\": \"jesse\",\n" + " \"startDate\": 2006\n" + " }\n" + " ]\n" + " }\n" + " ],\n" + " \"assistant\": {\n" + " \"userid\": \"jerome\",\n" + " \"startDate\": 2006\n" + " }\n" + " }\n" + "}", json); Company copied = gson.fromJson(json, Company.class); assertEquals(json, gson.toJson(copied, Company.class)); assertEquals(copied.ceo.userid, company.ceo.userid); assertEquals(copied.ceo.assistant.userid, company.ceo.assistant.userid); assertEquals(copied.ceo.minions[0].userid, company.ceo.minions[0].userid); assertEquals(copied.ceo.minions[1].userid, company.ceo.minions[1].userid); assertEquals(copied.ceo.minions[2].userid, company.ceo.minions[2].userid); assertEquals(((Manager) copied.ceo.minions[2]).minions[0].userid, ((Manager) company.ceo.minions[2]).minions[0].userid); assertEquals(((Manager) copied.ceo.minions[2]).minions[1].userid, ((Manager) company.ceo.minions[2]).minions[1].userid); } @Test public void testRegisterSuperTypeFirst() { Gson gson = new GsonBuilder() .registerTypeHierarchyAdapter(Employee.class, new EmployeeAdapter()) .registerTypeHierarchyAdapter(Manager.class, new ManagerAdapter()) .create(); Manager manager = new Manager(); manager.userid = "inder"; String json = gson.toJson(manager, Manager.class); assertEquals("\"inder\"", json); Manager copied = gson.fromJson(json, Manager.class); assertEquals(manager.userid, copied.userid); } /** This behaviour changed in Gson 2.1; it used to throw. */ @Test public void testRegisterSubTypeFirstAllowed() { new GsonBuilder() .registerTypeHierarchyAdapter(Manager.class, new ManagerAdapter()) .registerTypeHierarchyAdapter(Employee.class, new EmployeeAdapter()) .create(); } static class ManagerAdapter implements JsonSerializer, JsonDeserializer { @Override public Manager deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { Manager result = new Manager(); result.userid = json.getAsString(); return result; } @Override public JsonElement serialize(Manager src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.userid); } } static class EmployeeAdapter implements JsonSerializer, JsonDeserializer { @Override public JsonElement serialize(Employee employee, Type typeOfSrc, JsonSerializationContext context) { JsonObject result = new JsonObject(); result.add("userid", context.serialize(employee.userid, String.class)); result.add("startDate", context.serialize(employee.startDate, long.class)); if (employee instanceof Manager) { result.add("minions", context.serialize(((Manager) employee).minions, Employee[].class)); if (employee instanceof CEO) { result.add("assistant", context.serialize(((CEO) employee).assistant, Employee.class)); } } return result; } @Override public Employee deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject object = json.getAsJsonObject(); Employee result = null; // if the employee has an assistant, she must be the CEO JsonElement assistant = object.get("assistant"); if (assistant != null) { result = new CEO(); ((CEO) result).assistant = context.deserialize(assistant, Employee.class); } // only managers have minions JsonElement minons = object.get("minions"); if (minons != null) { if (result == null) { result = new Manager(); } ((Manager) result).minions = context.deserialize(minons, Employee[].class); } if (result == null) { result = new Employee(); } result.userid = context.deserialize(object.get("userid"), String.class); result.startDate = context.deserialize(object.get("startDate"), long.class); return result; } } static class Employee { String userid; long startDate; Employee(String userid, long startDate) { this.userid = userid; this.startDate = startDate; } Employee() {} } static class Manager extends Employee { Employee[] minions; } static class CEO extends Manager { Employee assistant; } static class Company { CEO ceo; } }