| 1 | /* |
| 2 | * Copyright (C) 2008 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 java.lang.reflect.Type; |
| 20 | import java.sql.Timestamp; |
| 21 | import java.text.DateFormat; |
| 22 | import java.util.ArrayList; |
| 23 | import java.util.Collections; |
| 24 | import java.util.Date; |
| 25 | import java.util.HashMap; |
| 26 | import java.util.List; |
| 27 | import java.util.Map; |
| 28 | |
| 29 | import com.google.gson.internal.$Gson$Preconditions; |
| 30 | import com.google.gson.internal.Excluder; |
| 31 | import com.google.gson.internal.bind.TypeAdapters; |
| 32 | import com.google.gson.reflect.TypeToken; |
| 33 | |
| 34 | /** |
| 35 | * <p>Use this builder to construct a {@link Gson} instance when you need to set configuration |
| 36 | * options other than the default. For {@link Gson} with default configuration, it is simpler to |
| 37 | * use {@code new Gson()}. {@code GsonBuilder} is best used by creating it, and then invoking its |
| 38 | * various configuration methods, and finally calling create.</p> |
| 39 | * |
| 40 | * <p>The following is an example shows how to use the {@code GsonBuilder} to construct a Gson |
| 41 | * instance: |
| 42 | * |
| 43 | * <pre> |
| 44 | * Gson gson = new GsonBuilder() |
| 45 | * .registerTypeAdapter(Id.class, new IdTypeAdapter()) |
| 46 | * .enableComplexMapKeySerialization() |
| 47 | * .serializeNulls() |
| 48 | * .setDateFormat(DateFormat.LONG) |
| 49 | * .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) |
| 50 | * .setPrettyPrinting() |
| 51 | * .setVersion(1.0) |
| 52 | * .create(); |
| 53 | * </pre></p> |
| 54 | * |
| 55 | * <p>NOTES: |
| 56 | * <ul> |
| 57 | * <li> the order of invocation of configuration methods does not matter.</li> |
| 58 | * <li> The default serialization of {@link Date} and its subclasses in Gson does |
| 59 | * not contain time-zone information. So, if you are using date/time instances, |
| 60 | * use {@code GsonBuilder} and its {@code setDateFormat} methods.</li> |
| 61 | * </ul> |
| 62 | * </p> |
| 63 | * |
| 64 | * @author Inderjeet Singh |
| 65 | * @author Joel Leitch |
| 66 | * @author Jesse Wilson |
| 67 | */ |
| 68 | public final class GsonBuilder { |
| 69 | private Excluder excluder = Excluder.DEFAULT; |
| 70 | private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT; |
| 71 | private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY; |
| 72 | private final Map<Type, InstanceCreator<?>> instanceCreators |
| 73 | = new HashMap<Type, InstanceCreator<?>>(); |
| 74 | private final List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>(); |
| 75 | /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ |
| 76 | private final List<TypeAdapterFactory> hierarchyFactories = new ArrayList<TypeAdapterFactory>(); |
| 77 | private boolean serializeNulls; |
| 78 | private String datePattern; |
| 79 | private int dateStyle = DateFormat.DEFAULT; |
| 80 | private int timeStyle = DateFormat.DEFAULT; |
| 81 | private boolean complexMapKeySerialization; |
| 82 | private boolean serializeSpecialFloatingPointValues; |
| 83 | private boolean escapeHtmlChars = true; |
| 84 | private boolean prettyPrinting; |
| 85 | private boolean generateNonExecutableJson; |
| 86 | |
| 87 | /** |
| 88 | * Creates a GsonBuilder instance that can be used to build Gson with various configuration |
| 89 | * settings. GsonBuilder follows the builder pattern, and it is typically used by first |
| 90 | * invoking various configuration methods to set desired options, and finally calling |
| 91 | * {@link #create()}. |
| 92 | */ |
| 93 | public GsonBuilder() { |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * Configures Gson to enable versioning support. |
| 98 | * |
| 99 | * @param ignoreVersionsAfter any field or type marked with a version higher than this value |
| 100 | * are ignored during serialization or deserialization. |
| 101 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 102 | */ |
| 103 | public GsonBuilder setVersion(double ignoreVersionsAfter) { |
| 104 | excluder = excluder.withVersion(ignoreVersionsAfter); |
| 105 | return this; |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Configures Gson to excludes all class fields that have the specified modifiers. By default, |
| 110 | * Gson will exclude all fields marked transient or static. This method will override that |
| 111 | * behavior. |
| 112 | * |
| 113 | * @param modifiers the field modifiers. You must use the modifiers specified in the |
| 114 | * {@link java.lang.reflect.Modifier} class. For example, |
| 115 | * {@link java.lang.reflect.Modifier#TRANSIENT}, |
| 116 | * {@link java.lang.reflect.Modifier#STATIC}. |
| 117 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 118 | */ |
| 119 | public GsonBuilder excludeFieldsWithModifiers(int... modifiers) { |
| 120 | excluder = excluder.withModifiers(modifiers); |
| 121 | return this; |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some |
| 126 | * special text. This prevents attacks from third-party sites through script sourcing. See |
| 127 | * <a href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson Issue 42</a> |
| 128 | * for details. |
| 129 | * |
| 130 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 131 | * @since 1.3 |
| 132 | */ |
| 133 | public GsonBuilder generateNonExecutableJson() { |
| 134 | this.generateNonExecutableJson = true; |
| 135 | return this; |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Configures Gson to exclude all fields from consideration for serialization or deserialization |
| 140 | * that do not have the {@link com.google.gson.annotations.Expose} annotation. |
| 141 | * |
| 142 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 143 | */ |
| 144 | public GsonBuilder excludeFieldsWithoutExposeAnnotation() { |
| 145 | excluder = excluder.excludeFieldsWithoutExposeAnnotation(); |
| 146 | return this; |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Configure Gson to serialize null fields. By default, Gson omits all fields that are null |
| 151 | * during serialization. |
| 152 | * |
| 153 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 154 | * @since 1.2 |
| 155 | */ |
| 156 | public GsonBuilder serializeNulls() { |
| 157 | this.serializeNulls = true; |
| 158 | return this; |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Enabling this feature will only change the serialized form if the map key is |
| 163 | * a complex type (i.e. non-primitive) in its <strong>serialized</strong> JSON |
| 164 | * form. The default implementation of map serialization uses {@code toString()} |
| 165 | * on the key; however, when this is called then one of the following cases |
| 166 | * apply: |
| 167 | * |
| 168 | * <h3>Maps as JSON objects</h3> |
| 169 | * For this case, assume that a type adapter is registered to serialize and |
| 170 | * deserialize some {@code Point} class, which contains an x and y coordinate, |
| 171 | * to/from the JSON Primitive string value {@code "(x,y)"}. The Java map would |
| 172 | * then be serialized as a {@link JsonObject}. |
| 173 | * |
| 174 | * <p>Below is an example: |
| 175 | * <pre> {@code |
| 176 | * Gson gson = new GsonBuilder() |
| 177 | * .register(Point.class, new MyPointTypeAdapter()) |
| 178 | * .enableComplexMapKeySerialization() |
| 179 | * .create(); |
| 180 | * |
| 181 | * Map<Point, String> original = new LinkedHashMap<Point, String>(); |
| 182 | * original.put(new Point(5, 6), "a"); |
| 183 | * original.put(new Point(8, 8), "b"); |
| 184 | * System.out.println(gson.toJson(original, type)); |
| 185 | * }</pre> |
| 186 | * The above code prints this JSON object:<pre> {@code |
| 187 | * { |
| 188 | * "(5,6)": "a", |
| 189 | * "(8,8)": "b" |
| 190 | * } |
| 191 | * }</pre> |
| 192 | * |
| 193 | * <h3>Maps as JSON arrays</h3> |
| 194 | * For this case, assume that a type adapter was NOT registered for some |
| 195 | * {@code Point} class, but rather the default Gson serialization is applied. |
| 196 | * In this case, some {@code new Point(2,3)} would serialize as {@code |
| 197 | * {"x":2,"y":5}}. |
| 198 | * |
| 199 | * <p>Given the assumption above, a {@code Map<Point, String>} will be |
| 200 | * serialize as an array of arrays (can be viewed as an entry set of pairs). |
| 201 | * |
| 202 | * <p>Below is an example of serializing complex types as JSON arrays: |
| 203 | * <pre> {@code |
| 204 | * Gson gson = new GsonBuilder() |
| 205 | * .enableComplexMapKeySerialization() |
| 206 | * .create(); |
| 207 | * |
| 208 | * Map<Point, String> original = new LinkedHashMap<Point, String>(); |
| 209 | * original.put(new Point(5, 6), "a"); |
| 210 | * original.put(new Point(8, 8), "b"); |
| 211 | * System.out.println(gson.toJson(original, type)); |
| 212 | * } |
| 213 | * |
| 214 | * The JSON output would look as follows: |
| 215 | * <pre> {@code |
| 216 | * [ |
| 217 | * [ |
| 218 | * { |
| 219 | * "x": 5, |
| 220 | * "y": 6 |
| 221 | * }, |
| 222 | * "a" |
| 223 | * ], |
| 224 | * [ |
| 225 | * { |
| 226 | * "x": 8, |
| 227 | * "y": 8 |
| 228 | * }, |
| 229 | * "b" |
| 230 | * ] |
| 231 | * ] |
| 232 | * }</pre> |
| 233 | * |
| 234 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 235 | * @since 1.7 |
| 236 | */ |
| 237 | public GsonBuilder enableComplexMapKeySerialization() { |
| 238 | complexMapKeySerialization = true; |
| 239 | return this; |
| 240 | } |
| 241 | |
| 242 | /** |
| 243 | * Configures Gson to exclude inner classes during serialization. |
| 244 | * |
| 245 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 246 | * @since 1.3 |
| 247 | */ |
| 248 | public GsonBuilder disableInnerClassSerialization() { |
| 249 | excluder = excluder.disableInnerClassSerialization(); |
| 250 | return this; |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Configures Gson to apply a specific serialization policy for {@code Long} and {@code long} |
| 255 | * objects. |
| 256 | * |
| 257 | * @param serializationPolicy the particular policy to use for serializing longs. |
| 258 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 259 | * @since 1.3 |
| 260 | */ |
| 261 | public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) { |
| 262 | this.longSerializationPolicy = serializationPolicy; |
| 263 | return this; |
| 264 | } |
| 265 | |
| 266 | /** |
| 267 | * Configures Gson to apply a specific naming policy to an object's field during serialization |
| 268 | * and deserialization. |
| 269 | * |
| 270 | * @param namingConvention the JSON field naming convention to use for serialization and |
| 271 | * deserialization. |
| 272 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 273 | */ |
| 274 | public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) { |
| 275 | this.fieldNamingPolicy = namingConvention; |
| 276 | return this; |
| 277 | } |
| 278 | |
| 279 | /** |
| 280 | * Configures Gson to apply a specific naming policy strategy to an object's field during |
| 281 | * serialization and deserialization. |
| 282 | * |
| 283 | * @param fieldNamingStrategy the actual naming strategy to apply to the fields |
| 284 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 285 | * @since 1.3 |
| 286 | */ |
| 287 | public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) { |
| 288 | this.fieldNamingPolicy = fieldNamingStrategy; |
| 289 | return this; |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * Configures Gson to apply a set of exclusion strategies during both serialization and |
| 294 | * deserialization. Each of the {@code strategies} will be applied as a disjunction rule. |
| 295 | * This means that if one of the {@code strategies} suggests that a field (or class) should be |
| 296 | * skipped then that field (or object) is skipped during serializaiton/deserialization. |
| 297 | * |
| 298 | * @param strategies the set of strategy object to apply during object (de)serialization. |
| 299 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 300 | * @since 1.4 |
| 301 | */ |
| 302 | public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) { |
| 303 | for (ExclusionStrategy strategy : strategies) { |
| 304 | excluder = excluder.withExclusionStrategy(strategy, true, true); |
| 305 | } |
| 306 | return this; |
| 307 | } |
| 308 | |
| 309 | /** |
| 310 | * Configures Gson to apply the passed in exclusion strategy during serialization. |
| 311 | * If this method is invoked numerous times with different exclusion strategy objects |
| 312 | * then the exclusion strategies that were added will be applied as a disjunction rule. |
| 313 | * This means that if one of the added exclusion strategies suggests that a field (or |
| 314 | * class) should be skipped then that field (or object) is skipped during its |
| 315 | * serialization. |
| 316 | * |
| 317 | * @param strategy an exclusion strategy to apply during serialization. |
| 318 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 319 | * @since 1.7 |
| 320 | */ |
| 321 | public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) { |
| 322 | excluder = excluder.withExclusionStrategy(strategy, true, false); |
| 323 | return this; |
| 324 | } |
| 325 | |
| 326 | /** |
| 327 | * Configures Gson to apply the passed in exclusion strategy during deserialization. |
| 328 | * If this method is invoked numerous times with different exclusion strategy objects |
| 329 | * then the exclusion strategies that were added will be applied as a disjunction rule. |
| 330 | * This means that if one of the added exclusion strategies suggests that a field (or |
| 331 | * class) should be skipped then that field (or object) is skipped during its |
| 332 | * deserialization. |
| 333 | * |
| 334 | * @param strategy an exclusion strategy to apply during deserialization. |
| 335 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 336 | * @since 1.7 |
| 337 | */ |
| 338 | public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy) { |
| 339 | excluder = excluder.withExclusionStrategy(strategy, false, true); |
| 340 | return this; |
| 341 | } |
| 342 | |
| 343 | /** |
| 344 | * Configures Gson to output Json that fits in a page for pretty printing. This option only |
| 345 | * affects Json serialization. |
| 346 | * |
| 347 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 348 | */ |
| 349 | public GsonBuilder setPrettyPrinting() { |
| 350 | prettyPrinting = true; |
| 351 | return this; |
| 352 | } |
| 353 | |
| 354 | /** |
| 355 | * By default, Gson escapes HTML characters such as < > etc. Use this option to configure |
| 356 | * Gson to pass-through HTML characters as is. |
| 357 | * |
| 358 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 359 | * @since 1.3 |
| 360 | */ |
| 361 | public GsonBuilder disableHtmlEscaping() { |
| 362 | this.escapeHtmlChars = false; |
| 363 | return this; |
| 364 | } |
| 365 | |
| 366 | /** |
| 367 | * Configures Gson to serialize {@code Date} objects according to the pattern provided. You can |
| 368 | * call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation |
| 369 | * will be used to decide the serialization format. |
| 370 | * |
| 371 | * <p>The date format will be used to serialize and deserialize {@link java.util.Date}, {@link |
| 372 | * java.sql.Timestamp} and {@link java.sql.Date}. |
| 373 | * |
| 374 | * <p>Note that this pattern must abide by the convention provided by {@code SimpleDateFormat} |
| 375 | * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on |
| 376 | * valid date and time patterns.</p> |
| 377 | * |
| 378 | * @param pattern the pattern that dates will be serialized/deserialized to/from |
| 379 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 380 | * @since 1.2 |
| 381 | */ |
| 382 | public GsonBuilder setDateFormat(String pattern) { |
| 383 | // TODO(Joel): Make this fail fast if it is an invalid date format |
| 384 | this.datePattern = pattern; |
| 385 | return this; |
| 386 | } |
| 387 | |
| 388 | /** |
| 389 | * Configures Gson to to serialize {@code Date} objects according to the style value provided. |
| 390 | * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last |
| 391 | * invocation will be used to decide the serialization format. |
| 392 | * |
| 393 | * <p>Note that this style value should be one of the predefined constants in the |
| 394 | * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more |
| 395 | * information on the valid style constants.</p> |
| 396 | * |
| 397 | * @param style the predefined date style that date objects will be serialized/deserialized |
| 398 | * to/from |
| 399 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 400 | * @since 1.2 |
| 401 | */ |
| 402 | public GsonBuilder setDateFormat(int style) { |
| 403 | this.dateStyle = style; |
| 404 | this.datePattern = null; |
| 405 | return this; |
| 406 | } |
| 407 | |
| 408 | /** |
| 409 | * Configures Gson to to serialize {@code Date} objects according to the style value provided. |
| 410 | * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last |
| 411 | * invocation will be used to decide the serialization format. |
| 412 | * |
| 413 | * <p>Note that this style value should be one of the predefined constants in the |
| 414 | * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more |
| 415 | * information on the valid style constants.</p> |
| 416 | * |
| 417 | * @param dateStyle the predefined date style that date objects will be serialized/deserialized |
| 418 | * to/from |
| 419 | * @param timeStyle the predefined style for the time portion of the date objects |
| 420 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 421 | * @since 1.2 |
| 422 | */ |
| 423 | public GsonBuilder setDateFormat(int dateStyle, int timeStyle) { |
| 424 | this.dateStyle = dateStyle; |
| 425 | this.timeStyle = timeStyle; |
| 426 | this.datePattern = null; |
| 427 | return this; |
| 428 | } |
| 429 | |
| 430 | /** |
| 431 | * Configures Gson for custom serialization or deserialization. This method combines the |
| 432 | * registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a |
| 433 | * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements |
| 434 | * all the required interfaces for custom serialization with Gson. If a type adapter was |
| 435 | * previously registered for the specified {@code type}, it is overwritten. |
| 436 | * |
| 437 | * <p>This registers the type specified and no other types: you must manually register related |
| 438 | * types! For example, applications registering {@code boolean.class} should also register {@code |
| 439 | * Boolean.class}. |
| 440 | * |
| 441 | * @param type the type definition for the type adapter being registered |
| 442 | * @param typeAdapter This object must implement at least one of the {@link TypeAdapter}, |
| 443 | * {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. |
| 444 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 445 | */ |
| 446 | @SuppressWarnings({"unchecked", "rawtypes"}) |
| 447 | public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { |
| 448 | $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?> |
| 449 | || typeAdapter instanceof JsonDeserializer<?> |
| 450 | || typeAdapter instanceof InstanceCreator<?> |
| 451 | || typeAdapter instanceof TypeAdapter<?>); |
| 452 | if (typeAdapter instanceof InstanceCreator<?>) { |
| 453 | instanceCreators.put(type, (InstanceCreator) typeAdapter); |
| 454 | } |
| 455 | if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) { |
| 456 | TypeToken<?> typeToken = TypeToken.get(type); |
| 457 | factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter)); |
| 458 | } |
| 459 | if (typeAdapter instanceof TypeAdapter<?>) { |
| 460 | factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter)); |
| 461 | } |
| 462 | return this; |
| 463 | } |
| 464 | |
| 465 | /** |
| 466 | * Register a factory for type adapters. Registering a factory is useful when the type |
| 467 | * adapter needs to be configured based on the type of the field being processed. Gson |
| 468 | * is designed to handle a large number of factories, so you should consider registering |
| 469 | * them to be at par with registering an individual type adapter. |
| 470 | * |
| 471 | * @since 2.1 |
| 472 | */ |
| 473 | public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) { |
| 474 | factories.add(factory); |
| 475 | return this; |
| 476 | } |
| 477 | |
| 478 | /** |
| 479 | * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy. |
| 480 | * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and |
| 481 | * a {@link JsonDeserializer}. If a type adapter was previously registered for the specified |
| 482 | * type hierarchy, it is overridden. If a type adapter is registered for a specific type in |
| 483 | * the type hierarchy, it will be invoked instead of the one registered for the type hierarchy. |
| 484 | * |
| 485 | * @param baseType the class definition for the type adapter being registered for the base class |
| 486 | * or interface |
| 487 | * @param typeAdapter This object must implement at least one of {@link TypeAdapter}, |
| 488 | * {@link JsonSerializer} or {@link JsonDeserializer} interfaces. |
| 489 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 490 | * @since 1.7 |
| 491 | */ |
| 492 | @SuppressWarnings({"unchecked", "rawtypes"}) |
| 493 | public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) { |
| 494 | $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?> |
| 495 | || typeAdapter instanceof JsonDeserializer<?> |
| 496 | || typeAdapter instanceof TypeAdapter<?>); |
| 497 | if (typeAdapter instanceof JsonDeserializer || typeAdapter instanceof JsonSerializer) { |
| 498 | hierarchyFactories.add(0, |
| 499 | TreeTypeAdapter.newTypeHierarchyFactory(baseType, typeAdapter)); |
| 500 | } |
| 501 | if (typeAdapter instanceof TypeAdapter<?>) { |
| 502 | factories.add(TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter)typeAdapter)); |
| 503 | } |
| 504 | return this; |
| 505 | } |
| 506 | |
| 507 | /** |
| 508 | * Section 2.4 of <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a> disallows |
| 509 | * special double values (NaN, Infinity, -Infinity). However, |
| 510 | * <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">Javascript |
| 511 | * specification</a> (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript |
| 512 | * values. Moreover, most JavaScript engines will accept these special values in JSON without |
| 513 | * problem. So, at a practical level, it makes sense to accept these values as valid JSON even |
| 514 | * though JSON specification disallows them. |
| 515 | * |
| 516 | * <p>Gson always accepts these special values during deserialization. However, it outputs |
| 517 | * strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN}, |
| 518 | * {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value |
| 519 | * {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it |
| 520 | * will throw an {@link IllegalArgumentException}. This method provides a way to override the |
| 521 | * default behavior when you know that the JSON receiver will be able to handle these special |
| 522 | * values. |
| 523 | * |
| 524 | * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern |
| 525 | * @since 1.3 |
| 526 | */ |
| 527 | public GsonBuilder serializeSpecialFloatingPointValues() { |
| 528 | this.serializeSpecialFloatingPointValues = true; |
| 529 | return this; |
| 530 | } |
| 531 | |
| 532 | /** |
| 533 | * Creates a {@link Gson} instance based on the current configuration. This method is free of |
| 534 | * side-effects to this {@code GsonBuilder} instance and hence can be called multiple times. |
| 535 | * |
| 536 | * @return an instance of Gson configured with the options currently set in this builder |
| 537 | */ |
| 538 | public Gson create() { |
| 539 | List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>(); |
| 540 | factories.addAll(this.factories); |
| 541 | Collections.reverse(factories); |
| 542 | factories.addAll(this.hierarchyFactories); |
| 543 | addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories); |
| 544 | |
| 545 | return new Gson(excluder, fieldNamingPolicy, instanceCreators, |
| 546 | serializeNulls, complexMapKeySerialization, |
| 547 | generateNonExecutableJson, escapeHtmlChars, prettyPrinting, |
| 548 | serializeSpecialFloatingPointValues, longSerializationPolicy, factories); |
| 549 | } |
| 550 | |
| 551 | private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, |
| 552 | List<TypeAdapterFactory> factories) { |
| 553 | DefaultDateTypeAdapter dateTypeAdapter; |
| 554 | if (datePattern != null && !"".equals(datePattern.trim())) { |
| 555 | dateTypeAdapter = new DefaultDateTypeAdapter(datePattern); |
| 556 | } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) { |
| 557 | dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle); |
| 558 | } else { |
| 559 | return; |
| 560 | } |
| 561 | |
| 562 | factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Date.class), dateTypeAdapter)); |
| 563 | factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Timestamp.class), dateTypeAdapter)); |
| 564 | factories.add(TreeTypeAdapter.newFactory(TypeToken.get(java.sql.Date.class), dateTypeAdapter)); |
| 565 | } |
| 566 | } |