1 /* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.gson; 18 19 import com.google.gson.reflect.TypeToken; 20 21 /** 22 * Creates type adapters for set of related types. Type adapter factories are 23 * most useful when several types share similar structure in their JSON form. 24 * 25 * <h2>Examples</h2> 26 * <h3>Example: Converting enums to lowercase</h3> 27 * In this example, we implement a factory that creates type adapters for all 28 * enums. The type adapters will write enums in lowercase, despite the fact 29 * that they're defined in {@code CONSTANT_CASE} in the corresponding Java 30 * model: <pre> {@code 31 * 32 * public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory { 33 * public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { 34 * Class<T> rawType = (Class<T>) type.getRawType(); 35 * if (!rawType.isEnum()) { 36 * return null; 37 * } 38 * 39 * final Map<String, T> lowercaseToConstant = new HashMap<>(); 40 * for (T constant : rawType.getEnumConstants()) { 41 * lowercaseToConstant.put(toLowercase(constant), constant); 42 * } 43 * 44 * return new TypeAdapter<T>() { 45 * public void write(JsonWriter out, T value) throws IOException { 46 * if (value == null) { 47 * out.nullValue(); 48 * } else { 49 * out.value(toLowercase(value)); 50 * } 51 * } 52 * 53 * public T read(JsonReader reader) throws IOException { 54 * if (reader.peek() == JsonToken.NULL) { 55 * reader.nextNull(); 56 * return null; 57 * } else { 58 * return lowercaseToConstant.get(reader.nextString()); 59 * } 60 * } 61 * }; 62 * } 63 * 64 * private String toLowercase(Object o) { 65 * return o.toString().toLowerCase(Locale.US); 66 * } 67 * } 68 * }</pre> 69 * 70 * <p>Type adapter factories select which types they provide type adapters 71 * for. If a factory cannot support a given type, it must return null when 72 * that type is passed to {@link #create}. Factories should expect {@code 73 * create()} to be called on them for many types and should return null for 74 * most of those types. In the above example the factory returns null for 75 * calls to {@code create()} where {@code type} is not an enum. 76 * 77 * <p>A factory is typically called once per type, but the returned type 78 * adapter may be used many times. It is most efficient to do expensive work 79 * like reflection in {@code create()} so that the type adapter's {@code 80 * read()} and {@code write()} methods can be very fast. In this example the 81 * mapping from lowercase name to enum value is computed eagerly. 82 * 83 * <p>As with type adapters, factories must be <i>registered</i> with a {@link 84 * com.google.gson.GsonBuilder} for them to take effect: <pre> {@code 85 * 86 * GsonBuilder builder = new GsonBuilder(); 87 * builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory()); 88 * ... 89 * Gson gson = builder.create(); 90 * }</pre> 91 * If multiple factories support the same type, the factory registered earlier 92 * takes precedence. 93 * 94 * <h3>Example: Composing other type adapters</h3> 95 * In this example we implement a factory for Guava's {@code Multiset} 96 * collection type. The factory can be used to create type adapters for 97 * multisets of any element type: the type adapter for {@code 98 * Multiset<String>} is different from the type adapter for {@code 99 * Multiset<URL>}. 100 * 101 * <p>The type adapter <i>delegates</i> to another type adapter for the 102 * multiset elements. It figures out the element type by reflecting on the 103 * multiset's type token. A {@code Gson} is passed in to {@code create} for 104 * just this purpose: <pre> {@code 105 * 106 * public class MultisetTypeAdapterFactory implements TypeAdapterFactory { 107 * public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { 108 * Type type = typeToken.getType(); 109 * if (typeToken.getRawType() != Multiset.class 110 * || !(type instanceof ParameterizedType)) { 111 * return null; 112 * } 113 * 114 * Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; 115 * TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType)); 116 * return (TypeAdapter<T>) newMultisetAdapter(elementAdapter); 117 * } 118 * 119 * private <E> TypeAdapter<Multiset<E>> newMultisetAdapter( 120 * final TypeAdapter<E> elementAdapter) { 121 * return new TypeAdapter<Multiset<E>>() { 122 * public void write(JsonWriter out, Multiset<E> value) throws IOException { 123 * if (value == null) { 124 * out.nullValue(); 125 * return; 126 * } 127 * 128 * out.beginArray(); 129 * for (Multiset.Entry<E> entry : value.entrySet()) { 130 * out.value(entry.getCount()); 131 * elementAdapter.write(out, entry.getElement()); 132 * } 133 * out.endArray(); 134 * } 135 * 136 * public Multiset<E> read(JsonReader in) throws IOException { 137 * if (in.peek() == JsonToken.NULL) { 138 * in.nextNull(); 139 * return null; 140 * } 141 * 142 * Multiset<E> result = LinkedHashMultiset.create(); 143 * in.beginArray(); 144 * while (in.hasNext()) { 145 * int count = in.nextInt(); 146 * E element = elementAdapter.read(in); 147 * result.add(element, count); 148 * } 149 * in.endArray(); 150 * return result; 151 * } 152 * }; 153 * } 154 * } 155 * }</pre> 156 * Delegating from one type adapter to another is extremely powerful; it's 157 * the foundation of how Gson converts Java objects and collections. Whenever 158 * possible your factory should retrieve its delegate type adapter in the 159 * {@code create()} method; this ensures potentially-expensive type adapter 160 * creation happens only once. 161 * 162 * @since 2.1 163 */ 164 public interface TypeAdapterFactory { 165 166 /** 167 * Returns a type adapter for {@code type}, or null if this factory doesn't 168 * support {@code type}. 169 */ create(Gson gson, TypeToken<T> type)170 <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type); 171 } 172