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
This commit is contained in:
Marcono1234 2022-09-09 16:32:55 +02:00 committed by GitHub
parent 2266ccdd67
commit 847d7f6638
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 133 additions and 43 deletions

View File

@ -155,7 +155,8 @@ BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
* While serializing, a null field is omitted from the output. * 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. * 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. * 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) ### <a name="TOC-Nested-Classes-including-Inner-Classes-"></a>Nested Classes (including Inner Classes)

View File

@ -17,11 +17,8 @@
package com.google.gson; package com.google.gson;
/** /**
* A strategy (or policy) definition that is used to decide whether or not a field or top-level * 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. For serialization, * class should be serialized or deserialized as part of the JSON output/input.
* 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.
* *
* <p>The following are a few examples that shows how you can use this exclusion mechanism. * <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 * <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 * 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"> * <pre class="code">
* ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class); * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
* Gson gson = new GsonBuilder() * Gson gson = new GsonBuilder()

View File

@ -156,8 +156,11 @@ public final class GsonBuilder {
/** /**
* Configures Gson to excludes all class fields that have the specified modifiers. By default, * 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 * Gson will exclude all fields marked {@code transient} or {@code static}. This method will
* behavior. * 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 * @param modifiers the field modifiers. You must use the modifiers specified in the
* {@link java.lang.reflect.Modifier} class. For example, * {@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. * 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 * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/ */
public GsonBuilder excludeFieldsWithoutExposeAnnotation() { 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 * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3 * @since 1.3
@ -369,6 +388,16 @@ public final class GsonBuilder {
* The strategies are added to the existing strategies (if any); the existing strategies * The strategies are added to the existing strategies (if any); the existing strategies
* are not replaced. * 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. * @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 * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.4 * @since 1.4
@ -389,6 +418,9 @@ public final class GsonBuilder {
* class) should be skipped then that field (or object) is skipped during its * class) should be skipped then that field (or object) is skipped during its
* serialization. * 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. * @param strategy an exclusion strategy to apply during serialization.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.7 * @since 1.7
@ -407,6 +439,9 @@ public final class GsonBuilder {
* class) should be skipped then that field (or object) is skipped during its * class) should be skipped then that field (or object) is skipped during its
* deserialization. * 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. * @param strategy an exclusion strategy to apply during deserialization.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.7 * @since 1.7

View File

@ -387,11 +387,20 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
return getAsSingleElement().getAsBoolean(); 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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return (o == this) || (o instanceof JsonArray && ((JsonArray) o).elements.equals(elements)); 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 @Override
public int hashCode() { public int hashCode() {
return elements.hashCode(); return elements.hashCode();

View File

@ -19,7 +19,7 @@ package com.google.gson;
import java.lang.reflect.Type; 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 * deserializer, if you are not happy with the default deserialization done by Gson. You will
* also need to register this deserializer through * also need to register this deserializer through
* {@link GsonBuilder#registerTypeAdapter(Type, Object)}.</p> * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.</p>
@ -42,9 +42,9 @@ import java.lang.reflect.Type;
* </pre> * </pre>
* *
* <p>The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the * <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 * 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> * deserializer:</p>
* *
* <pre> * <pre>

View File

@ -17,7 +17,7 @@
package com.google.gson; package com.google.gson;
/** /**
* A class representing a Json {@code null} value. * A class representing a JSON {@code null} value.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
@ -25,16 +25,16 @@ package com.google.gson;
*/ */
public final class JsonNull extends JsonElement { public final class JsonNull extends JsonElement {
/** /**
* Singleton for JsonNull * Singleton for {@code JsonNull}.
* *
* @since 1.8 * @since 1.8
*/ */
public static final JsonNull INSTANCE = new JsonNull(); 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 @Deprecated
public JsonNull() { 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 * @since 2.8.2
*/ */
@Override @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 @Override
public int hashCode() { 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 @Override
public boolean equals(Object other) { public boolean equals(Object other) {

View File

@ -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 * @since 2.8.2
*/ */
@Override @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 * 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. * rooted at this node.
* *
* @param property name of the member. * @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. * @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 * @since 1.3
*/ */
public JsonElement remove(String property) { 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 * Convenience method to add a string member. The specified value is converted to a
* JsonPrimitive of String. * {@link JsonPrimitive} of String.
* *
* @param property name of the member. * @param property name of the member.
* @param value the string value associated with 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 * Convenience method to add a number member. The specified value is converted to a
* JsonPrimitive of Number. * {@link JsonPrimitive} of Number.
* *
* @param property name of the member. * @param property name of the member.
* @param value the number value associated with 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 * 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 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) { public void addProperty(String property, Boolean value) {
add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(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 * 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 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) { public void addProperty(String property, Character value) {
add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(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. * Returns the member with the specified name.
* *
* @param memberName name of the member that is being requested. * @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) { public JsonElement get(String memberName) {
return members.get(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. * @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) { public JsonPrimitive getAsJsonPrimitive(String memberName) {
return (JsonPrimitive) members.get(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. * @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) { public JsonArray getAsJsonArray(String memberName) {
return (JsonArray) members.get(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. * @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) { public JsonObject getAsJsonObject(String memberName) {
return (JsonObject) members.get(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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return (o == this) || (o instanceof JsonObject return (o == this) || (o instanceof JsonObject
&& ((JsonObject) o).members.equals(members)); && ((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 @Override
public int hashCode() { public int hashCode() {
return members.hashCode(); return members.hashCode();

View File

@ -78,6 +78,7 @@ public final class JsonPrimitive extends JsonElement {
/** /**
* Returns the same value as primitives are immutable. * Returns the same value as primitives are immutable.
*
* @since 2.8.2 * @since 2.8.2
*/ */
@Override @Override
@ -243,6 +244,9 @@ public final class JsonPrimitive extends JsonElement {
} }
} }
/**
* Returns the hash code of this object.
*/
@Override @Override
public int hashCode() { public int hashCode() {
if (value == null) { if (value == null) {
@ -260,6 +264,11 @@ public final class JsonPrimitive extends JsonElement {
return value.hashCode(); 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 @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) { if (this == obj) {

View File

@ -19,7 +19,7 @@ package com.google.gson;
import java.lang.reflect.Type; 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 * 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)}. * this serializer through {@link com.google.gson.GsonBuilder#registerTypeAdapter(Type, Object)}.
* *
@ -43,7 +43,7 @@ import java.lang.reflect.Type;
* </pre> * </pre>
* *
* <p>The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be * <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 * the value instead, which is {@code 20} in this case. You can achieve that by writing a custom
* serializer:</p> * serializer:</p>
* *

View File

@ -50,6 +50,8 @@ public class JsonObjectTest extends TestCase {
assertEquals(value, removedElement); assertEquals(value, removedElement);
assertFalse(jsonObj.has(propertyName)); assertFalse(jsonObj.has(propertyName));
assertNull(jsonObj.get(propertyName)); assertNull(jsonObj.get(propertyName));
assertNull(jsonObj.remove(propertyName));
} }
public void testAddingNullPropertyValue() throws Exception { public void testAddingNullPropertyValue() throws Exception {
@ -170,6 +172,22 @@ public class JsonObjectTest extends TestCase {
assertFalse(b.equals(a)); 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() { public void testSize() {
JsonObject o = new JsonObject(); JsonObject o = new JsonObject();
assertEquals(0, o.size()); assertEquals(0, o.size());

View File

@ -8,6 +8,7 @@ import static org.junit.Assert.fail;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException; import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
@ -111,8 +112,10 @@ public class ReflectionAccessTest {
// But deserialization should fail // But deserialization should fail
Class<?> internalClass = Collections.emptyList().getClass(); Class<?> internalClass = Collections.emptyList().getClass();
try { try {
gson.fromJson("{}", internalClass); gson.fromJson("[]", internalClass);
fail("Missing exception; test has to be run with `--illegal-access=deny`"); 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) { } catch (JsonIOException expected) {
assertTrue(expected.getMessage().startsWith( assertTrue(expected.getMessage().startsWith(
"Failed making constructor 'java.util.Collections$EmptyList#EmptyList()' accessible; " "Failed making constructor 'java.util.Collections$EmptyList#EmptyList()' accessible; "