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.JsonTreeReader; 20 import com.google.gson.internal.bind.JsonTreeWriter; 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 * <h2>Defining a type's JSON form</h2> 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 // <h2>JSON Conversion</h2> 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 TypeAdapter()121 public TypeAdapter() { 122 } 123 124 /** 125 * Writes one JSON value (an array, object, string, number, boolean or null) 126 * for {@code value}. 127 * 128 * @param value the Java object to write. May be null. 129 */ write(JsonWriter out, T value)130 public abstract void write(JsonWriter out, T value) throws IOException; 131 132 /** 133 * Converts {@code value} to a JSON document and writes it to {@code out}. 134 * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson} 135 * method, this write is strict. Create a {@link 136 * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call 137 * {@link #write(JsonWriter, Object)} for lenient writing. 138 * 139 * @param value the Java object to convert. May be null. 140 * @since 2.2 141 */ toJson(Writer out, T value)142 public final void toJson(Writer out, T value) throws IOException { 143 JsonWriter writer = new JsonWriter(out); 144 write(writer, value); 145 } 146 147 /** 148 * This wrapper method is used to make a type adapter null tolerant. In general, a 149 * type adapter is required to handle nulls in write and read methods. Here is how this 150 * is typically done:<br> 151 * <pre> {@code 152 * 153 * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, 154 * new TypeAdapter<Foo>() { 155 * public Foo read(JsonReader in) throws IOException { 156 * if (in.peek() == JsonToken.NULL) { 157 * in.nextNull(); 158 * return null; 159 * } 160 * // read a Foo from in and return it 161 * } 162 * public void write(JsonWriter out, Foo src) throws IOException { 163 * if (src == null) { 164 * out.nullValue(); 165 * return; 166 * } 167 * // write src as JSON to out 168 * } 169 * }).create(); 170 * }</pre> 171 * You can avoid this boilerplate handling of nulls by wrapping your type adapter with 172 * this method. Here is how we will rewrite the above example: 173 * <pre> {@code 174 * 175 * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, 176 * new TypeAdapter<Foo>() { 177 * public Foo read(JsonReader in) throws IOException { 178 * // read a Foo from in and return it 179 * } 180 * public void write(JsonWriter out, Foo src) throws IOException { 181 * // write src as JSON to out 182 * } 183 * }.nullSafe()).create(); 184 * }</pre> 185 * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. 186 */ nullSafe()187 public final TypeAdapter<T> nullSafe() { 188 return new TypeAdapter<T>() { 189 @Override public void write(JsonWriter out, T value) throws IOException { 190 if (value == null) { 191 out.nullValue(); 192 } else { 193 TypeAdapter.this.write(out, value); 194 } 195 } 196 @Override public T read(JsonReader reader) throws IOException { 197 if (reader.peek() == JsonToken.NULL) { 198 reader.nextNull(); 199 return null; 200 } 201 return TypeAdapter.this.read(reader); 202 } 203 }; 204 } 205 206 /** 207 * Converts {@code value} to a JSON document. Unlike Gson's similar {@link 208 * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link 209 * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call 210 * {@link #write(JsonWriter, Object)} for lenient writing. 211 * 212 * @throws JsonIOException wrapping {@code IOException}s thrown by {@link #write(JsonWriter, Object)} 213 * @param value the Java object to convert. May be null. 214 * @since 2.2 215 */ 216 public final String toJson(T value) { 217 StringWriter stringWriter = new StringWriter(); 218 try { 219 toJson(stringWriter, value); 220 } catch (IOException e) { 221 throw new JsonIOException(e); 222 } 223 return stringWriter.toString(); 224 } 225 226 /** 227 * Converts {@code value} to a JSON tree. 228 * 229 * @param value the Java object to convert. May be null. 230 * @return the converted JSON tree. May be {@link JsonNull}. 231 * @throws JsonIOException wrapping {@code IOException}s thrown by {@link #write(JsonWriter, Object)} 232 * @since 2.2 233 */ 234 public final JsonElement toJsonTree(T value) { 235 try { 236 JsonTreeWriter jsonWriter = new JsonTreeWriter(); 237 write(jsonWriter, value); 238 return jsonWriter.get(); 239 } catch (IOException e) { 240 throw new JsonIOException(e); 241 } 242 } 243 244 /** 245 * Reads one JSON value (an array, object, string, number, boolean or null) 246 * and converts it to a Java object. Returns the converted object. 247 * 248 * @return the converted Java object. May be null. 249 */ 250 public abstract T read(JsonReader in) throws IOException; 251 252 /** 253 * Converts the JSON document in {@code in} to a Java object. Unlike Gson's 254 * similar {@link Gson#fromJson(Reader, Class) fromJson} method, this 255 * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient} 256 * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading. 257 * 258 * <p>No exception is thrown if the JSON data has multiple top-level JSON elements, 259 * or if there is trailing data. 260 * 261 * @return the converted Java object. May be null. 262 * @since 2.2 263 */ 264 public final T fromJson(Reader in) throws IOException { 265 JsonReader reader = new JsonReader(in); 266 return read(reader); 267 } 268 269 /** 270 * Converts the JSON document in {@code json} to a Java object. Unlike Gson's 271 * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is 272 * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code 273 * JsonReader} and call {@link #read(JsonReader)} for lenient reading. 274 * 275 * <p>No exception is thrown if the JSON data has multiple top-level JSON elements, 276 * or if there is trailing data. 277 * 278 * @return the converted Java object. May be null. 279 * @since 2.2 280 */ 281 public final T fromJson(String json) throws IOException { 282 return fromJson(new StringReader(json)); 283 } 284 285 /** 286 * Converts {@code jsonTree} to a Java object. 287 * 288 * @param jsonTree the JSON element to convert. May be {@link JsonNull}. 289 * @return the converted Java object. May be null. 290 * @throws JsonIOException wrapping {@code IOException}s thrown by {@link #read(JsonReader)} 291 * @since 2.2 292 */ 293 public final T fromJsonTree(JsonElement jsonTree) { 294 try { 295 JsonReader jsonReader = new JsonTreeReader(jsonTree); 296 return read(jsonReader); 297 } catch (IOException e) { 298 throw new JsonIOException(e); 299 } 300 } 301 } 302