]>
Commit | Line | Data |
---|---|---|
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 | * <h3>Example: Converting enums to lowercase</h3> | |
26 | * In this example, we implement a factory that creates type adapters for all | |
27 | * enums. The type adapters will write enums in lowercase, despite the fact | |
28 | * that they're defined in {@code CONSTANT_CASE} in the corresponding Java | |
29 | * model: <pre> {@code | |
30 | * | |
31 | * public class LowercaseEnumTypeAdapterFactory implements TypeAdapter.Factory { | |
32 | * public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { | |
33 | * Class<T> rawType = (Class<T>) type.getRawType(); | |
34 | * if (!rawType.isEnum()) { | |
35 | * return null; | |
36 | * } | |
37 | * | |
38 | * final Map<String, T> lowercaseToConstant = new HashMap<String, T>(); | |
39 | * for (T constant : rawType.getEnumConstants()) { | |
40 | * lowercaseToConstant.put(toLowercase(constant), constant); | |
41 | * } | |
42 | * | |
43 | * return new TypeAdapter<T>() { | |
44 | * public void write(JsonWriter out, T value) throws IOException { | |
45 | * if (value == null) { | |
46 | * out.nullValue(); | |
47 | * } else { | |
48 | * out.value(toLowercase(value)); | |
49 | * } | |
50 | * } | |
51 | * | |
52 | * public T read(JsonReader reader) throws IOException { | |
53 | * if (reader.peek() == JsonToken.NULL) { | |
54 | * reader.nextNull(); | |
55 | * return null; | |
56 | * } else { | |
57 | * return lowercaseToConstant.get(reader.nextString()); | |
58 | * } | |
59 | * } | |
60 | * }; | |
61 | * } | |
62 | * | |
63 | * private String toLowercase(Object o) { | |
64 | * return o.toString().toLowerCase(Locale.US); | |
65 | * } | |
66 | * } | |
67 | * }</pre> | |
68 | * | |
69 | * <p>Type adapter factories select which types they provide type adapters | |
70 | * for. If a factory cannot support a given type, it must return null when | |
71 | * that type is passed to {@link #create}. Factories should expect {@code | |
72 | * create()} to be called on them for many types and should return null for | |
73 | * most of those types. In the above example the factory returns null for | |
74 | * calls to {@code create()} where {@code type} is not an enum. | |
75 | * | |
76 | * <p>A factory is typically called once per type, but the returned type | |
77 | * adapter may be used many times. It is most efficient to do expensive work | |
78 | * like reflection in {@code create()} so that the type adapter's {@code | |
79 | * read()} and {@code write()} methods can be very fast. In this example the | |
80 | * mapping from lowercase name to enum value is computed eagerly. | |
81 | * | |
82 | * <p>As with type adapters, factories must be <i>registered</i> with a {@link | |
83 | * com.google.gson.GsonBuilder} for them to take effect: <pre> {@code | |
84 | * | |
85 | * GsonBuilder builder = new GsonBuilder(); | |
86 | * builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory()); | |
87 | * ... | |
88 | * Gson gson = builder.create(); | |
89 | * }</pre> | |
90 | * If multiple factories support the same type, the factory registered earlier | |
91 | * takes precedence. | |
92 | * | |
93 | * <h3>Example: composing other type adapters</h3> | |
94 | * In this example we implement a factory for Guava's {@code Multiset} | |
95 | * collection type. The factory can be used to create type adapters for | |
96 | * multisets of any element type: the type adapter for {@code | |
97 | * Multiset<String>} is different from the type adapter for {@code | |
98 | * Multiset<URL>}. | |
99 | * | |
100 | * <p>The type adapter <i>delegates</i> to another type adapter for the | |
101 | * multiset elements. It figures out the element type by reflecting on the | |
102 | * multiset's type token. A {@code Gson} is passed in to {@code create} for | |
103 | * just this purpose: <pre> {@code | |
104 | * | |
105 | * public class MultisetTypeAdapterFactory implements TypeAdapter.Factory { | |
106 | * public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
107 | * Type type = typeToken.getType(); | |
108 | * if (typeToken.getRawType() != Multiset.class | |
109 | * || !(type instanceof ParameterizedType)) { | |
110 | * return null; | |
111 | * } | |
112 | * | |
113 | * Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; | |
114 | * TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType)); | |
115 | * return (TypeAdapter<T>) newMultisetAdapter(elementAdapter); | |
116 | * } | |
117 | * | |
118 | * private <E> TypeAdapter<Multiset<E>> newMultisetAdapter( | |
119 | * final TypeAdapter<E> elementAdapter) { | |
120 | * return new TypeAdapter<Multiset<E>>() { | |
121 | * public void write(JsonWriter out, Multiset<E> value) throws IOException { | |
122 | * if (value == null) { | |
123 | * out.nullValue(); | |
124 | * return; | |
125 | * } | |
126 | * | |
127 | * out.beginArray(); | |
128 | * for (Multiset.Entry<E> entry : value.entrySet()) { | |
129 | * out.value(entry.getCount()); | |
130 | * elementAdapter.write(out, entry.getElement()); | |
131 | * } | |
132 | * out.endArray(); | |
133 | * } | |
134 | * | |
135 | * public Multiset<E> read(JsonReader in) throws IOException { | |
136 | * if (in.peek() == JsonToken.NULL) { | |
137 | * in.nextNull(); | |
138 | * return null; | |
139 | * } | |
140 | * | |
141 | * Multiset<E> result = LinkedHashMultiset.create(); | |
142 | * in.beginArray(); | |
143 | * while (in.hasNext()) { | |
144 | * int count = in.nextInt(); | |
145 | * E element = elementAdapter.read(in); | |
146 | * result.add(element, count); | |
147 | * } | |
148 | * in.endArray(); | |
149 | * return result; | |
150 | * } | |
151 | * }; | |
152 | * } | |
153 | * } | |
154 | * }</pre> | |
155 | * Delegating from one type adapter to another is extremely powerful; it's | |
156 | * the foundation of how Gson converts Java objects and collections. Whenever | |
157 | * possible your factory should retrieve its delegate type adapter in the | |
158 | * {@code create()} method; this ensures potentially-expensive type adapter | |
159 | * creation happens only once. | |
160 | * | |
161 | * @since 2.1 | |
162 | */ | |
163 | public interface TypeAdapterFactory { | |
164 | ||
165 | /** | |
166 | * Returns a type adapter for {@code type}, or null if this factory doesn't | |
167 | * support {@code type}. | |
168 | */ | |
169 | <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type); | |
170 | } |