Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package com.google.gson; | ||
|
||
import com.google.gson.internal.reflect.ReflectionHelper; | ||
import com.google.gson.reflect.TypeToken; | ||
import java.lang.reflect.Field; | ||
|
||
/** | ||
* A strategy defining how to handle missing field values during reflection-based deserialization. | ||
* | ||
* @see GsonBuilder#setMissingFieldValueStrategy(MissingFieldValueStrategy) | ||
* @since $next-version$ | ||
*/ | ||
public interface MissingFieldValueStrategy { | ||
/** | ||
* This strategy does nothing when a missing field is detected, it preserves the initial field | ||
* value, if any. | ||
* | ||
* <p>This is the default missing field value strategy. | ||
*/ | ||
MissingFieldValueStrategy DO_NOTHING = new MissingFieldValueStrategy() { | ||
@Override | ||
public Object handleMissingField(TypeToken<?> declaringType, Object instance, Field field, TypeToken<?> resolvedFieldType) { | ||
// Preserve initial field value | ||
return null; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "MissingFieldValueStrategy.DO_NOTHING"; | ||
} | ||
}; | ||
|
||
/** | ||
* This strategy throws an exception when a missing field is detected. | ||
*/ | ||
MissingFieldValueStrategy THROW_EXCEPTION = new MissingFieldValueStrategy() { | ||
@Override | ||
public Object handleMissingField(TypeToken<?> declaringType, Object instance, Field field, TypeToken<?> resolvedFieldType) { | ||
// TODO: Proper exception | ||
throw new RuntimeException("Missing value for field '" + ReflectionHelper.fieldToString(field) + "'"); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "MissingFieldValueStrategy.THROW_EXCEPTION"; | ||
} | ||
}; | ||
|
||
/** | ||
* Called when a missing field value is detected. Implementations can either throw an exception or | ||
* return a default value. | ||
* | ||
* <p>Returning {@code null} will keep the initial field value, if any. For example when returning | ||
* {@code null} for the field {@code String f = "default"}, the field will still have the value | ||
* {@code "default"} afterwards (assuming the constructor of the class was called, see also | ||
* {@link GsonBuilder#disableJdkUnsafe()}). The type of the returned value has to match the | ||
* type of the field, no narrowing or widening numeric conversion is performed. | ||
* | ||
* <p>The {@code instance} represents an instance of the declaring type with the so far already | ||
* deserialized fields. It is intended to be used for looking up existing field values to derive | ||
* the missing field value from them. Manipulating {@code instance} in any way is not recommended.<br> | ||
* For Record classes (Java 16 feature) the {@code instance} is {@code null}. | ||
* | ||
* <p>{@code resolvedFieldType} is the type of the field with type variables being resolved, if | ||
* possible. For example if {@code class MyClass<T>} has a field {@code T myField} and | ||
* {@code MyClass<String>} is deserialized, then {@code resolvedFieldType} will be {@code String}. | ||
* | ||
* @param declaringType type declaring the field | ||
* @param instance instance of the declaring type, {@code null} for Record classes | ||
* @param field field whose value is missing | ||
* @param resolvedFieldType resolved type of the field | ||
* @return the field value, or {@code null} | ||
*/ | ||
// TODO: Should this really expose `instance`? Only use case would be to derive value from other fields | ||
// but besides that user should not directly manipulate `instance` but return new value instead | ||
Object handleMissingField(TypeToken<?> declaringType, Object instance, Field field, TypeToken<?> resolvedFieldType); | ||
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'declaringType' is never used.
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'instance' is never used.
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'resolvedFieldType' is never used.
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package com.google.gson; | ||
|
||
import com.google.gson.reflect.TypeToken; | ||
import com.google.gson.stream.JsonReader; | ||
import java.io.IOException; | ||
|
||
/** | ||
* A strategy defining how to handle unknown fields during reflection-based deserialization. | ||
* | ||
* @see GsonBuilder#setUnknownFieldStrategy(UnknownFieldStrategy) | ||
* @since $next-version$ | ||
*/ | ||
public interface UnknownFieldStrategy { | ||
/** | ||
* This strategy ignores the unknown field. | ||
* | ||
* <p>This is the default unknown field strategy. | ||
*/ | ||
UnknownFieldStrategy IGNORE = new UnknownFieldStrategy() { | ||
@Override | ||
public void handleUnknownField(TypeToken<?> declaringType, Object instance, String fieldName, | ||
JsonReader jsonReader, Gson gson) throws IOException { | ||
jsonReader.skipValue(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "UnknownFieldStrategy.IGNORE"; | ||
} | ||
}; | ||
|
||
/** | ||
* This strategy throws an exception when an unknown field is encountered. | ||
* | ||
* <p><b>Note:</b> Be careful when using this strategy; while it might sound tempting | ||
* to strictly validate that the JSON data matches the expected format, this strategy | ||
* makes it difficult to add new fields to the JSON structure in a backward compatible way. | ||
* Usually it suffices to use only {@link MissingFieldValueStrategy#THROW_EXCEPTION} for | ||
* validation and to ignore unknown fields. | ||
*/ | ||
UnknownFieldStrategy THROW_EXCEPTION = new UnknownFieldStrategy() { | ||
@Override | ||
public void handleUnknownField(TypeToken<?> declaringType, Object instance, String fieldName, | ||
JsonReader jsonReader, Gson gson) throws IOException { | ||
// TODO: Proper exception | ||
throw new RuntimeException("Unknown field '" + fieldName + "' for " + declaringType.getRawType() + " at path " + jsonReader.getPath()); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "UnknownFieldStrategy.THROW_EXCEPTION"; | ||
} | ||
}; | ||
|
||
/** | ||
* Called when an unknown field is encountered. Implementations can throw an exception, | ||
* store the field value in {@code instance} or ignore the unknown field. | ||
* | ||
* <p>The {@code jsonReader} is positioned to read the value of the unknown field. If an | ||
* implementation of this method does not throw an exception it must consume the value, either | ||
* by reading it with methods like {@link JsonReader#nextString()} (possibly after peeking | ||
* at the value type first), or by skipping it with {@link JsonReader#skipValue()}.<br> | ||
* The {@code gson} object can be used to read from the {@code jsonReader}. It is the same | ||
* instance which was originally used to perform the deserialization. | ||
* | ||
* <p>The {@code instance} represents an instance of the declaring type with the so far already | ||
* deserialized fields. It can be used to store the value of the unknown field, for example | ||
* if it declares a {@code transient Map<String, Object>} field for all unknown values.<br> | ||
* For Record classes (Java 16 feature) the {@code instance} is {@code null}. | ||
* | ||
* @param declaringType type declaring the field | ||
* @param instance instance of the declaring type, {@code null} for Record classes | ||
* @param fieldName name of the unknown field | ||
* @param jsonReader reader to be used to read or skip the field value | ||
* @param gson {@code Gson} instance which can be used to read the field value from {@code jsonReader} | ||
* @throws IOException if reading or skipping the field value fails | ||
*/ | ||
void handleUnknownField(TypeToken<?> declaringType, Object instance, String fieldName, JsonReader jsonReader, Gson gson) throws IOException; | ||
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'instance' is never used.
Check notice Code scanning / CodeQL Useless parameter Note
The parameter 'gson' is never used.
|
||
} |