Add Gson support to unsafe deserialization query

This commit is contained in:
Chris Smowton
2021-08-10 15:20:11 +01:00
parent 6b4ca31783
commit cd2c9e9ca3
21 changed files with 929 additions and 3 deletions

View File

@@ -77,6 +77,8 @@ private import FlowSummary
*/
private module Frameworks {
private import internal.ContainerFlow
private import semmle.code.java.frameworks.android.Android
private import semmle.code.java.frameworks.android.Intent
private import semmle.code.java.frameworks.android.XssSinks
private import semmle.code.java.frameworks.android.Intent
private import semmle.code.java.frameworks.ApacheHttp

View File

@@ -5,6 +5,7 @@
import java
import semmle.code.java.dataflow.ExternalFlow
import semmle.code.xml.AndroidManifest
private import semmle.code.java.dataflow.ExternalFlow
/**
* Gets a transitive superType avoiding magic optimisation
@@ -202,3 +203,59 @@ private class ContentProviderSourceModels extends SourceModelCsv {
]
}
}
/** Interface for classes whose instances can be written to and restored from a Parcel. */
class TypeParcelable extends Interface {
TypeParcelable() { this.hasQualifiedName("android.os", "Parcelable") }
}
/**
* A method that overrides `android.os.Parcelable.Creator.createFromParcel`.
*/
class CreateFromParcelMethod extends Method {
CreateFromParcelMethod() {
this.hasName("createFromParcel") and
this.getEnclosingCallable().getDeclaringType().getASupertype*() instanceof TypeParcelable
}
}
private class TaintPropagationModels extends SummaryModelCsv {
override predicate row(string s) {
// BaseBundle getters
s =
"android.os;BaseBundle;true;get" + ["Boolean", "Double", "Int", "Long", "String"] +
["", "Array"] + ";;;Argument[-1];ReturnValue;taint"
or
// Bundle getters
s =
"android.os;Bundle;true;get" +
[
"Binder", "Bundle", "Byte", "ByteArray", "Char", "CharArray", "CharSequence",
"CharSequenceArray", "CharSequenceArrayList", "Float", "FloatArray", "IntegerArrayList",
"Parcelable", "ParcelableArray", "ParcelableArrayList", "Serializable", "Short",
"ShortArray", "Size", "SizeF", "SparseParcelableArray", "StringArrayList"
] + ";;;Argument[-1];ReturnValue;taint"
or
// Intent readers that return their value
s =
"android.os;Parcel;false;read" +
[
"Array", "ArrayList", "Boolean", "Bundle", "Byte", "Double", "FileDescriptor", "Float",
"HashMap", "Int", "Long", "Parcelable", "ParcelableArray", "PersistableBundle",
"Serializable", "Size", "SizeF", "SparseArray", "SparseBolleanArray", "String",
"StrongBinder", "TypedObject", "Value"
] + ";;;Argument[-1];ReturnValue;taint"
or
// Intent readers that write to an existing object
s =
"android.os;Parcel;false;read" +
[
"BinderArray", "BinderList", "BooleanArray", "ByteArray", "CharArray", "DoubleArray",
"FloatArray", "IntArray", "List", "LongArray", "Map", "ParcelableList", "StringArray",
"StringList", "TypedArray", "TypedList"
] + ";;;Argument[-1];Argument[0];taint"
or
// One Intent method that aliases an argument to a return value
s = "android.os;Parcel;false;readParcelableList;;;Argument[0];ReturnValue;value"
}
}

View File

@@ -3,32 +3,53 @@ private import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSteps
import semmle.code.java.dataflow.ExternalFlow
/**
* The class `android.content.Intent`.
*/
class TypeIntent extends Class {
TypeIntent() { hasQualifiedName("android.content", "Intent") }
}
/**
* The class `android.app.Activity`.
*/
class TypeActivity extends Class {
TypeActivity() { hasQualifiedName("android.app", "Activity") }
}
/**
* The class `android.content.Context`.
*/
class TypeContext extends RefType {
TypeContext() { hasQualifiedName("android.content", "Context") }
}
/**
* The class `android.content.BroadcastReceiver`.
*/
class TypeBroadcastReceiver extends Class {
TypeBroadcastReceiver() { hasQualifiedName("android.content", "BroadcastReceiver") }
}
/**
* The method `Activity.getIntent`
*/
class AndroidGetIntentMethod extends Method {
AndroidGetIntentMethod() { hasName("getIntent") and getDeclaringType() instanceof TypeActivity }
}
/**
* The method `BroadcastReceiver.onReceive`.
*/
class AndroidReceiveIntentMethod extends Method {
AndroidReceiveIntentMethod() {
hasName("onReceive") and getDeclaringType() instanceof TypeBroadcastReceiver
}
}
/**
* The method `Context.startActivity` or `startActivities`.
*/
class ContextStartActivityMethod extends Method {
ContextStartActivityMethod() {
(hasName("startActivity") or hasName("startActivities")) and
@@ -44,6 +65,16 @@ private class IntentFieldsInheritTaint extends DataFlow::SyntheticFieldContent,
IntentFieldsInheritTaint() { this.getField().matches("android.content.Intent.%") }
}
/**
* The method `Intent.getParcelableExtra`.
*/
class IntentGetParcelableExtraMethod extends Method {
IntentGetParcelableExtraMethod() {
hasName("getParcelableExtra") and
getDeclaringType() instanceof TypeIntent
}
}
private class IntentBundleFlowSteps extends SummaryModelCsv {
override predicate row(string row) {
row =

View File

@@ -0,0 +1,35 @@
/**
* Provides classes for working with the Gson framework.
*/
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.frameworks.android.Android
import semmle.code.java.frameworks.android.Intent
/** The class `com.google.gson.Gson`. */
class Gson extends RefType {
Gson() { this.hasQualifiedName("com.google.gson", "Gson") }
}
/** The `fromJson` deserialization method. */
class GsonDeserializeMethod extends Method {
GsonDeserializeMethod() {
this.getDeclaringType() instanceof Gson and
this.hasName("fromJson")
}
}
/**
* Holds if `intentNode` is an `Intent` used in the context `(T)intentNode.getParcelableExtra(...)` and
* `parcelNode` is the corresponding parameter of `Parcelable.Creator<T> { public T createFromParcel(Parcel parcelNode) { }`.
*/
predicate intentFlowsToParcel(DataFlow::Node intentNode, DataFlow::Node parcelNode) {
exists(MethodAccess getParcelableExtraCall, CreateFromParcelMethod cfpm, Type createdType |
intentNode.asExpr() = getParcelableExtraCall.getQualifier() and
getParcelableExtraCall.getMethod() instanceof IntentGetParcelableExtraMethod and
DataFlow::localExprFlow(getParcelableExtraCall, any(Expr e | e.getType() = createdType)) and
parcelNode.asParameter() = cfpm.getParameter(0) and
cfpm.getReturnType() = createdType
)
}

View File

@@ -17,6 +17,7 @@ private import semmle.code.java.frameworks.Jackson
private import semmle.code.java.frameworks.Jabsorb
private import semmle.code.java.frameworks.JoddJson
private import semmle.code.java.frameworks.Flexjson
private import semmle.code.java.frameworks.google.Gson
private import semmle.code.java.frameworks.apache.Lang
private import semmle.code.java.Reflection
@@ -207,6 +208,10 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
or
m instanceof FlexjsonDeserializeMethod and
sink = ma.getArgument(0)
or
m instanceof GsonDeserializeMethod and
sink = ma.getArgument(0) and
any(UnsafeTypeConfig config).hasFlowToExpr(ma.getArgument(1))
)
}
@@ -249,6 +254,8 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration {
createJacksonJsonParserStep(pred, succ)
or
createJacksonTreeNodeStep(pred, succ)
or
intentFlowsToParcel(pred, succ)
}
override predicate isSanitizer(DataFlow::Node node) {
@@ -362,9 +369,15 @@ class UnsafeTypeConfig extends TaintTracking2::Configuration {
ma.getMethod() instanceof JabsorbUnmarshallMethod
or
ma.getMethod() instanceof JoddJsonParseMethod
or
ma.getMethod() instanceof GsonDeserializeMethod
) and
// Note `JacksonTypeDescriptorType` includes plain old `java.lang.Class`
arg.getType() instanceof JacksonTypeDescriptorType and
(
arg.getType() instanceof JacksonTypeDescriptorType
or
arg.getType().(RefType).hasQualifiedName("java.lang.reflect", "Type")
) and
arg = sink.asExpr()
)
}
@@ -375,7 +388,8 @@ class UnsafeTypeConfig extends TaintTracking2::Configuration {
*/
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
resolveClassStep(fromNode, toNode) or
looksLikeResolveClassStep(fromNode, toNode)
looksLikeResolveClassStep(fromNode, toNode) or
intentFlowsToParcel(fromNode, toNode)
}
}

View File

@@ -0,0 +1,24 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app"
android:installLocation="auto"
android:versionCode="1"
android:versionName="0.1" >
<uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".GsonActivity"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,17 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.Gson;
public class GsonActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
ParcelableEntity entity = (ParcelableEntity) getIntent().getParcelableExtra("jsonEntity");
}
}

View File

@@ -0,0 +1,77 @@
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.typeadapters.RuntimeTypeAdapterFactory;
import com.example.User;
import com.thirdparty.Person;
public class GsonServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
// GOOD: concrete class type specified
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
Gson gson = new Gson();
Object obj = gson.fromJson(json, User.class);
}
@Override
// GOOD: concrete class type specified
public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
Gson gson = new Gson();
Object obj = gson.fromJson(json, Person.class);
}
@Override
// BAD: allow class name to be controlled by remote source
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
try {
Gson gson = new Gson();
Object obj = gson.fromJson(json, Class.forName(clazz)); // $unsafeDeserialization
} catch (ClassNotFoundException cne) {
throw new IOException(cne.getMessage());
}
}
@Override
// BAD: allow class name to be controlled by remote source even with a type adapter factory
public void doHead(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
try {
RuntimeTypeAdapterFactory<User> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(User.class, "type");
Gson gson = new GsonBuilder().registerTypeAdapterFactory(runtimeTypeAdapterFactory).create();
Object obj = gson.fromJson(json, Class.forName(clazz)); // $unsafeDeserialization
} catch (ClassNotFoundException cne) {
throw new IOException(cne.getMessage());
}
}
@Override
// GOOD: specify allowed class types without explicitly configured vulnerable subclass types
public void doTrace(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
RuntimeTypeAdapterFactory<Person> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Person.class, "type");
Gson gson = new GsonBuilder().registerTypeAdapterFactory(runtimeTypeAdapterFactory).create();
Person obj = gson.fromJson(json, Person.class);
}
}

View File

@@ -0,0 +1,42 @@
package com.example.app;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class ParcelableEntity implements Parcelable {
private static final Gson GSON = new GsonBuilder().create();
public ParcelableEntity(Object obj) {
this.obj = obj;
}
private Object obj;
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(obj.getClass().getName());
parcel.writeString(GSON.toJson(obj));
}
public static final Parcelable.Creator CREATOR = new Creator<ParcelableEntity>() {
@Override
public ParcelableEntity createFromParcel(Parcel parcel) {
try {
Class clazz = Class.forName(parcel.readString());
Object obj = GSON.fromJson(parcel.readString(), clazz); // $unsafeDeserialization
return new ParcelableEntity(obj);
}
catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public ParcelableEntity[] newArray(int size) {
return new ParcelableEntity[size];
}
};
}

View File

@@ -1 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3:${testdir}/../../../stubs/flexjson-2.1
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3:${testdir}/../../../stubs/flexjson-2.1:${testdir}/../../../stubs/gson-2.8.6:${testdir}/../../../stubs/google-android-9.0.0

View File

@@ -348,6 +348,18 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
return null;
}
/**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a Parcelable value, or null
*/
public <T extends Parcelable> T getParcelable(String key) {
return null;
}
/**
* Returns the value associated with the given key, or null if no mapping of the
* desired type exists for the given key or a null value is explicitly

View File

@@ -66,6 +66,31 @@ public interface Parcelable {
*/
public void writeToParcel(Parcel dest, int flags);
/**
* Interface that must be implemented and provided as a public CREATOR
* field that generates instances of your Parcelable class from a Parcel.
*/
public interface Creator<T> {
/**
* Create a new instance of the Parcelable class, instantiating it
* from the given Parcel whose data had previously been written by
* {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
*
* @param source The Parcel to read the object's data from.
* @return Returns a new instance of the Parcelable class.
*/
public T createFromParcel(Parcel source);
/**
* Create a new array of the Parcelable class.
*
* @param size Size of the array.
* @return Returns an array of the Parcelable class, with every entry
* initialized to null.
*/
public T[] newArray(int size);
}
/**
* Specialization of {@link Creator} that allows you to receive the ClassLoader
* the object is being created in.

View File

@@ -1,7 +1,38 @@
package com.google.gson;
import java.lang.reflect.Type;
import java.io.Reader;
import com.google.gson.stream.JsonReader;
public final class Gson {
public Gson() {
}
public String toJson(Object src) {
return null;
}
public String toJson(Object src, Type typeOfSrc) {
return null;
}
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
return null;
}
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
return null;
}
public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
return null;
}
public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
return null;
}
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
return null;
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;
import java.lang.reflect.Type;
public final class GsonBuilder {
/**
* Creates a GsonBuilder instance that can be used to build Gson with various configuration
* settings. GsonBuilder follows the builder pattern, and it is typically used by first
* invoking various configuration methods to set desired options, and finally calling
* {@link #create()}.
*/
public GsonBuilder() {
}
/**
* Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder
* has the same configuration as the previously built Gson instance.
*
* @param gson the gson instance whose configuration should by applied to a new GsonBuilder.
*/
GsonBuilder(Gson gson) {
}
/**
* Configures Gson for custom serialization or deserialization. This method combines the
* registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a
* {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
* all the required interfaces for custom serialization with Gson. If a type adapter was
* previously registered for the specified {@code type}, it is overwritten.
*
* <p>This registers the type specified and no other types: you must manually register related
* types! For example, applications registering {@code boolean.class} should also register {@code
* Boolean.class}.
*
* @param type the type definition for the type adapter being registered
* @param typeAdapter This object must implement at least one of the {@link TypeAdapter},
* {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
return null;
}
/**
* Register a factory for type adapters. Registering a factory is useful when the type
* adapter needs to be configured based on the type of the field being processed. Gson
* is designed to handle a large number of factories, so you should consider registering
* them to be at par with registering an individual type adapter.
*
* @since 2.1
*/
public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
return null;
}
/**
* Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
* This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and
* a {@link JsonDeserializer}. If a type adapter was previously registered for the specified
* type hierarchy, it is overridden. If a type adapter is registered for a specific type in
* the type hierarchy, it will be invoked instead of the one registered for the type hierarchy.
*
* @param baseType the class definition for the type adapter being registered for the base class
* or interface
* @param typeAdapter This object must implement at least one of {@link TypeAdapter},
* {@link JsonSerializer} or {@link JsonDeserializer} interfaces.
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.7
*/
public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) {
return null;
}
/**
* Creates a {@link Gson} instance based on the current configuration. This method is free of
* side-effects to this {@code GsonBuilder} instance and hence can be called multiple times.
*
* @return an instance of Gson configured with the options currently set in this builder
*/
public Gson create() {
return null;
}
}

View File

@@ -0,0 +1,4 @@
package com.google.gson;
public final class JsonIOException extends RuntimeException {
}

View File

@@ -0,0 +1,4 @@
package com.google.gson;
public final class JsonSyntaxException extends RuntimeException {
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;
import com.google.gson.stream.JsonReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
public abstract class TypeAdapter<T> {
/**
* Converts {@code value} to a JSON document and writes it to {@code out}.
* Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson}
* method, this write is strict. Create a {@link
* JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
* {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
* writing.
*
* @param value the Java object to convert. May be null.
* @since 2.2
*/
public final void toJson(Writer out, T value) throws IOException {
}
/**
* This wrapper method is used to make a type adapter null tolerant. In general, a
* type adapter is required to handle nulls in write and read methods. Here is how this
* is typically done:<br>
* <pre> {@code
*
* Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
* new TypeAdapter<Foo>() {
* public Foo read(JsonReader in) throws IOException {
* if (in.peek() == JsonToken.NULL) {
* in.nextNull();
* return null;
* }
* // read a Foo from in and return it
* }
* public void write(JsonWriter out, Foo src) throws IOException {
* if (src == null) {
* out.nullValue();
* return;
* }
* // write src as JSON to out
* }
* }).create();
* }</pre>
* You can avoid this boilerplate handling of nulls by wrapping your type adapter with
* this method. Here is how we will rewrite the above example:
* <pre> {@code
*
* Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
* new TypeAdapter<Foo>() {
* public Foo read(JsonReader in) throws IOException {
* // read a Foo from in and return it
* }
* public void write(JsonWriter out, Foo src) throws IOException {
* // write src as JSON to out
* }
* }.nullSafe()).create();
* }</pre>
* Note that we didn't need to check for nulls in our type adapter after we used nullSafe.
*/
public final TypeAdapter<T> nullSafe() {
return null;
}
/**
* Converts {@code value} to a JSON document. Unlike Gson's similar {@link
* Gson#toJson(Object) toJson} method, this write is strict. Create a {@link
* JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
* {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
* writing.
*
* @param value the Java object to convert. May be null.
* @since 2.2
*/
public final String toJson(T value) {
return null;
}
/**
* Reads one JSON value (an array, object, string, number, boolean or null)
* and converts it to a Java object. Returns the converted object.
*
* @return the converted Java object. May be null.
*/
public abstract T read(JsonReader in) throws IOException;
/**
* Converts the JSON document in {@code in} to a Java object. Unlike Gson's
* similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this
* read is strict. Create a {@link JsonReader#setLenient(boolean) lenient}
* {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading.
*
* @return the converted Java object. May be null.
* @since 2.2
*/
public final T fromJson(Reader in) throws IOException {
return null;
}
/**
* Converts the JSON document in {@code json} to a Java object. Unlike Gson's
* similar {@link Gson#fromJson(String, Class) fromJson} method, this read is
* strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code
* JsonReader} and call {@link #read(JsonReader)} for lenient reading.
*
* @return the converted Java object. May be null.
* @since 2.2
*/
public final T fromJson(String json) throws IOException {
return null;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;
import com.google.gson.reflect.TypeToken;
public interface TypeAdapterFactory {
/**
* Returns a type adapter for {@code type}, or null if this factory doesn't
* support {@code type}.
*/
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.reflect;
/**
* Represents a generic type {@code T}. Java doesn't yet provide a way to
* represent generic types, so this class does. Forces clients to create a
* subclass of this class which enables retrieval the type information even at
* runtime.
*
* <p>For example, to create a type literal for {@code List<String>}, you can
* create an empty anonymous inner class:
*
* <p>
* {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
*
* <p>This syntax cannot be used to create type literals that have wildcard
* parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}.
*
* @author Bob Lee
* @author Sven Mawson
* @author Jesse Wilson
*/
public class TypeToken<T> {
/**
* Constructs a new type literal. Derives represented class from type
* parameter.
*
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
* parameter in the anonymous class's type hierarchy so we can reconstitute it
* at runtime despite erasure.
*/
protected TypeToken() {
}
}

View File

@@ -0,0 +1,66 @@
package com.google.gson.stream;
import java.io.Closeable;
import java.io.IOException;
import java.io.Reader;
public class JsonReader implements Closeable {
public JsonReader(Reader in) {
}
public final void setLenient(boolean lenient) {
}
public final boolean isLenient() {
return false;
}
public void beginArray() throws IOException {
}
public void endArray() throws IOException {
}
public void beginObject() throws IOException {
}
public void endObject() throws IOException {
}
public boolean hasNext() throws IOException {
return false;
}
public String nextName() throws IOException {
return null;
}
public String nextString() throws IOException {
return null;
}
public boolean nextBoolean() throws IOException {
return false;
}
public void nextNull() throws IOException {
}
public double nextDouble() throws IOException {
return -1;
}
public long nextLong() throws IOException {
return -1;
}
public int nextInt() throws IOException {
return -1;
}
public void close() throws IOException {
}
public void skipValue() throws IOException {
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.typeadapters;
import java.io.IOException;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
/**
* Adapts values whose runtime type may differ from their declaration type. This
* is necessary when a field's type is not the same type that GSON should create
* when deserializing that field. For example, consider these types:
* <pre> {@code
* abstract class Shape {
* int x;
* int y;
* }
* class Circle extends Shape {
* int radius;
* }
* class Rectangle extends Shape {
* int width;
* int height;
* }
* class Diamond extends Shape {
* int width;
* int height;
* }
* class Drawing {
* Shape bottomShape;
* Shape topShape;
* }
* }</pre>
* <p>Without additional type information, the serialized JSON is ambiguous. Is
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code
* {
* "bottomShape": {
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* This class addresses this problem by adding type information to the
* serialized JSON and honoring that type information when the JSON is
* deserialized: <pre> {@code
* {
* "bottomShape": {
* "type": "Diamond",
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "type": "Circle",
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* Both the type field name ({@code "type"}) and the type labels ({@code
* "Rectangle"}) are configurable.
*
* <h3>Registering Types</h3>
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
* name to the {@link #of} factory method. If you don't supply an explicit type
* field name, {@code "type"} will be used. <pre> {@code
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory
* = RuntimeTypeAdapterFactory.of(Shape.class, "type");
* }</pre>
* Next register all of your subtypes. Every subtype must be explicitly
* registered. This protects your application from injection attacks. If you
* don't supply an explicit type label, the type's simple name will be used.
* <pre> {@code
* shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
* shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
* shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
* }</pre>
* Finally, register the type adapter factory in your application's GSON builder:
* <pre> {@code
* Gson gson = new GsonBuilder()
* .registerTypeAdapterFactory(shapeAdapterFactory)
* .create();
* }</pre>
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
* .registerSubtype(Rectangle.class)
* .registerSubtype(Circle.class)
* .registerSubtype(Diamond.class);
* }</pre>
*
* <h3>Serialization and deserialization</h3>
* In order to serialize and deserialize a polymorphic object,
* you must specify the base type explicitly.
* <pre> {@code
* Diamond diamond = new Diamond();
* String json = gson.toJson(diamond, Shape.class);
* }</pre>
* And then:
* <pre> {@code
* Shape shape = gson.fromJson(json, Shape.class);
* }</pre>
*/
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
/**
* Creates a new runtime type adapter using for {@code baseType} using {@code
* typeFieldName} as the type field name. Type field names are case sensitive.
* {@code maintainType} flag decide if the type will be stored in pojo or not.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName, boolean maintainType) {
return null;
}
/**
* Creates a new runtime type adapter using for {@code baseType} using {@code
* typeFieldName} as the type field name. Type field names are case sensitive.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
return null;
}
/**
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
* the type field name.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
return null;
}
/**
* Registers {@code type} identified by {@code label}. Labels are case
* sensitive.
*
* @throws IllegalArgumentException if either {@code type} or {@code label}
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
return null;
}
/**
* Registers {@code type} identified by its {@link Class#getSimpleName simple
* name}. Labels are case sensitive.
*
* @throws IllegalArgumentException if either {@code type} or its simple name
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
return null;
}
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
return null;
}
}