diff --git a/UserGuide.md b/UserGuide.md index ec7fb7eb..12b53351 100644 --- a/UserGuide.md +++ b/UserGuide.md @@ -155,7 +155,8 @@ BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class); * While serializing, a null field is omitted from the output. * While deserializing, a missing entry in JSON results in setting the corresponding field in the object to its default value: null for object types, zero for numeric types, and false for booleans. * If a field is _synthetic_, it is ignored and not included in JSON serialization or deserialization. -* Fields corresponding to the outer classes in inner classes, anonymous classes, and local classes are ignored and not included in serialization or deserialization. +* Fields corresponding to the outer classes in inner classes are ignored and not included in serialization or deserialization. +* Anonymous and local classes are excluded. They will be serialized as JSON `null` and when deserialized their JSON value is ignored and `null` is returned. Convert the classes to `static` nested classes to enable serialization and deserialization for them. ### Nested Classes (including Inner Classes) diff --git a/gson/src/main/java/com/google/gson/ExclusionStrategy.java b/gson/src/main/java/com/google/gson/ExclusionStrategy.java index bc0dc74a..557eddb2 100644 --- a/gson/src/main/java/com/google/gson/ExclusionStrategy.java +++ b/gson/src/main/java/com/google/gson/ExclusionStrategy.java @@ -17,11 +17,8 @@ package com.google.gson; /** - * A strategy (or policy) definition that is used to decide whether or not a field or top-level - * class should be serialized or deserialized as part of the JSON output/input. For serialization, - * if the {@link #shouldSkipClass(Class)} method returns true then that class or field type - * will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)} - * returns true, then it will not be set as part of the Java object structure. + * A strategy (or policy) definition that is used to decide whether or not a field or + * class should be serialized or deserialized as part of the JSON output/input. * *

The following are a few examples that shows how you can use this exclusion mechanism. * @@ -64,7 +61,7 @@ package com.google.gson; * *

Now if you want to configure {@code Gson} to use a user defined exclusion strategy, then * the {@code GsonBuilder} is required. The following is an example of how you can use the - * {@code GsonBuilder} to configure Gson to use one of the above sample: + * {@code GsonBuilder} to configure Gson to use one of the above samples: *

  * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
  * Gson gson = new GsonBuilder()
diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java
index e28da7c7..8332ccb3 100644
--- a/gson/src/main/java/com/google/gson/GsonBuilder.java
+++ b/gson/src/main/java/com/google/gson/GsonBuilder.java
@@ -156,8 +156,11 @@ public final class GsonBuilder {
 
   /**
    * Configures Gson to excludes all class fields that have the specified modifiers. By default,
-   * Gson will exclude all fields marked transient or static. This method will override that
-   * behavior.
+   * Gson will exclude all fields marked {@code transient} or {@code static}. This method will
+   * override that behavior.
+   *
+   * 

This is a convenience method which behaves as if an {@link ExclusionStrategy} which + * excludes these fields was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this builder}. * * @param modifiers the field modifiers. You must use the modifiers specified in the * {@link java.lang.reflect.Modifier} class. For example, @@ -186,9 +189,12 @@ public final class GsonBuilder { } /** - * Configures Gson to exclude all fields from consideration for serialization or deserialization + * Configures Gson to exclude all fields from consideration for serialization and deserialization * that do not have the {@link com.google.gson.annotations.Expose} annotation. * + *

This is a convenience method which behaves as if an {@link ExclusionStrategy} which excludes + * these fields was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this builder}. + * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder excludeFieldsWithoutExposeAnnotation() { @@ -291,7 +297,20 @@ public final class GsonBuilder { } /** - * Configures Gson to exclude inner classes during serialization. + * Configures Gson to exclude inner classes (= non-{@code static} nested classes) during serialization + * and deserialization. This is a convenience method which behaves as if an {@link ExclusionStrategy} + * which excludes inner classes was {@linkplain #setExclusionStrategies(ExclusionStrategy...) registered with this builder}. + * This means inner classes will be serialized as JSON {@code null}, and will be deserialized as + * Java {@code null} with their JSON data being ignored. And fields with an inner class as type will + * be ignored during serialization and deserialization. + * + *

By default Gson serializes and deserializes inner classes, but ignores references to the + * enclosing instance. Deserialization might not be possible at all when {@link #disableJdkUnsafe()} + * is used (and no custom {@link InstanceCreator} is registered), or it can lead to unexpected + * {@code NullPointerException}s when the deserialized instance is used afterwards. + * + *

In general using inner classes with Gson should be avoided; they should be converted to {@code static} + * nested classes if possible. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.3 @@ -369,6 +388,16 @@ public final class GsonBuilder { * The strategies are added to the existing strategies (if any); the existing strategies * are not replaced. * + *

Fields are excluded for serialization and deserialization when + * {@link ExclusionStrategy#shouldSkipField(FieldAttributes) shouldSkipField} returns {@code true}, + * or when {@link ExclusionStrategy#shouldSkipClass(Class) shouldSkipClass} returns {@code true} + * for the field type. Gson behaves as if the field did not exist; its value is not serialized + * and on deserialization if a JSON member with this name exists it is skipped by default.
+ * When objects of an excluded type (as determined by + * {@link ExclusionStrategy#shouldSkipClass(Class) shouldSkipClass}) are serialized a + * JSON null is written to output, and when deserialized the JSON value is skipped and + * {@code null} is returned. + * * @param strategies the set of strategy object to apply during object (de)serialization. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.4 @@ -389,6 +418,9 @@ public final class GsonBuilder { * class) should be skipped then that field (or object) is skipped during its * serialization. * + *

See the documentation of {@link #setExclusionStrategies(ExclusionStrategy...)} + * for a detailed description of the effect of exclusion strategies. + * * @param strategy an exclusion strategy to apply during serialization. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.7 @@ -407,6 +439,9 @@ public final class GsonBuilder { * class) should be skipped then that field (or object) is skipped during its * deserialization. * + *

See the documentation of {@link #setExclusionStrategies(ExclusionStrategy...)} + * for a detailed description of the effect of exclusion strategies. + * * @param strategy an exclusion strategy to apply during deserialization. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @since 1.7 diff --git a/gson/src/main/java/com/google/gson/JsonArray.java b/gson/src/main/java/com/google/gson/JsonArray.java index cf5b77d3..d861cfb3 100644 --- a/gson/src/main/java/com/google/gson/JsonArray.java +++ b/gson/src/main/java/com/google/gson/JsonArray.java @@ -387,11 +387,20 @@ public final class JsonArray extends JsonElement implements IterableInterface representing a custom deserializer for Json. You should write a custom + *

Interface representing a custom deserializer for JSON. You should write a custom * deserializer, if you are not happy with the default deserialization done by Gson. You will * also need to register this deserializer through * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.

@@ -42,9 +42,9 @@ import java.lang.reflect.Type; *
* *

The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the - * Json string to be {"clazz":com.foo.MyObject,"value":20}. Suppose, you already know + * JSON string to be {"clazz":"com.foo.MyObject","value":20}. Suppose, you already know * the type of the field that the {@code Id} will be deserialized into, and hence just want to - * deserialize it from a Json string {@code 20}. You can achieve that by writing a custom + * deserialize it from a JSON string {@code 20}. You can achieve that by writing a custom * deserializer:

* *
diff --git a/gson/src/main/java/com/google/gson/JsonNull.java b/gson/src/main/java/com/google/gson/JsonNull.java
index 934dfc7d..b14fd3f1 100644
--- a/gson/src/main/java/com/google/gson/JsonNull.java
+++ b/gson/src/main/java/com/google/gson/JsonNull.java
@@ -17,7 +17,7 @@
 package com.google.gson;
 
 /**
- * A class representing a Json {@code null} value.
+ * A class representing a JSON {@code null} value.
  *
  * @author Inderjeet Singh
  * @author Joel Leitch
@@ -25,16 +25,16 @@ package com.google.gson;
  */
 public final class JsonNull extends JsonElement {
   /**
-   * Singleton for JsonNull
+   * Singleton for {@code JsonNull}.
    *
    * @since 1.8
    */
   public static final JsonNull INSTANCE = new JsonNull();
 
   /**
-   * Creates a new JsonNull object.
+   * Creates a new {@code JsonNull} object.
    *
-   * @deprecated Deprecated since Gson version 1.8. Use {@link #INSTANCE} instead
+   * @deprecated Deprecated since Gson version 1.8, use {@link #INSTANCE} instead.
    */
   @Deprecated
   public JsonNull() {
@@ -42,7 +42,8 @@ public final class JsonNull extends JsonElement {
   }
 
   /**
-   * Returns the same instance since it is an immutable value
+   * Returns the same instance since it is an immutable value.
+   *
    * @since 2.8.2
    */
   @Override
@@ -51,7 +52,7 @@ public final class JsonNull extends JsonElement {
   }
 
   /**
-   * All instances of JsonNull have the same hash code since they are indistinguishable
+   * All instances of {@code JsonNull} have the same hash code since they are indistinguishable.
    */
   @Override
   public int hashCode() {
@@ -59,7 +60,7 @@ public final class JsonNull extends JsonElement {
   }
 
   /**
-   * All instances of JsonNull are the same
+   * All instances of {@code JsonNull} are considered equal.
    */
   @Override
   public boolean equals(Object other) {
diff --git a/gson/src/main/java/com/google/gson/JsonObject.java b/gson/src/main/java/com/google/gson/JsonObject.java
index 0c3a6885..4b600510 100644
--- a/gson/src/main/java/com/google/gson/JsonObject.java
+++ b/gson/src/main/java/com/google/gson/JsonObject.java
@@ -41,7 +41,8 @@ public final class JsonObject extends JsonElement {
   }
 
   /**
-   * Creates a deep copy of this element and all its children
+   * Creates a deep copy of this element and all its children.
+   *
    * @since 2.8.2
    */
   @Override
@@ -55,7 +56,7 @@ public final class JsonObject extends JsonElement {
 
   /**
    * Adds a member, which is a name-value pair, to self. The name must be a String, but the value
-   * can be an arbitrary JsonElement, thereby allowing you to build a full tree of JsonElements
+   * can be an arbitrary {@link JsonElement}, thereby allowing you to build a full tree of JsonElements
    * rooted at this node.
    *
    * @param property name of the member.
@@ -66,10 +67,11 @@ public final class JsonObject extends JsonElement {
   }
 
   /**
-   * Removes the {@code property} from this {@link JsonObject}.
+   * Removes the {@code property} from this object.
    *
    * @param property name of the member that should be removed.
-   * @return the {@link JsonElement} object that is being removed.
+   * @return the {@link JsonElement} object that is being removed, or {@code null} if no
+   *   member with this name exists.
    * @since 1.3
    */
   public JsonElement remove(String property) {
@@ -77,8 +79,8 @@ public final class JsonObject extends JsonElement {
   }
 
   /**
-   * Convenience method to add a primitive member. The specified value is converted to a
-   * JsonPrimitive of String.
+   * Convenience method to add a string member. The specified value is converted to a
+   * {@link JsonPrimitive} of String.
    *
    * @param property name of the member.
    * @param value the string value associated with the member.
@@ -88,8 +90,8 @@ public final class JsonObject extends JsonElement {
   }
 
   /**
-   * Convenience method to add a primitive member. The specified value is converted to a
-   * JsonPrimitive of Number.
+   * Convenience method to add a number member. The specified value is converted to a
+   * {@link JsonPrimitive} of Number.
    *
    * @param property name of the member.
    * @param value the number value associated with the member.
@@ -100,10 +102,10 @@ public final class JsonObject extends JsonElement {
 
   /**
    * Convenience method to add a boolean member. The specified value is converted to a
-   * JsonPrimitive of Boolean.
+   * {@link JsonPrimitive} of Boolean.
    *
    * @param property name of the member.
-   * @param value the number value associated with the member.
+   * @param value the boolean value associated with the member.
    */
   public void addProperty(String property, Boolean value) {
     add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
@@ -111,10 +113,10 @@ public final class JsonObject extends JsonElement {
 
   /**
    * Convenience method to add a char member. The specified value is converted to a
-   * JsonPrimitive of Character.
+   * {@link JsonPrimitive} of Character.
    *
    * @param property name of the member.
-   * @param value the number value associated with the member.
+   * @param value the char value associated with the member.
    */
   public void addProperty(String property, Character value) {
     add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
@@ -163,48 +165,63 @@ public final class JsonObject extends JsonElement {
    * Returns the member with the specified name.
    *
    * @param memberName name of the member that is being requested.
-   * @return the member matching the name. Null if no such member exists.
+   * @return the member matching the name, or {@code null} if no such member exists.
    */
   public JsonElement get(String memberName) {
     return members.get(memberName);
   }
 
   /**
-   * Convenience method to get the specified member as a JsonPrimitive element.
+   * Convenience method to get the specified member as a {@link JsonPrimitive}.
    *
    * @param memberName name of the member being requested.
-   * @return the JsonPrimitive corresponding to the specified member.
+   * @return the {@code JsonPrimitive} corresponding to the specified member, or {@code null} if no
+   *   member with this name exists.
+   * @throws ClassCastException if the member is not of type {@code JsonPrimitive}.
    */
   public JsonPrimitive getAsJsonPrimitive(String memberName) {
     return (JsonPrimitive) members.get(memberName);
   }
 
   /**
-   * Convenience method to get the specified member as a JsonArray.
+   * Convenience method to get the specified member as a {@link JsonArray}.
    *
    * @param memberName name of the member being requested.
-   * @return the JsonArray corresponding to the specified member.
+   * @return the {@code JsonArray} corresponding to the specified member, or {@code null} if no
+   *   member with this name exists.
+   * @throws ClassCastException if the member is not of type {@code JsonArray}.
    */
   public JsonArray getAsJsonArray(String memberName) {
     return (JsonArray) members.get(memberName);
   }
 
   /**
-   * Convenience method to get the specified member as a JsonObject.
+   * Convenience method to get the specified member as a {@link JsonObject}.
    *
    * @param memberName name of the member being requested.
-   * @return the JsonObject corresponding to the specified member.
+   * @return the {@code JsonObject} corresponding to the specified member, or {@code null} if no
+   *   member with this name exists.
+   * @throws ClassCastException if the member is not of type {@code JsonObject}.
    */
   public JsonObject getAsJsonObject(String memberName) {
     return (JsonObject) members.get(memberName);
   }
 
+  /**
+   * Returns whether the other object is equal to this. This method only considers
+   * the other object to be equal if it is an instance of {@code JsonObject} and has
+   * equal members, ignoring order.
+   */
   @Override
   public boolean equals(Object o) {
     return (o == this) || (o instanceof JsonObject
         && ((JsonObject) o).members.equals(members));
   }
 
+  /**
+   * Returns the hash code of this object. This method calculates the hash code based
+   * on the members of this object, ignoring order.
+   */
   @Override
   public int hashCode() {
     return members.hashCode();
diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java
index f69ffd5f..92a8df15 100644
--- a/gson/src/main/java/com/google/gson/JsonPrimitive.java
+++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java
@@ -78,6 +78,7 @@ public final class JsonPrimitive extends JsonElement {
 
   /**
    * Returns the same value as primitives are immutable.
+   *
    * @since 2.8.2
    */
   @Override
@@ -243,6 +244,9 @@ public final class JsonPrimitive extends JsonElement {
     }
   }
 
+  /**
+   * Returns the hash code of this object.
+   */
   @Override
   public int hashCode() {
     if (value == null) {
@@ -260,6 +264,11 @@ public final class JsonPrimitive extends JsonElement {
     return value.hashCode();
   }
 
+  /**
+   * Returns whether the other object is equal to this. This method only considers
+   * the other object to be equal if it is an instance of {@code JsonPrimitive} and
+   * has an equal value.
+   */
   @Override
   public boolean equals(Object obj) {
     if (this == obj) {
diff --git a/gson/src/main/java/com/google/gson/JsonSerializer.java b/gson/src/main/java/com/google/gson/JsonSerializer.java
index 19eaf17d..9b3e1ed5 100644
--- a/gson/src/main/java/com/google/gson/JsonSerializer.java
+++ b/gson/src/main/java/com/google/gson/JsonSerializer.java
@@ -19,7 +19,7 @@ package com.google.gson;
 import java.lang.reflect.Type;
 
 /**
- * Interface representing a custom serializer for Json. You should write a custom serializer, if
+ * Interface representing a custom serializer for JSON. You should write a custom serializer, if
  * you are not happy with the default serialization done by Gson. You will also need to register
  * this serializer through {@link com.google.gson.GsonBuilder#registerTypeAdapter(Type, Object)}.
  *
@@ -43,7 +43,7 @@ import java.lang.reflect.Type;
  * 
* *

The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be - * {"clazz":com.foo.MyObject,"value":20}. Suppose, you just want the output to be + * {"clazz":"com.foo.MyObject","value":20}. Suppose, you just want the output to be * the value instead, which is {@code 20} in this case. You can achieve that by writing a custom * serializer:

* diff --git a/gson/src/test/java/com/google/gson/JsonObjectTest.java b/gson/src/test/java/com/google/gson/JsonObjectTest.java index 8ae573ab..d12d12d8 100644 --- a/gson/src/test/java/com/google/gson/JsonObjectTest.java +++ b/gson/src/test/java/com/google/gson/JsonObjectTest.java @@ -50,6 +50,8 @@ public class JsonObjectTest extends TestCase { assertEquals(value, removedElement); assertFalse(jsonObj.has(propertyName)); assertNull(jsonObj.get(propertyName)); + + assertNull(jsonObj.remove(propertyName)); } public void testAddingNullPropertyValue() throws Exception { @@ -170,6 +172,22 @@ public class JsonObjectTest extends TestCase { assertFalse(b.equals(a)); } + public void testEqualsHashCodeIgnoringOrder() { + JsonObject a = new JsonObject(); + JsonObject b = new JsonObject(); + + a.addProperty("1", true); + b.addProperty("2", false); + + a.addProperty("2", false); + b.addProperty("1", true); + + assertEquals(Arrays.asList("1", "2"), new ArrayList<>(a.keySet())); + assertEquals(Arrays.asList("2", "1"), new ArrayList<>(b.keySet())); + + MoreAsserts.assertEqualsAndHashCode(a, b); + } + public void testSize() { JsonObject o = new JsonObject(); assertEquals(0, o.size()); diff --git a/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java b/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java index ece35124..7b7dc303 100644 --- a/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java +++ b/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java @@ -8,6 +8,7 @@ import static org.junit.Assert.fail; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -111,8 +112,10 @@ public class ReflectionAccessTest { // But deserialization should fail Class internalClass = Collections.emptyList().getClass(); try { - gson.fromJson("{}", internalClass); + gson.fromJson("[]", internalClass); fail("Missing exception; test has to be run with `--illegal-access=deny`"); + } catch (JsonSyntaxException e) { + fail("Unexpected exception; test has to be run with `--illegal-access=deny`"); } catch (JsonIOException expected) { assertTrue(expected.getMessage().startsWith( "Failed making constructor 'java.util.Collections$EmptyList#EmptyList()' accessible; "