| 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 | } |