Created an alpha package that holds experimental feature.

Added support for JsonPostDeserializer that allows you to invoke postDeserialize methods on an Gson deserialized object.
This commit is contained in:
Inderjeet Singh 2012-10-11 03:20:36 +00:00
parent 14f16e2d0c
commit c25278b4d6
5 changed files with 192 additions and 2 deletions

View File

@ -33,8 +33,11 @@ import java.util.Map;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.Excluder;
import com.google.gson.internal.ObjectConstructor;
import com.google.gson.internal.Primitives;
import com.google.gson.internal.Streams;
import com.google.gson.internal.alpha.Intercept;
import com.google.gson.internal.alpha.JsonPostDeserializer;
import com.google.gson.internal.bind.ArrayTypeAdapter;
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
import com.google.gson.internal.bind.DateTypeAdapter;
@ -791,8 +794,11 @@ public final class Gson {
try {
reader.peek();
isEmpty = false;
TypeAdapter<T> typeAdapter = (TypeAdapter<T>) getAdapter(TypeToken.get(typeOfT));
return typeAdapter.read(reader);
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = (TypeAdapter<T>) getAdapter(typeToken);
T object = typeAdapter.read(reader);
invokeInterceptorIfNeeded(object, typeToken);
return object;
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return null for empty
@ -884,6 +890,20 @@ public final class Gson {
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <T> void invokeInterceptorIfNeeded(T object, TypeToken<T> type) {
Class<? super T> clazz = type.getRawType();
Intercept interceptor = clazz.getAnnotation(Intercept.class);
if (interceptor == null) return;
// TODO: We don't need to construct an instance of postDeserializer every time. we can
// create it once and cache it.
Class<? extends JsonPostDeserializer> postDeserializerClass = interceptor.postDeserialize();
ObjectConstructor<? extends JsonPostDeserializer> objectConstructor =
constructorConstructor.get(TypeToken.get(postDeserializerClass));
JsonPostDeserializer<T> postDeserializer = objectConstructor.construct();
postDeserializer.postDeserialize(object);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{")

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2012 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.internal.alpha;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Use this annotation to indicate various interceptors for class instances after
* they have been processed by Gson. For example, you can use it to validate an instance
* after it has been deserialized from Json.
* Here is an example of how this annotation is used:
* <p>Here is an example of how this annotation is used:
* <p><pre>
* &#64Intercept(postDeserialize=UserValidator.class)
* public class User {
* String name;
* String password;
* String emailAddress;
* }
*
* public class UserValidator implements JsonPostDeserializer&lt;User&gt; {
* public void postDeserialize(User user) {
* // Do some checks on user
* if (user.name == null || user.password == null) {
* throw new JsonParseException("name and password are required fields.");
* }
* if (user.emailAddress == null) {
* emailAddress = "unknown"; // assign a default value.
* }
* }
* }
* </pre></p>
*
* @author Inderjeet Singh
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercept {
/**
* Specify the class that provides the methods that should be invoked after an instance
* has been deserialized.
*/
@SuppressWarnings("rawtypes")
public Class<? extends JsonPostDeserializer> postDeserialize();
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2012 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.internal.alpha;
import com.google.gson.InstanceCreator;
/**
* This interface is implemented by a class that wishes to inspect or modify an object
* after it has been deserialized. You must define a no-args constructor or register an
* {@link InstanceCreator} for such a class.
*
* @author Inderjeet Singh
*/
public interface JsonPostDeserializer<T> {
/**
* This method is called by Gson after the object has been deserialized from Json.
*/
public void postDeserialize(T object);
}

View File

@ -0,0 +1,9 @@
/**
* This package provides experimental Gson features that are very likely to change from
* release to release. Backwards compatibility will almost certainly be broken in a future
* release by either changing the package name (when the feature moves to main Gson) or when
* we decide that the feature isn't worth adding.
*
* @author Inderjeet Singh, Joel Leitch, Jesse Wilson
*/
package com.google.gson.internal.alpha;

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2012 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.functional;
import junit.framework.TestCase;
import com.google.gson.Gson;
import com.google.gson.internal.alpha.Intercept;
import com.google.gson.internal.alpha.JsonPostDeserializer;
/**
* Unit tests for {@link Intercept} and {@link JsonPostDeserializer}.
*
* @author Inderjeet Singh
*/
public final class InterceptorTest extends TestCase {
private Gson gson;
@Override
public void setUp() throws Exception {
super.setUp();
this.gson = new Gson();
}
public void testPostDeserialize() {
MyObject target = gson.fromJson("{}", MyObject.class);
assertEquals(MyObject.DEFAULT_VALUE, target.value);
assertEquals(MyObject.DEFAULT_MESSAGE, target.message);
}
@Intercept(postDeserialize = MyObjectInterceptor.class)
private static final class MyObject {
static final int DEFAULT_VALUE = 10;
static final String DEFAULT_MESSAGE = "hello";
int value = 0;
String message = null;
}
private static final class MyObjectInterceptor implements JsonPostDeserializer<MyObject> {
public void postDeserialize(MyObject o) {
if (o.value == 0) {
o.value = MyObject.DEFAULT_VALUE;
}
if (o.message == null) {
o.message = MyObject.DEFAULT_MESSAGE;
}
}
}
}