diff options
author | Marcono1234 <Marcono1234@users.noreply.github.com> | 2022-09-09 16:32:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-09 07:32:55 -0700 |
commit | 847d7f66380e6bd54eedcdead2a95d4037ed87ee (patch) | |
tree | 373f2bd4f6428c0bd72ba5894756f42fe7db7c14 | |
parent | 2266ccdd670b1e914c18aa5928ddd24c93deeed4 (diff) | |
download | gson-847d7f66380e6bd54eedcdead2a95d4037ed87ee.tar.gz |
Improve documentation (#2193)
* Improve JsonElement subclasses javadoc and add tests
* Slightly improve JsonSerializer and JsonDeserializer javadoc
* Improve ReflectionAccessTest failure message
* Improve documentation regarding field and class exclusion
11 files changed, 133 insertions, 43 deletions
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. ### <a name="TOC-Nested-Classes-including-Inner-Classes-"></a>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. * * <p>The following are a few examples that shows how you can use this exclusion mechanism. * @@ -64,7 +61,7 @@ package com.google.gson; * * <p>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: * <pre class="code"> * 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. + * + * <p>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. * + * <p>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. + * + * <p>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. + * + * <p>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. * + * <p>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.<br> + * 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. * + * <p>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. * + * <p>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 Iterable<JsonElement return getAsSingleElement().getAsBoolean(); } + /** + * 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 JsonArray} and has + * equal elements in the same order. + */ @Override public boolean equals(Object o) { return (o == this) || (o instanceof JsonArray && ((JsonArray) o).elements.equals(elements)); } + /** + * Returns the hash code of this array. This method calculates the hash code based + * on the elements of this array. + */ @Override public int hashCode() { return elements.hashCode(); diff --git a/gson/src/main/java/com/google/gson/JsonDeserializer.java b/gson/src/main/java/com/google/gson/JsonDeserializer.java index 0589eb28..46550edf 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializer.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializer.java @@ -19,7 +19,7 @@ package com.google.gson; import java.lang.reflect.Type; /** - * <p>Interface representing a custom deserializer for Json. You should write a custom + * <p>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)}.</p> @@ -42,9 +42,9 @@ import java.lang.reflect.Type; * </pre> * * <p>The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the - * Json string to be <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you already know + * JSON string to be <code>{"clazz":"com.foo.MyObject","value":20}</code>. 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:</p> * * <pre> 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; * </pre> * * <p>The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be - * <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you just want the output to be + * <code>{"clazz":"com.foo.MyObject","value":20}</code>. 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:</p> * 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; " |