]>
Commit | Line | Data |
---|---|---|
cfd903b6 MG |
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.internal.bind.JsonTreeWriter; | |
20 | import com.google.gson.internal.bind.JsonTreeReader; | |
21 | import com.google.gson.stream.JsonReader; | |
22 | import com.google.gson.stream.JsonToken; | |
23 | import com.google.gson.stream.JsonWriter; | |
24 | import java.io.IOException; | |
25 | import java.io.Reader; | |
26 | import java.io.StringReader; | |
27 | import java.io.StringWriter; | |
28 | import java.io.Writer; | |
29 | ||
30 | /** | |
31 | * Converts Java objects to and from JSON. | |
32 | * | |
33 | * <h3>Defining a type's JSON form</h3> | |
34 | * By default Gson converts application classes to JSON using its built-in type | |
35 | * adapters. If Gson's default JSON conversion isn't appropriate for a type, | |
36 | * extend this class to customize the conversion. Here's an example of a type | |
37 | * adapter for an (X,Y) coordinate point: <pre> {@code | |
38 | * | |
39 | * public class PointAdapter extends TypeAdapter<Point> { | |
40 | * public Point read(JsonReader reader) throws IOException { | |
41 | * if (reader.peek() == JsonToken.NULL) { | |
42 | * reader.nextNull(); | |
43 | * return null; | |
44 | * } | |
45 | * String xy = reader.nextString(); | |
46 | * String[] parts = xy.split(","); | |
47 | * int x = Integer.parseInt(parts[0]); | |
48 | * int y = Integer.parseInt(parts[1]); | |
49 | * return new Point(x, y); | |
50 | * } | |
51 | * public void write(JsonWriter writer, Point value) throws IOException { | |
52 | * if (value == null) { | |
53 | * writer.nullValue(); | |
54 | * return; | |
55 | * } | |
56 | * String xy = value.getX() + "," + value.getY(); | |
57 | * writer.value(xy); | |
58 | * } | |
59 | * }}</pre> | |
60 | * With this type adapter installed, Gson will convert {@code Points} to JSON as | |
61 | * strings like {@code "5,8"} rather than objects like {@code {"x":5,"y":8}}. In | |
62 | * this case the type adapter binds a rich Java class to a compact JSON value. | |
63 | * | |
64 | * <p>The {@link #read(JsonReader) read()} method must read exactly one value | |
65 | * and {@link #write(JsonWriter,Object) write()} must write exactly one value. | |
66 | * For primitive types this is means readers should make exactly one call to | |
67 | * {@code nextBoolean()}, {@code nextDouble()}, {@code nextInt()}, {@code | |
68 | * nextLong()}, {@code nextString()} or {@code nextNull()}. Writers should make | |
69 | * exactly one call to one of <code>value()</code> or <code>nullValue()</code>. | |
70 | * For arrays, type adapters should start with a call to {@code beginArray()}, | |
71 | * convert all elements, and finish with a call to {@code endArray()}. For | |
72 | * objects, they should start with {@code beginObject()}, convert the object, | |
73 | * and finish with {@code endObject()}. Failing to convert a value or converting | |
74 | * too many values may cause the application to crash. | |
75 | * | |
76 | * <p>Type adapters should be prepared to read null from the stream and write it | |
77 | * to the stream. Alternatively, they should use {@link #nullSafe()} method while | |
78 | * registering the type adapter with Gson. If your {@code Gson} instance | |
79 | * has been configured to {@link GsonBuilder#serializeNulls()}, these nulls will be | |
80 | * written to the final document. Otherwise the value (and the corresponding name | |
81 | * when writing to a JSON object) will be omitted automatically. In either case | |
82 | * your type adapter must handle null. | |
83 | * | |
84 | * <p>To use a custom type adapter with Gson, you must <i>register</i> it with a | |
85 | * {@link GsonBuilder}: <pre> {@code | |
86 | * | |
87 | * GsonBuilder builder = new GsonBuilder(); | |
88 | * builder.registerTypeAdapter(Point.class, new PointAdapter()); | |
89 | * // if PointAdapter didn't check for nulls in its read/write methods, you should instead use | |
90 | * // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe()); | |
91 | * ... | |
92 | * Gson gson = builder.create(); | |
93 | * }</pre> | |
94 | * | |
95 | * @since 2.1 | |
96 | */ | |
97 | // non-Javadoc: | |
98 | // | |
99 | // <h3>JSON Conversion</h3> | |
100 | // <p>A type adapter registered with Gson is automatically invoked while serializing | |
101 | // or deserializing JSON. However, you can also use type adapters directly to serialize | |
102 | // and deserialize JSON. Here is an example for deserialization: <pre> {@code | |
103 | // | |
104 | // String json = "{'origin':'0,0','points':['1,2','3,4']}"; | |
105 | // TypeAdapter<Graph> graphAdapter = gson.getAdapter(Graph.class); | |
106 | // Graph graph = graphAdapter.fromJson(json); | |
107 | // }</pre> | |
108 | // And an example for serialization: <pre> {@code | |
109 | // | |
110 | // Graph graph = new Graph(...); | |
111 | // TypeAdapter<Graph> graphAdapter = gson.getAdapter(Graph.class); | |
112 | // String json = graphAdapter.toJson(graph); | |
113 | // }</pre> | |
114 | // | |
115 | // <p>Type adapters are <strong>type-specific</strong>. For example, a {@code | |
116 | // TypeAdapter<Date>} can convert {@code Date} instances to JSON and JSON to | |
117 | // instances of {@code Date}, but cannot convert any other types. | |
118 | // | |
119 | public abstract class TypeAdapter<T> { | |
120 | ||
121 | /** | |
122 | * Writes one JSON value (an array, object, string, number, boolean or null) | |
123 | * for {@code value}. | |
124 | * | |
125 | * @param value the Java object to write. May be null. | |
126 | */ | |
127 | public abstract void write(JsonWriter out, T value) throws IOException; | |
128 | ||
129 | /** | |
130 | * Converts {@code value} to a JSON document and writes it to {@code out}. | |
131 | * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson} | |
132 | * method, this write is strict. Create a {@link | |
133 | * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call | |
134 | * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient | |
135 | * writing. | |
136 | * | |
137 | * @param value the Java object to convert. May be null. | |
138 | * @since 2.2 | |
139 | */ | |
140 | public final void toJson(Writer out, T value) throws IOException { | |
141 | JsonWriter writer = new JsonWriter(out); | |
142 | write(writer, value); | |
143 | } | |
144 | ||
145 | /** | |
146 | * This wrapper method is used to make a type adapter null tolerant. In general, a | |
147 | * type adapter is required to handle nulls in write and read methods. Here is how this | |
148 | * is typically done:<br> | |
149 | * <pre> {@code | |
150 | * | |
151 | * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, | |
152 | * new TypeAdapter<Foo>() { | |
153 | * public Foo read(JsonReader in) throws IOException { | |
154 | * if (in.peek() == JsonToken.NULL) { | |
155 | * in.nextNull(); | |
156 | * return null; | |
157 | * } | |
158 | * // read a Foo from in and return it | |
159 | * } | |
160 | * public void write(JsonWriter out, Foo src) throws IOException { | |
161 | * if (src == null) { | |
162 | * out.nullValue(); | |
163 | * return; | |
164 | * } | |
165 | * // write src as JSON to out | |
166 | * } | |
167 | * }).create(); | |
168 | * }</pre> | |
169 | * You can avoid this boilerplate handling of nulls by wrapping your type adapter with | |
170 | * this method. Here is how we will rewrite the above example: | |
171 | * <pre> {@code | |
172 | * | |
173 | * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, | |
174 | * new TypeAdapter<Foo>() { | |
175 | * public Foo read(JsonReader in) throws IOException { | |
176 | * // read a Foo from in and return it | |
177 | * } | |
178 | * public void write(JsonWriter out, Foo src) throws IOException { | |
179 | * // write src as JSON to out | |
180 | * } | |
181 | * }.nullSafe()).create(); | |
182 | * }</pre> | |
183 | * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. | |
184 | */ | |
185 | public final TypeAdapter<T> nullSafe() { | |
186 | return new TypeAdapter<T>() { | |
187 | @Override public void write(JsonWriter out, T value) throws IOException { | |
188 | if (value == null) { | |
189 | out.nullValue(); | |
190 | } else { | |
191 | TypeAdapter.this.write(out, value); | |
192 | } | |
193 | } | |
194 | @Override public T read(JsonReader reader) throws IOException { | |
195 | if (reader.peek() == JsonToken.NULL) { | |
196 | reader.nextNull(); | |
197 | return null; | |
198 | } | |
199 | return TypeAdapter.this.read(reader); | |
200 | } | |
201 | }; | |
202 | } | |
203 | ||
204 | /** | |
205 | * Converts {@code value} to a JSON document. Unlike Gson's similar {@link | |
206 | * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link | |
207 | * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call | |
208 | * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient | |
209 | * writing. | |
210 | * | |
211 | * @param value the Java object to convert. May be null. | |
212 | * @since 2.2 | |
213 | */ | |
214 | public final String toJson(T value) throws IOException { | |
215 | StringWriter stringWriter = new StringWriter(); | |
216 | toJson(stringWriter, value); | |
217 | return stringWriter.toString(); | |
218 | } | |
219 | ||
220 | /** | |
221 | * Converts {@code value} to a JSON tree. | |
222 | * | |
223 | * @param value the Java object to convert. May be null. | |
224 | * @return the converted JSON tree. May be {@link JsonNull}. | |
225 | * @since 2.2 | |
226 | */ | |
227 | public final JsonElement toJsonTree(T value) { | |
228 | try { | |
229 | JsonTreeWriter jsonWriter = new JsonTreeWriter(); | |
230 | write(jsonWriter, value); | |
231 | return jsonWriter.get(); | |
232 | } catch (IOException e) { | |
233 | throw new JsonIOException(e); | |
234 | } | |
235 | } | |
236 | ||
237 | /** | |
238 | * Reads one JSON value (an array, object, string, number, boolean or null) | |
239 | * and converts it to a Java object. Returns the converted object. | |
240 | * | |
241 | * @return the converted Java object. May be null. | |
242 | */ | |
243 | public abstract T read(JsonReader in) throws IOException; | |
244 | ||
245 | /** | |
246 | * Converts the JSON document in {@code in} to a Java object. Unlike Gson's | |
247 | * similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this | |
248 | * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient} | |
249 | * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading. | |
250 | * | |
251 | * @return the converted Java object. May be null. | |
252 | * @since 2.2 | |
253 | */ | |
254 | public final T fromJson(Reader in) throws IOException { | |
255 | JsonReader reader = new JsonReader(in); | |
256 | return read(reader); | |
257 | } | |
258 | ||
259 | /** | |
260 | * Converts the JSON document in {@code json} to a Java object. Unlike Gson's | |
261 | * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is | |
262 | * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code | |
263 | * JsonReader} and call {@link #read(JsonReader)} for lenient reading. | |
264 | * | |
265 | * @return the converted Java object. May be null. | |
266 | * @since 2.2 | |
267 | */ | |
268 | public final T fromJson(String json) throws IOException { | |
269 | return fromJson(new StringReader(json)); | |
270 | } | |
271 | ||
272 | /** | |
273 | * Converts {@code jsonTree} to a Java object. | |
274 | * | |
275 | * @param jsonTree the Java object to convert. May be {@link JsonNull}. | |
276 | * @since 2.2 | |
277 | */ | |
278 | public final T fromJsonTree(JsonElement jsonTree) { | |
279 | try { | |
280 | JsonReader jsonReader = new JsonTreeReader(jsonTree); | |
281 | return read(jsonReader); | |
282 | } catch (IOException e) { | |
283 | throw new JsonIOException(e); | |
284 | } | |
285 | } | |
286 | } |