diff --git a/extras/README.md b/extras/README.md deleted file mode 100644 index 41447726e2..0000000000 --- a/extras/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# extras - -This Maven module contains the source code for supplementary Gson features which -are not included by default. - -The artifacts created by this module are currently not deployed to Maven Central. diff --git a/extras/pom.xml b/extras/pom.xml deleted file mode 100644 index ebeb66b45c..0000000000 --- a/extras/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - 4.0.0 - - com.google.code.gson - gson-parent - 2.10.1 - - - gson-extras - 2008 - Gson Extras - Google Gson grab bag of utilities, type adapters, etc. - - - - Apache-2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - Google, Inc. - https://www.google.com - - - - - com.google.code.gson - gson - ${project.parent.version} - - - javax.annotation - jsr250-api - 1.0 - - - junit - junit - test - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - - true - - - - - - - - - Inderjeet Singh - - - Joel Leitch - Google Inc. - - - Jesse Wilson - Square Inc. - - - diff --git a/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java b/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java deleted file mode 100644 index 9ea350cae8..0000000000 --- a/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.extras.examples.rawcollections; - -import java.util.ArrayList; -import java.util.Collection; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonParser; - -public class RawCollectionsExample { - static class Event { - private String name; - private String source; - private Event(String name, String source) { - this.name = name; - this.source = source; - } - @Override - public String toString() { - return String.format("(name=%s, source=%s)", name, source); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void main(String[] args) { - Gson gson = new Gson(); - Collection collection = new ArrayList(); - collection.add("hello"); - collection.add(5); - collection.add(new Event("GREETINGS", "guest")); - String json = gson.toJson(collection); - System.out.println("Using Gson.toJson() on a raw collection: " + json); - JsonArray array = JsonParser.parseString(json).getAsJsonArray(); - String message = gson.fromJson(array.get(0), String.class); - int number = gson.fromJson(array.get(1), int.class); - Event event = gson.fromJson(array.get(2), Event.class); - System.out.printf("Using Gson.fromJson() to get: %s, %d, %s", message, number, event); - } -} diff --git a/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java b/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java deleted file mode 100644 index b226b220f0..0000000000 --- a/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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.graph; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.InstanceCreator; -import com.google.gson.JsonElement; -import com.google.gson.ReflectionAccessFilter; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.internal.ConstructorConstructor; -import com.google.gson.internal.ObjectConstructor; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; - -/** - * Writes a graph of objects as a list of named nodes. - */ -// TODO: proper documentation -public final class GraphAdapterBuilder { - private final Map> instanceCreators; - private final ConstructorConstructor constructorConstructor; - - public GraphAdapterBuilder() { - this.instanceCreators = new HashMap<>(); - this.constructorConstructor = new ConstructorConstructor(instanceCreators, true, Collections.emptyList()); - } - public GraphAdapterBuilder addType(Type type) { - final ObjectConstructor objectConstructor = constructorConstructor.get(TypeToken.get(type)); - InstanceCreator instanceCreator = new InstanceCreator() { - @Override - public Object createInstance(Type type) { - return objectConstructor.construct(); - } - }; - return addType(type, instanceCreator); - } - - public GraphAdapterBuilder addType(Type type, InstanceCreator instanceCreator) { - if (type == null || instanceCreator == null) { - throw new NullPointerException(); - } - instanceCreators.put(type, instanceCreator); - return this; - } - - public void registerOn(GsonBuilder gsonBuilder) { - Factory factory = new Factory(instanceCreators); - gsonBuilder.registerTypeAdapterFactory(factory); - for (Map.Entry> entry : instanceCreators.entrySet()) { - gsonBuilder.registerTypeAdapter(entry.getKey(), factory); - } - } - - static class Factory implements TypeAdapterFactory, InstanceCreator { - private final Map> instanceCreators; - private final ThreadLocal graphThreadLocal = new ThreadLocal<>(); - - Factory(Map> instanceCreators) { - this.instanceCreators = instanceCreators; - } - - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - if (!instanceCreators.containsKey(type.getType())) { - return null; - } - - final TypeAdapter typeAdapter = gson.getDelegateAdapter(this, type); - final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); - return new TypeAdapter() { - @Override public void write(JsonWriter out, T value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - - Graph graph = graphThreadLocal.get(); - boolean writeEntireGraph = false; - - /* - * We have one of two cases: - * 1. We've encountered the first known object in this graph. Write - * out the graph, starting with that object. - * 2. We've encountered another graph object in the course of #1. - * Just write out this object's name. We'll circle back to writing - * out the object's value as a part of #1. - */ - - if (graph == null) { - writeEntireGraph = true; - graph = new Graph(new IdentityHashMap>()); - } - - @SuppressWarnings("unchecked") // graph.map guarantees consistency between value and T - Element element = (Element) graph.map.get(value); - if (element == null) { - element = new Element<>(value, graph.nextName(), typeAdapter, null); - graph.map.put(value, element); - graph.queue.add(element); - } - - if (writeEntireGraph) { - graphThreadLocal.set(graph); - try { - out.beginObject(); - Element current; - while ((current = graph.queue.poll()) != null) { - out.name(current.id); - current.write(out); - } - out.endObject(); - } finally { - graphThreadLocal.remove(); - } - } else { - out.value(element.id); - } - } - - @Override public T read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - /* - * Again we have one of two cases: - * 1. We've encountered the first known object in this graph. Read - * the entire graph in as a map from names to their JsonElements. - * Then convert the first JsonElement to its Java object. - * 2. We've encountered another graph object in the course of #1. - * Read in its name, then deserialize its value from the - * JsonElement in our map. We need to do this lazily because we - * don't know which TypeAdapter to use until a value is - * encountered in the wild. - */ - - String currentName = null; - Graph graph = graphThreadLocal.get(); - boolean readEntireGraph = false; - - if (graph == null) { - graph = new Graph(new HashMap>()); - readEntireGraph = true; - - // read the entire tree into memory - in.beginObject(); - while (in.hasNext()) { - String name = in.nextName(); - if (currentName == null) { - currentName = name; - } - JsonElement element = elementAdapter.read(in); - graph.map.put(name, new Element<>(null, name, typeAdapter, element)); - } - in.endObject(); - } else { - currentName = in.nextString(); - } - - if (readEntireGraph) { - graphThreadLocal.set(graph); - } - try { - @SuppressWarnings("unchecked") // graph.map guarantees consistency between value and T - Element element = (Element) graph.map.get(currentName); - // now that we know the typeAdapter for this name, go from JsonElement to 'T' - if (element.value == null) { - element.typeAdapter = typeAdapter; - element.read(graph); - } - return element.value; - } finally { - if (readEntireGraph) { - graphThreadLocal.remove(); - } - } - } - }; - } - - /** - * Hook for the graph adapter to get a reference to a deserialized value - * before that value is fully populated. This is useful to deserialize - * values that directly or indirectly reference themselves: we can hand - * out an instance before read() returns. - * - *

Gson should only ever call this method when we're expecting it to; - * that is only when we've called back into Gson to deserialize a tree. - */ - @Override - public Object createInstance(Type type) { - Graph graph = graphThreadLocal.get(); - if (graph == null || graph.nextCreate == null) { - throw new IllegalStateException("Unexpected call to createInstance() for " + type); - } - InstanceCreator creator = instanceCreators.get(type); - Object result = creator.createInstance(type); - graph.nextCreate.value = result; - graph.nextCreate = null; - return result; - } - } - - static class Graph { - /** - * The graph elements. On serialization keys are objects (using an identity - * hash map) and on deserialization keys are the string names (using a - * standard hash map). - */ - private final Map> map; - - /** - * The queue of elements to write during serialization. Unused during - * deserialization. - */ - private final Queue> queue = new LinkedList<>(); - - /** - * The instance currently being deserialized. Used as a backdoor between - * the graph traversal (which needs to know instances) and instance creators - * which create them. - */ - private Element nextCreate; - - private Graph(Map> map) { - this.map = map; - } - - /** - * Returns a unique name for an element to be inserted into the graph. - */ - public String nextName() { - return "0x" + Integer.toHexString(map.size() + 1); - } - } - - /** - * An element of the graph during serialization or deserialization. - */ - static class Element { - /** - * This element's name in the top level graph object. - */ - private final String id; - - /** - * The value if known. During deserialization this is lazily populated. - */ - private T value; - - /** - * This element's type adapter if known. During deserialization this is - * lazily populated. - */ - private TypeAdapter typeAdapter; - - /** - * The element to deserialize. Unused in serialization. - */ - private final JsonElement element; - - Element(T value, String id, TypeAdapter typeAdapter, JsonElement element) { - this.value = value; - this.id = id; - this.typeAdapter = typeAdapter; - this.element = element; - } - - void write(JsonWriter out) throws IOException { - typeAdapter.write(out, value); - } - - @SuppressWarnings("unchecked") - void read(Graph graph) { - if (graph.nextCreate != null) { - throw new IllegalStateException("Unexpected recursive call to read() for " + id); - } - graph.nextCreate = (Element) this; - value = typeAdapter.fromJsonTree(element); - if (value == null) { - throw new IllegalStateException("non-null value deserialized to null: " + element); - } - } - } -} diff --git a/extras/src/main/java/com/google/gson/interceptors/Intercept.java b/extras/src/main/java/com/google/gson/interceptors/Intercept.java deleted file mode 100644 index fef29cbf0b..0000000000 --- a/extras/src/main/java/com/google/gson/interceptors/Intercept.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.interceptors; - -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: - *

Here is an example of how this annotation is used: - *

- * @Intercept(postDeserialize=UserValidator.class)
- * public class User {
- *   String name;
- *   String password;
- *   String emailAddress;
- * }
- *
- * public class UserValidator implements JsonPostDeserializer<User> {
- *   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.
- *     }
- *   }
- * }
- * 
- * - * @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 postDeserialize(); -} diff --git a/extras/src/main/java/com/google/gson/interceptors/InterceptorFactory.java b/extras/src/main/java/com/google/gson/interceptors/InterceptorFactory.java deleted file mode 100644 index 51916c220d..0000000000 --- a/extras/src/main/java/com/google/gson/interceptors/InterceptorFactory.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.google.gson.interceptors; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; - -/** - * A type adapter factory that implements {@code @Intercept}. - */ -public final class InterceptorFactory implements TypeAdapterFactory { - @Override public TypeAdapter create(Gson gson, TypeToken type) { - Intercept intercept = type.getRawType().getAnnotation(Intercept.class); - if (intercept == null) { - return null; - } - - TypeAdapter delegate = gson.getDelegateAdapter(this, type); - return new InterceptorAdapter<>(delegate, intercept); - } - - static class InterceptorAdapter extends TypeAdapter { - private final TypeAdapter delegate; - private final JsonPostDeserializer postDeserializer; - - @SuppressWarnings("unchecked") // ? - public InterceptorAdapter(TypeAdapter delegate, Intercept intercept) { - try { - this.delegate = delegate; - this.postDeserializer = intercept.postDeserialize().newInstance(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override public void write(JsonWriter out, T value) throws IOException { - delegate.write(out, value); - } - - @Override public T read(JsonReader in) throws IOException { - T result = delegate.read(in); - postDeserializer.postDeserialize(result); - return result; - } - } -} diff --git a/extras/src/main/java/com/google/gson/interceptors/JsonPostDeserializer.java b/extras/src/main/java/com/google/gson/interceptors/JsonPostDeserializer.java deleted file mode 100644 index 0f3a72ca82..0000000000 --- a/extras/src/main/java/com/google/gson/interceptors/JsonPostDeserializer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.interceptors; - -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 { - - /** - * This method is called by Gson after the object has been deserialized from Json. - */ - public void postDeserialize(T object); -} diff --git a/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java b/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java deleted file mode 100644 index 450ebbab2d..0000000000 --- a/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2016 Gson Authors - * - * 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 java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import javax.annotation.PostConstruct; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; - -public class PostConstructAdapterFactory implements TypeAdapterFactory { - // copied from https://gist.github.com/swankjesse/20df26adaf639ed7fd160f145a0b661a - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - for (Class t = type.getRawType(); (t != Object.class) && (t.getSuperclass() != null); t = t.getSuperclass()) { - for (Method m : t.getDeclaredMethods()) { - if (m.isAnnotationPresent(PostConstruct.class)) { - m.setAccessible(true); - TypeAdapter delegate = gson.getDelegateAdapter(this, type); - return new PostConstructAdapter<>(delegate, m); - } - } - } - return null; - } - - final static class PostConstructAdapter extends TypeAdapter { - private final TypeAdapter delegate; - private final Method method; - - public PostConstructAdapter(TypeAdapter delegate, Method method) { - this.delegate = delegate; - this.method = method; - } - - @Override public T read(JsonReader in) throws IOException { - T result = delegate.read(in); - if (result != null) { - try { - method.invoke(result); - } catch (IllegalAccessException e) { - throw new AssertionError(); - } catch (InvocationTargetException e) { - if (e.getCause() instanceof RuntimeException) throw (RuntimeException) e.getCause(); - throw new RuntimeException(e.getCause()); - } - } - return result; - } - - @Override public void write(JsonWriter out, T value) throws IOException { - delegate.write(out, value); - } - } -} diff --git a/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java b/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java deleted file mode 100644 index 87b522f0c4..0000000000 --- a/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 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 com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * 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: - *
   {@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;
- *   }
- * }
- *

Without additional type information, the serialized JSON is ambiguous. Is - * the bottom shape in this drawing a rectangle or a diamond?

   {@code
- *   {
- *     "bottomShape": {
- *       "width": 10,
- *       "height": 5,
- *       "x": 0,
- *       "y": 0
- *     },
- *     "topShape": {
- *       "radius": 2,
- *       "x": 4,
- *       "y": 1
- *     }
- *   }}
- * This class addresses this problem by adding type information to the - * serialized JSON and honoring that type information when the JSON is - * deserialized:
   {@code
- *   {
- *     "bottomShape": {
- *       "type": "Diamond",
- *       "width": 10,
- *       "height": 5,
- *       "x": 0,
- *       "y": 0
- *     },
- *     "topShape": {
- *       "type": "Circle",
- *       "radius": 2,
- *       "x": 4,
- *       "y": 1
- *     }
- *   }}
- * Both the type field name ({@code "type"}) and the type labels ({@code - * "Rectangle"}) are configurable. - * - *

Registering Types

- * 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.
   {@code
- *   RuntimeTypeAdapterFactory shapeAdapterFactory
- *       = RuntimeTypeAdapterFactory.of(Shape.class, "type");
- * }
- * 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. - *
   {@code
- *   shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
- *   shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
- *   shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
- * }
- * Finally, register the type adapter factory in your application's GSON builder: - *
   {@code
- *   Gson gson = new GsonBuilder()
- *       .registerTypeAdapterFactory(shapeAdapterFactory)
- *       .create();
- * }
- * Like {@code GsonBuilder}, this API supports chaining:
   {@code
- *   RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
- *       .registerSubtype(Rectangle.class)
- *       .registerSubtype(Circle.class)
- *       .registerSubtype(Diamond.class);
- * }
- * - *

Serialization and deserialization

- * In order to serialize and deserialize a polymorphic object, - * you must specify the base type explicitly. - *
   {@code
- *   Diamond diamond = new Diamond();
- *   String json = gson.toJson(diamond, Shape.class);
- * }
- * And then: - *
   {@code
- *   Shape shape = gson.fromJson(json, Shape.class);
- * }
- */ -public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { - private final Class baseType; - private final String typeFieldName; - private final Map> labelToSubtype = new LinkedHashMap<>(); - private final Map, String> subtypeToLabel = new LinkedHashMap<>(); - private final boolean maintainType; - private boolean recognizeSubtypes; - - private RuntimeTypeAdapterFactory( - Class baseType, String typeFieldName, boolean maintainType) { - if (typeFieldName == null || baseType == null) { - throw new NullPointerException(); - } - this.baseType = baseType; - this.typeFieldName = typeFieldName; - this.maintainType = maintainType; - } - - /** - * Creates a new runtime type adapter using for {@code baseType} using {@code - * typeFieldName} as the type field name. Type field names are case sensitive. - * - * @param maintainType true if the type field should be included in deserialized objects - */ - public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName, boolean maintainType) { - return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, maintainType); - } - - /** - * 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 RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { - return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, false); - } - - /** - * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as - * the type field name. - */ - public static RuntimeTypeAdapterFactory of(Class baseType) { - return new RuntimeTypeAdapterFactory<>(baseType, "type", false); - } - - /** - * Ensures that this factory will handle not just the given {@code baseType}, but any subtype - * of that type. - */ - public RuntimeTypeAdapterFactory recognizeSubtypes() { - this.recognizeSubtypes = true; - return this; - } - - /** - * 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 registerSubtype(Class type, String label) { - if (type == null || label == null) { - throw new NullPointerException(); - } - if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { - throw new IllegalArgumentException("types and labels must be unique"); - } - labelToSubtype.put(label, type); - subtypeToLabel.put(type, label); - return this; - } - - /** - * 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 registerSubtype(Class type) { - return registerSubtype(type, type.getSimpleName()); - } - - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - if (type == null) { - return null; - } - Class rawType = type.getRawType(); - boolean handle = - recognizeSubtypes ? baseType.isAssignableFrom(rawType) : baseType.equals(rawType); - if (!handle) { - return null; - } - - final TypeAdapter jsonElementAdapter = gson.getAdapter(JsonElement.class); - final Map> labelToDelegate = new LinkedHashMap<>(); - final Map, TypeAdapter> subtypeToDelegate = new LinkedHashMap<>(); - for (Map.Entry> entry : labelToSubtype.entrySet()) { - TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); - labelToDelegate.put(entry.getKey(), delegate); - subtypeToDelegate.put(entry.getValue(), delegate); - } - - return new TypeAdapter() { - @Override public R read(JsonReader in) throws IOException { - JsonElement jsonElement = jsonElementAdapter.read(in); - JsonElement labelJsonElement; - if (maintainType) { - labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName); - } else { - labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); - } - - if (labelJsonElement == null) { - throw new JsonParseException("cannot deserialize " + baseType - + " because it does not define a field named " + typeFieldName); - } - String label = labelJsonElement.getAsString(); - @SuppressWarnings("unchecked") // registration requires that subtype extends T - TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); - if (delegate == null) { - throw new JsonParseException("cannot deserialize " + baseType + " subtype named " - + label + "; did you forget to register a subtype?"); - } - return delegate.fromJsonTree(jsonElement); - } - - @Override public void write(JsonWriter out, R value) throws IOException { - Class srcType = value.getClass(); - String label = subtypeToLabel.get(srcType); - @SuppressWarnings("unchecked") // registration requires that subtype extends T - TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); - if (delegate == null) { - throw new JsonParseException("cannot serialize " + srcType.getName() - + "; did you forget to register a subtype?"); - } - JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); - - if (maintainType) { - jsonElementAdapter.write(out, jsonObject); - return; - } - - JsonObject clone = new JsonObject(); - - if (jsonObject.has(typeFieldName)) { - throw new JsonParseException("cannot serialize " + srcType.getName() - + " because it already defines a field named " + typeFieldName); - } - clone.add(typeFieldName, new JsonPrimitive(label)); - - for (Map.Entry e : jsonObject.entrySet()) { - clone.add(e.getKey(), e.getValue()); - } - jsonElementAdapter.write(out, clone); - } - }.nullSafe(); - } -} diff --git a/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java b/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java deleted file mode 100644 index 2278f842bf..0000000000 --- a/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * 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 java.text.ParseException; -import java.text.ParsePosition; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -public final class UtcDateTypeAdapter extends TypeAdapter { - private final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); - - @Override - public void write(JsonWriter out, Date date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - String value = format(date, true, UTC_TIME_ZONE); - out.value(value); - } - } - - @Override - public Date read(JsonReader in) throws IOException { - try { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - // Instead of using iso8601Format.parse(value), we use Jackson's date parsing - // This is because Android doesn't support XXX because it is JDK 1.6 - return parse(date, new ParsePosition(0)); - } - } catch (ParseException e) { - throw new JsonParseException(e); - } - } - - // Date parsing code from Jackson databind ISO8601Utils.java - // https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java - private static final String GMT_ID = "GMT"; - - /** - * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] - * - * @param date the date to format - * @param millis true to include millis precision otherwise false - * @param tz timezone to use for the formatting (GMT will produce 'Z') - * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] - */ - private static String format(Date date, boolean millis, TimeZone tz) { - Calendar calendar = new GregorianCalendar(tz, Locale.US); - calendar.setTime(date); - - // estimate capacity of buffer as close as we can (yeah, that's pedantic ;) - int capacity = "yyyy-MM-ddThh:mm:ss".length(); - capacity += millis ? ".sss".length() : 0; - capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length(); - StringBuilder formatted = new StringBuilder(capacity); - - padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length()); - formatted.append('-'); - padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length()); - formatted.append('-'); - padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length()); - formatted.append('T'); - padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length()); - formatted.append(':'); - padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length()); - formatted.append(':'); - padInt(formatted, calendar.get(Calendar.SECOND), "ss".length()); - if (millis) { - formatted.append('.'); - padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length()); - } - - int offset = tz.getOffset(calendar.getTimeInMillis()); - if (offset != 0) { - int hours = Math.abs((offset / (60 * 1000)) / 60); - int minutes = Math.abs((offset / (60 * 1000)) % 60); - formatted.append(offset < 0 ? '-' : '+'); - padInt(formatted, hours, "hh".length()); - formatted.append(':'); - padInt(formatted, minutes, "mm".length()); - } else { - formatted.append('Z'); - } - - return formatted.toString(); - } - /** - * Zero pad a number to a specified length - * - * @param buffer buffer to use for padding - * @param value the integer value to pad if necessary. - * @param length the length of the string we should zero pad - */ - private static void padInt(StringBuilder buffer, int value, int length) { - String strValue = Integer.toString(value); - for (int i = length - strValue.length(); i > 0; i--) { - buffer.append('0'); - } - buffer.append(strValue); - } - - /** - * Parse a date from ISO-8601 formatted string. It expects a format - * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]] - * - * @param date ISO string to parse in the appropriate format. - * @param pos The position to start parsing from, updated to where parsing stopped. - * @return the parsed date - * @throws ParseException if the date is not in the appropriate format - */ - private static Date parse(String date, ParsePosition pos) throws ParseException { - Exception fail = null; - try { - int offset = pos.getIndex(); - - // extract year - int year = parseInt(date, offset, offset += 4); - if (checkOffset(date, offset, '-')) { - offset += 1; - } - - // extract month - int month = parseInt(date, offset, offset += 2); - if (checkOffset(date, offset, '-')) { - offset += 1; - } - - // extract day - int day = parseInt(date, offset, offset += 2); - // default time value - int hour = 0; - int minutes = 0; - int seconds = 0; - int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time - if (checkOffset(date, offset, 'T')) { - - // extract hours, minutes, seconds and milliseconds - hour = parseInt(date, offset += 1, offset += 2); - if (checkOffset(date, offset, ':')) { - offset += 1; - } - - minutes = parseInt(date, offset, offset += 2); - if (checkOffset(date, offset, ':')) { - offset += 1; - } - // second and milliseconds can be optional - if (date.length() > offset) { - char c = date.charAt(offset); - if (c != 'Z' && c != '+' && c != '-') { - seconds = parseInt(date, offset, offset += 2); - // milliseconds can be optional in the format - if (checkOffset(date, offset, '.')) { - milliseconds = parseInt(date, offset += 1, offset += 3); - } - } - } - } - - // extract timezone - String timezoneId; - if (date.length() <= offset) { - throw new IllegalArgumentException("No time zone indicator"); - } - char timezoneIndicator = date.charAt(offset); - if (timezoneIndicator == '+' || timezoneIndicator == '-') { - String timezoneOffset = date.substring(offset); - timezoneId = GMT_ID + timezoneOffset; - offset += timezoneOffset.length(); - } else if (timezoneIndicator == 'Z') { - timezoneId = GMT_ID; - offset += 1; - } else { - throw new IndexOutOfBoundsException("Invalid time zone indicator " + timezoneIndicator); - } - - TimeZone timezone = TimeZone.getTimeZone(timezoneId); - if (!timezone.getID().equals(timezoneId)) { - throw new IndexOutOfBoundsException(); - } - - Calendar calendar = new GregorianCalendar(timezone); - calendar.setLenient(false); - calendar.set(Calendar.YEAR, year); - calendar.set(Calendar.MONTH, month - 1); - calendar.set(Calendar.DAY_OF_MONTH, day); - calendar.set(Calendar.HOUR_OF_DAY, hour); - calendar.set(Calendar.MINUTE, minutes); - calendar.set(Calendar.SECOND, seconds); - calendar.set(Calendar.MILLISECOND, milliseconds); - - pos.setIndex(offset); - return calendar.getTime(); - // If we get a ParseException it'll already have the right message/offset. - // Other exception types can convert here. - } catch (IndexOutOfBoundsException e) { - fail = e; - } catch (NumberFormatException e) { - fail = e; - } catch (IllegalArgumentException e) { - fail = e; - } - String input = (date == null) ? null : ("'" + date + "'"); - throw new ParseException("Failed to parse date [" + input + "]: " + fail.getMessage(), pos.getIndex()); - } - - /** - * Check if the expected character exist at the given offset in the value. - * - * @param value the string to check at the specified offset - * @param offset the offset to look for the expected character - * @param expected the expected character - * @return true if the expected character exist at the given offset - */ - private static boolean checkOffset(String value, int offset, char expected) { - return (offset < value.length()) && (value.charAt(offset) == expected); - } - - /** - * Parse an integer located between 2 given offsets in a string - * - * @param value the string to parse - * @param beginIndex the start index for the integer in the string - * @param endIndex the end index for the integer in the string - * @return the int - * @throws NumberFormatException if the value is not a number - */ - private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException { - if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) { - throw new NumberFormatException(value); - } - // use same logic as in Integer.parseInt() but less generic we're not supporting negative values - int i = beginIndex; - int result = 0; - int digit; - if (i < endIndex) { - digit = Character.digit(value.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException("Invalid number: " + value); - } - result = -digit; - } - while (i < endIndex) { - digit = Character.digit(value.charAt(i++), 10); - if (digit < 0) { - throw new NumberFormatException("Invalid number: " + value); - } - result *= 10; - result -= digit; - } - return -result; - } -} diff --git a/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java b/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java deleted file mode 100644 index 3b2425ec9e..0000000000 --- a/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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.graph; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; - -public final class GraphAdapterBuilderTest { - @Test - public void testSerialization() { - Roshambo rock = new Roshambo("ROCK"); - Roshambo scissors = new Roshambo("SCISSORS"); - Roshambo paper = new Roshambo("PAPER"); - rock.beats = scissors; - scissors.beats = paper; - paper.beats = rock; - - GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(Roshambo.class) - .registerOn(gsonBuilder); - Gson gson = gsonBuilder.create(); - - assertEquals("{'0x1':{'name':'ROCK','beats':'0x2'}," + - "'0x2':{'name':'SCISSORS','beats':'0x3'}," + - "'0x3':{'name':'PAPER','beats':'0x1'}}", - gson.toJson(rock).replace('"', '\'')); - } - - @Test - public void testDeserialization() { - String json = "{'0x1':{'name':'ROCK','beats':'0x2'}," + - "'0x2':{'name':'SCISSORS','beats':'0x3'}," + - "'0x3':{'name':'PAPER','beats':'0x1'}}"; - - GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(Roshambo.class) - .registerOn(gsonBuilder); - Gson gson = gsonBuilder.create(); - - Roshambo rock = gson.fromJson(json, Roshambo.class); - assertEquals("ROCK", rock.name); - Roshambo scissors = rock.beats; - assertEquals("SCISSORS", scissors.name); - Roshambo paper = scissors.beats; - assertEquals("PAPER", paper.name); - assertSame(rock, paper.beats); - } - - @Test - public void testDeserializationDirectSelfReference() { - String json = "{'0x1':{'name':'SUICIDE','beats':'0x1'}}"; - - GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(Roshambo.class) - .registerOn(gsonBuilder); - Gson gson = gsonBuilder.create(); - - Roshambo suicide = gson.fromJson(json, Roshambo.class); - assertEquals("SUICIDE", suicide.name); - assertSame(suicide, suicide.beats); - } - - @Test - public void testSerializeListOfLists() { - Type listOfListsType = new TypeToken>>() {}.getType(); - Type listOfAnyType = new TypeToken>() {}.getType(); - - List> listOfLists = new ArrayList<>(); - listOfLists.add(listOfLists); - listOfLists.add(new ArrayList<>()); - - GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(listOfListsType) - .addType(listOfAnyType) - .registerOn(gsonBuilder); - Gson gson = gsonBuilder.create(); - - String json = gson.toJson(listOfLists, listOfListsType); - assertEquals("{'0x1':['0x1','0x2'],'0x2':[]}", json.replace('"', '\'')); - } - - @Test - public void testDeserializeListOfLists() { - Type listOfAnyType = new TypeToken>() {}.getType(); - Type listOfListsType = new TypeToken>>() {}.getType(); - - GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(listOfListsType) - .addType(listOfAnyType) - .registerOn(gsonBuilder); - Gson gson = gsonBuilder.create(); - - List> listOfLists = gson.fromJson("{'0x1':['0x1','0x2'],'0x2':[]}", listOfListsType); - assertEquals(2, listOfLists.size()); - assertSame(listOfLists, listOfLists.get(0)); - assertEquals(Collections.emptyList(), listOfLists.get(1)); - } - - @Test - public void testSerializationWithMultipleTypes() { - Company google = new Company("Google"); - new Employee("Jesse", google); - new Employee("Joel", google); - - GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(Company.class) - .addType(Employee.class) - .registerOn(gsonBuilder); - Gson gson = gsonBuilder.create(); - - assertEquals("{'0x1':{'name':'Google','employees':['0x2','0x3']}," - + "'0x2':{'name':'Jesse','company':'0x1'}," - + "'0x3':{'name':'Joel','company':'0x1'}}", - gson.toJson(google).replace('"', '\'')); - } - - @Test - public void testDeserializationWithMultipleTypes() { - GsonBuilder gsonBuilder = new GsonBuilder(); - new GraphAdapterBuilder() - .addType(Company.class) - .addType(Employee.class) - .registerOn(gsonBuilder); - Gson gson = gsonBuilder.create(); - - String json = "{'0x1':{'name':'Google','employees':['0x2','0x3']}," - + "'0x2':{'name':'Jesse','company':'0x1'}," - + "'0x3':{'name':'Joel','company':'0x1'}}"; - Company company = gson.fromJson(json, Company.class); - assertEquals("Google", company.name); - Employee jesse = company.employees.get(0); - assertEquals("Jesse", jesse.name); - assertEquals(company, jesse.company); - Employee joel = company.employees.get(1); - assertEquals("Joel", joel.name); - assertEquals(company, joel.company); - } - - static class Roshambo { - String name; - Roshambo beats; - Roshambo(String name) { - this.name = name; - } - } - - static class Employee { - final String name; - final Company company; - Employee(String name, Company company) { - this.name = name; - this.company = company; - this.company.employees.add(this); - } - } - - static class Company { - final String name; - final List employees = new ArrayList<>(); - Company(String name) { - this.name = name; - } - } -} diff --git a/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java b/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java deleted file mode 100644 index ee475c8e2f..0000000000 --- a/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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.interceptors; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import org.junit.Before; -import org.junit.Test; - -/** - * Unit tests for {@link Intercept} and {@link JsonPostDeserializer}. - * - * @author Inderjeet Singh - */ -public final class InterceptorTest { - - private Gson gson; - - @Before - public void setUp() throws Exception { - this.gson = new GsonBuilder() - .registerTypeAdapterFactory(new InterceptorFactory()) - .enableComplexMapKeySerialization() - .create(); - } - - @Test - public void testExceptionsPropagated() { - try { - gson.fromJson("{}", User.class); - fail(); - } catch (JsonParseException expected) {} - } - - @Test - public void testTopLevelClass() { - User user = gson.fromJson("{name:'bob',password:'pwd'}", User.class); - assertEquals(User.DEFAULT_EMAIL, user.email); - } - - @Test - public void testList() { - List list = gson.fromJson("[{name:'bob',password:'pwd'}]", new TypeToken>(){}.getType()); - User user = list.get(0); - assertEquals(User.DEFAULT_EMAIL, user.email); - } - - @Test - public void testCollection() { - Collection list = gson.fromJson("[{name:'bob',password:'pwd'}]", new TypeToken>(){}.getType()); - User user = list.iterator().next(); - assertEquals(User.DEFAULT_EMAIL, user.email); - } - - @Test - public void testMapKeyAndValues() { - Type mapType = new TypeToken>(){}.getType(); - try { - gson.fromJson("[[{name:'bob',password:'pwd'},{}]]", mapType); - fail(); - } catch (JsonSyntaxException expected) {} - Map map = gson.fromJson("[[{name:'bob',password:'pwd'},{city:'Mountain View',state:'CA',zip:'94043'}]]", - mapType); - Entry entry = map.entrySet().iterator().next(); - assertEquals(User.DEFAULT_EMAIL, entry.getKey().email); - assertEquals(Address.DEFAULT_FIRST_LINE, entry.getValue().firstLine); - } - - @Test - public void testField() { - UserGroup userGroup = gson.fromJson("{user:{name:'bob',password:'pwd'}}", UserGroup.class); - assertEquals(User.DEFAULT_EMAIL, userGroup.user.email); - } - - @Test - public void testCustomTypeAdapter() { - Gson gson = new GsonBuilder() - .registerTypeAdapter(User.class, new TypeAdapter() { - @Override public void write(JsonWriter out, User value) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override public User read(JsonReader in) throws IOException { - in.beginObject(); - in.nextName(); - String name = in.nextString(); - in.nextName(); - String password = in.nextString(); - in.endObject(); - return new User(name, password); - } - }) - .registerTypeAdapterFactory(new InterceptorFactory()) - .create(); - UserGroup userGroup = gson.fromJson("{user:{name:'bob',password:'pwd'}}", UserGroup.class); - assertEquals(User.DEFAULT_EMAIL, userGroup.user.email); - } - - @Test - public void testDirectInvocationOfTypeAdapter() throws Exception { - TypeAdapter adapter = gson.getAdapter(UserGroup.class); - UserGroup userGroup = adapter.fromJson("{\"user\":{\"name\":\"bob\",\"password\":\"pwd\"}}"); - assertEquals(User.DEFAULT_EMAIL, userGroup.user.email); - } - - @SuppressWarnings("unused") - private static final class UserGroup { - User user; - String city; - } - - @Intercept(postDeserialize = UserValidator.class) - @SuppressWarnings("unused") - private static final class User { - static final String DEFAULT_EMAIL = "invalid@invalid.com"; - String name; - String password; - String email; - Address address; - public User(String name, String password) { - this.name = name; - this.password = password; - } - } - - public static final class UserValidator implements JsonPostDeserializer { - @Override public void postDeserialize(User user) { - if (user.name == null || user.password == null) { - throw new JsonSyntaxException("name and password are required fields."); - } - if (user.email == null) user.email = User.DEFAULT_EMAIL; - } - } - - @Intercept(postDeserialize = AddressValidator.class) - @SuppressWarnings("unused") - private static final class Address { - static final String DEFAULT_FIRST_LINE = "unknown"; - String firstLine; - String secondLine; - String city; - String state; - String zip; - } - - public static final class AddressValidator implements JsonPostDeserializer
{ - @Override public void postDeserialize(Address address) { - if (address.city == null || address.state == null || address.zip == null) { - throw new JsonSyntaxException("Address city, state and zip are required fields."); - } - if (address.firstLine == null) address.firstLine = Address.DEFAULT_FIRST_LINE; - } - } -} diff --git a/extras/src/test/java/com/google/gson/typeadapters/PostConstructAdapterFactoryTest.java b/extras/src/test/java/com/google/gson/typeadapters/PostConstructAdapterFactoryTest.java deleted file mode 100644 index 65053bacbf..0000000000 --- a/extras/src/test/java/com/google/gson/typeadapters/PostConstructAdapterFactoryTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2016 Gson Authors - * - * 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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import java.util.Arrays; -import java.util.List; -import javax.annotation.PostConstruct; -import org.junit.Test; - -public class PostConstructAdapterFactoryTest { - @Test - public void test() throws Exception { - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(new PostConstructAdapterFactory()) - .create(); - gson.fromJson("{\"bread\": \"white\", \"cheese\": \"cheddar\"}", Sandwich.class); - try { - gson.fromJson("{\"bread\": \"cheesey bread\", \"cheese\": \"swiss\"}", Sandwich.class); - fail(); - } catch (IllegalArgumentException expected) { - assertEquals("too cheesey", expected.getMessage()); - } - } - - @Test - public void testList() { - MultipleSandwiches sandwiches = new MultipleSandwiches(Arrays.asList( - new Sandwich("white", "cheddar"), - new Sandwich("whole wheat", "swiss"))); - - Gson gson = new GsonBuilder().registerTypeAdapterFactory(new PostConstructAdapterFactory()).create(); - - // Throws NullPointerException without the fix in https://github.com/google/gson/pull/1103 - String json = gson.toJson(sandwiches); - assertEquals("{\"sandwiches\":[{\"bread\":\"white\",\"cheese\":\"cheddar\"},{\"bread\":\"whole wheat\",\"cheese\":\"swiss\"}]}", json); - - MultipleSandwiches sandwichesFromJson = gson.fromJson(json, MultipleSandwiches.class); - assertEquals(sandwiches, sandwichesFromJson); - } - - @SuppressWarnings("overrides") // for missing hashCode() override - static class Sandwich { - public String bread; - public String cheese; - - public Sandwich(String bread, String cheese) { - this.bread = bread; - this.cheese = cheese; - } - - @PostConstruct private void validate() { - if (bread.equals("cheesey bread") && cheese != null) { - throw new IllegalArgumentException("too cheesey"); - } - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Sandwich)) { - return false; - } - final Sandwich other = (Sandwich) o; - if (this.bread == null ? other.bread != null : !this.bread.equals(other.bread)) { - return false; - } - if (this.cheese == null ? other.cheese != null : !this.cheese.equals(other.cheese)) { - return false; - } - return true; - } - } - - @SuppressWarnings("overrides") // for missing hashCode() override - static class MultipleSandwiches { - public List sandwiches; - - public MultipleSandwiches(List sandwiches) { - this.sandwiches = sandwiches; - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof MultipleSandwiches)) { - return false; - } - final MultipleSandwiches other = (MultipleSandwiches) o; - if (this.sandwiches == null ? other.sandwiches != null : !this.sandwiches.equals(other.sandwiches)) { - return false; - } - return true; - } - } -} diff --git a/extras/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java b/extras/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java deleted file mode 100644 index 1221b47bcc..0000000000 --- a/extras/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * 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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapterFactory; -import org.junit.Test; - -public final class RuntimeTypeAdapterFactoryTest { - - @Test - public void testRuntimeTypeAdapter() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class) - .registerSubtype(CreditCard.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(rta) - .create(); - - CreditCard original = new CreditCard("Jesse", 234); - assertEquals("{\"type\":\"CreditCard\",\"cvv\":234,\"ownerName\":\"Jesse\"}", - gson.toJson(original, BillingInstrument.class)); - BillingInstrument deserialized = gson.fromJson( - "{type:'CreditCard',cvv:234,ownerName:'Jesse'}", BillingInstrument.class); - assertEquals("Jesse", deserialized.ownerName); - assertTrue(deserialized instanceof CreditCard); - } - - @Test - public void testRuntimeTypeAdapterRecognizeSubtypes() { - // We don't have an explicit factory for CreditCard.class, but we do have one for - // BillingInstrument.class that has recognizeSubtypes(). So it should recognize CreditCard, and - // when we call gson.toJson(original) below, without an explicit type, it should be invoked. - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class) - .recognizeSubtypes() - .registerSubtype(CreditCard.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(rta) - .create(); - - CreditCard original = new CreditCard("Jesse", 234); - assertEquals("{\"type\":\"CreditCard\",\"cvv\":234,\"ownerName\":\"Jesse\"}", - gson.toJson(original)); - BillingInstrument deserialized = gson.fromJson( - "{type:'CreditCard',cvv:234,ownerName:'Jesse'}", BillingInstrument.class); - assertEquals("Jesse", deserialized.ownerName); - assertTrue(deserialized instanceof CreditCard); - } - - @Test - public void testRuntimeTypeIsBaseType() { - TypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class) - .registerSubtype(BillingInstrument.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(rta) - .create(); - - BillingInstrument original = new BillingInstrument("Jesse"); - assertEquals("{\"type\":\"BillingInstrument\",\"ownerName\":\"Jesse\"}", - gson.toJson(original, BillingInstrument.class)); - BillingInstrument deserialized = gson.fromJson( - "{type:'BillingInstrument',ownerName:'Jesse'}", BillingInstrument.class); - assertEquals("Jesse", deserialized.ownerName); - } - - @Test - public void testNullBaseType() { - try { - RuntimeTypeAdapterFactory.of(null); - fail(); - } catch (NullPointerException expected) { - } - } - - @Test - public void testNullTypeFieldName() { - try { - RuntimeTypeAdapterFactory.of(BillingInstrument.class, null); - fail(); - } catch (NullPointerException expected) { - } - } - - @Test - public void testNullSubtype() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class); - try { - rta.registerSubtype(null); - fail(); - } catch (NullPointerException expected) { - } - } - - @Test - public void testNullLabel() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class); - try { - rta.registerSubtype(CreditCard.class, null); - fail(); - } catch (NullPointerException expected) { - } - } - - @Test - public void testDuplicateSubtype() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class); - rta.registerSubtype(CreditCard.class, "CC"); - try { - rta.registerSubtype(CreditCard.class, "Visa"); - fail(); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testDuplicateLabel() { - RuntimeTypeAdapterFactory rta = RuntimeTypeAdapterFactory.of( - BillingInstrument.class); - rta.registerSubtype(CreditCard.class, "CC"); - try { - rta.registerSubtype(BankTransfer.class, "CC"); - fail(); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testDeserializeMissingTypeField() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class) - .registerSubtype(CreditCard.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); - try { - gson.fromJson("{ownerName:'Jesse'}", BillingInstrument.class); - fail(); - } catch (JsonParseException expected) { - } - } - - @Test - public void testDeserializeMissingSubtype() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class) - .registerSubtype(BankTransfer.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); - try { - gson.fromJson("{type:'CreditCard',ownerName:'Jesse'}", BillingInstrument.class); - fail(); - } catch (JsonParseException expected) { - } - } - - @Test - public void testSerializeMissingSubtype() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class) - .registerSubtype(BankTransfer.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); - try { - gson.toJson(new CreditCard("Jesse", 456), BillingInstrument.class); - fail(); - } catch (JsonParseException expected) { - } - } - - @Test - public void testSerializeCollidingTypeFieldName() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class, "cvv") - .registerSubtype(CreditCard.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); - try { - gson.toJson(new CreditCard("Jesse", 456), BillingInstrument.class); - fail(); - } catch (JsonParseException expected) { - } - } - - @Test - public void testSerializeWrappedNullValue() { - TypeAdapterFactory billingAdapter = RuntimeTypeAdapterFactory.of(BillingInstrument.class) - .registerSubtype(CreditCard.class) - .registerSubtype(BankTransfer.class); - Gson gson = new GsonBuilder() - .registerTypeAdapterFactory(billingAdapter) - .create(); - String serialized = gson.toJson(new BillingInstrumentWrapper(null), BillingInstrumentWrapper.class); - BillingInstrumentWrapper deserialized = gson.fromJson(serialized, BillingInstrumentWrapper.class); - assertNull(deserialized.instrument); - } - - static class BillingInstrumentWrapper { - BillingInstrument instrument; - BillingInstrumentWrapper(BillingInstrument instrument) { - this.instrument = instrument; - } - } - - static class BillingInstrument { - private final String ownerName; - BillingInstrument(String ownerName) { - this.ownerName = ownerName; - } - } - - static class CreditCard extends BillingInstrument { - int cvv; - CreditCard(String ownerName, int cvv) { - super(ownerName); - this.cvv = cvv; - } - } - - static class BankTransfer extends BillingInstrument { - int bankAccount; - BankTransfer(String ownerName, int bankAccount) { - super(ownerName); - this.bankAccount = bankAccount; - } - } -} diff --git a/extras/src/test/java/com/google/gson/typeadapters/UtcDateTypeAdapterTest.java b/extras/src/test/java/com/google/gson/typeadapters/UtcDateTypeAdapterTest.java deleted file mode 100644 index b520183528..0000000000 --- a/extras/src/test/java/com/google/gson/typeadapters/UtcDateTypeAdapterTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; -import org.junit.Test; - -public final class UtcDateTypeAdapterTest { - private final Gson gson = new GsonBuilder() - .registerTypeAdapter(Date.class, new UtcDateTypeAdapter()) - .create(); - - @Test - public void testLocalTimeZone() { - Date expected = new Date(); - String json = gson.toJson(expected); - Date actual = gson.fromJson(json, Date.class); - assertEquals(expected.getTime(), actual.getTime()); - } - - @Test - public void testDifferentTimeZones() { - for (String timeZone : TimeZone.getAvailableIDs()) { - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(timeZone)); - Date expected = cal.getTime(); - String json = gson.toJson(expected); - // System.out.println(json + ": " + timeZone); - Date actual = gson.fromJson(json, Date.class); - assertEquals(expected.getTime(), actual.getTime()); - } - } - - /** - * JDK 1.7 introduced support for XXX format to indicate UTC date. But Android is older JDK. - * We want to make sure that this date is parseable in Android. - */ - @Test - public void testUtcDatesOnJdkBefore1_7() { - Gson gson = new GsonBuilder() - .registerTypeAdapter(Date.class, new UtcDateTypeAdapter()) - .create(); - gson.fromJson("'2014-12-05T04:00:00.000Z'", Date.class); - } - - @Test - public void testUtcWithJdk7Default() { - Date expected = new Date(); - SimpleDateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.US); - iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); - String expectedJson = "\"" + iso8601Format.format(expected) + "\""; - String actualJson = gson.toJson(expected); - assertEquals(expectedJson, actualJson); - Date actual = gson.fromJson(expectedJson, Date.class); - assertEquals(expected.getTime(), actual.getTime()); - } - - @Test - public void testNullDateSerialization() { - String json = gson.toJson(null, Date.class); - assertEquals("null", json); - } - - @Test - public void testWellFormedParseException() { - try { - gson.fromJson("2017-06-20T14:32:30", Date.class); - fail("No exception"); - } catch (JsonParseException exe) { - assertEquals("java.text.ParseException: Failed to parse date ['2017-06-20T14']: 2017-06-20T14", exe.getMessage()); - } - } -} diff --git a/gson/pom.xml b/gson/pom.xml index d66e1dcb58..b4b7192c41 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -2,9 +2,9 @@ 4.0.0 - com.google.code.gson + com.firework.gson gson-parent - 2.10.1 + 2.10.1.0 gson @@ -123,12 +123,12 @@ - ${project.build.directory}/test-classes/com/google/gson/functional/EnumWithObfuscatedTest.class - ${project.build.directory}/test-classes-obfuscated-injar/com/google/gson/functional/EnumWithObfuscatedTest.class + ${project.build.directory}/test-classes/com/firework/gson/functional/EnumWithObfuscatedTest.class + ${project.build.directory}/test-classes-obfuscated-injar/com/firework/gson/functional/EnumWithObfuscatedTest.class - ${project.build.directory}/test-classes/com/google/gson/functional/EnumWithObfuscatedTest$Gender.class - ${project.build.directory}/test-classes-obfuscated-injar/com/google/gson/functional/EnumWithObfuscatedTest$Gender.class + ${project.build.directory}/test-classes/com/firework/gson/functional/EnumWithObfuscatedTest$Gender.class + ${project.build.directory}/test-classes-obfuscated-injar/com/firework/gson/functional/EnumWithObfuscatedTest$Gender.class @@ -171,10 +171,10 @@ copy-resources - ${project.build.directory}/test-classes/com/google/gson/functional + ${project.build.directory}/test-classes/com/firework/gson/functional - ${project.build.directory}/test-classes-obfuscated-outjar/com/google/gson/functional + ${project.build.directory}/test-classes-obfuscated-outjar/com/firework/gson/functional EnumWithObfuscatedTest.class EnumWithObfuscatedTest$Gender.class @@ -233,7 +233,7 @@ org.apache.maven.plugins maven-javadoc-plugin - com.google.gson.internal:com.google.gson.internal.bind + com.firework.gson.internal:com.firework.gson.internal.bind diff --git a/metrics/README.md b/metrics/README.md deleted file mode 100644 index 8c95485de2..0000000000 --- a/metrics/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# metrics - -This Maven module contains the source code for running internal benchmark tests against Gson. diff --git a/metrics/pom.xml b/metrics/pom.xml deleted file mode 100644 index 7a9dc18ee0..0000000000 --- a/metrics/pom.xml +++ /dev/null @@ -1,82 +0,0 @@ - - 4.0.0 - - com.google.code.gson - gson-parent - 2.10.1 - - - gson-metrics - 2011 - Gson Metrics - Performance Metrics for Google Gson library - - - - Apache-2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - Google, Inc. - https://www.google.com - - - - - com.google.code.gson - gson - ${project.parent.version} - - - com.fasterxml.jackson.core - jackson-databind - 2.14.1 - - - com.google.caliper - caliper - 1.0-beta-3 - - - - - - - - com.github.siom79.japicmp - japicmp-maven-plugin - 0.17.1 - - - true - - - - org.apache.maven.plugins - maven-deploy-plugin - - - true - - - - - - - - - Inderjeet Singh - Google Inc. - - - Joel Leitch - Google Inc. - - - Jesse Wilson - Google Inc. - - - diff --git a/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitives.java b/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitives.java deleted file mode 100644 index a073974d4f..0000000000 --- a/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitives.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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.metrics; - -/** - * Class with a bunch of primitive fields - * - * @author Inderjeet Singh - */ -public class BagOfPrimitives { - public static final long DEFAULT_VALUE = 0; - public long longValue; - public int intValue; - public boolean booleanValue; - public String stringValue; - - public BagOfPrimitives() { - this(DEFAULT_VALUE, 0, false, ""); - } - - public BagOfPrimitives(long longValue, int intValue, boolean booleanValue, String stringValue) { - this.longValue = longValue; - this.intValue = intValue; - this.booleanValue = booleanValue; - this.stringValue = stringValue; - } - - public int getIntValue() { - return intValue; - } - - public String getExpectedJson() { - return "{" - + "\"longValue\":" + longValue + "," - + "\"intValue\":" + intValue + "," - + "\"booleanValue\":" + booleanValue + "," - + "\"stringValue\":\"" + stringValue + "\"" - + "}"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (booleanValue ? 1231 : 1237); - result = prime * result + intValue; - result = prime * result + (int) (longValue ^ (longValue >>> 32)); - result = prime * result + ((stringValue == null) ? 0 : stringValue.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - BagOfPrimitives other = (BagOfPrimitives) obj; - if (booleanValue != other.booleanValue) return false; - if (intValue != other.intValue) return false; - if (longValue != other.longValue) return false; - if (stringValue == null) { - return other.stringValue == null; - } else { - return stringValue.equals(other.stringValue); - } - } - - @Override - public String toString() { - return String.format("(longValue=%d,intValue=%d,booleanValue=%b,stringValue=%s)", - longValue, intValue, booleanValue, stringValue); - } -} diff --git a/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitivesDeserializationBenchmark.java b/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitivesDeserializationBenchmark.java deleted file mode 100644 index 80881dc8e6..0000000000 --- a/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitivesDeserializationBenchmark.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.metrics; - -import com.google.caliper.BeforeExperiment; -import com.google.gson.Gson; -import com.google.gson.stream.JsonReader; -import java.io.IOException; -import java.io.StringReader; -import java.lang.reflect.Field; - -/** - * Caliper based micro benchmarks for Gson - * - * @author Inderjeet Singh - * @author Jesse Wilson - * @author Joel Leitch - */ -public class BagOfPrimitivesDeserializationBenchmark { - - private Gson gson; - private String json; - - public static void main(String[] args) { - NonUploadingCaliperRunner.run(BagOfPrimitivesDeserializationBenchmark.class, args); - } - - @BeforeExperiment - void setUp() throws Exception { - this.gson = new Gson(); - BagOfPrimitives bag = new BagOfPrimitives(10L, 1, false, "foo"); - this.json = gson.toJson(bag); - } - - /** - * Benchmark to measure Gson performance for deserializing an object - */ - public void timeBagOfPrimitivesDefault(int reps) { - for (int i=0; i fieldType = field.getType(); - if (fieldType.equals(long.class)) { - field.setLong(bag, jr.nextLong()); - } else if (fieldType.equals(int.class)) { - field.setInt(bag, jr.nextInt()); - } else if (fieldType.equals(boolean.class)) { - field.setBoolean(bag, jr.nextBoolean()); - } else if (fieldType.equals(String.class)) { - field.set(bag, jr.nextString()); - } else { - throw new RuntimeException("Unexpected: type: " + fieldType + ", name: " + name); - } - } - } - } - jr.endObject(); - } - } -} diff --git a/metrics/src/main/java/com/google/gson/metrics/CollectionsDeserializationBenchmark.java b/metrics/src/main/java/com/google/gson/metrics/CollectionsDeserializationBenchmark.java deleted file mode 100644 index 738b5ae4bf..0000000000 --- a/metrics/src/main/java/com/google/gson/metrics/CollectionsDeserializationBenchmark.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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.metrics; - -import com.google.caliper.BeforeExperiment; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import java.io.IOException; -import java.io.StringReader; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -/** - * Caliper based micro benchmarks for Gson - * - * @author Inderjeet Singh - */ -public class CollectionsDeserializationBenchmark { - - private static final TypeToken> LIST_TYPE_TOKEN = new TypeToken>(){}; - private static final Type LIST_TYPE = LIST_TYPE_TOKEN.getType(); - private Gson gson; - private String json; - - public static void main(String[] args) { - NonUploadingCaliperRunner.run(CollectionsDeserializationBenchmark.class, args); - } - - @BeforeExperiment - void setUp() throws Exception { - this.gson = new Gson(); - List bags = new ArrayList<>(); - for (int i = 0; i < 100; ++i) { - bags.add(new BagOfPrimitives(10L, 1, false, "foo")); - } - this.json = gson.toJson(bags, LIST_TYPE); - } - - /** - * Benchmark to measure Gson performance for deserializing an object - */ - public void timeCollectionsDefault(int reps) { - for (int i=0; i bags = new ArrayList<>(); - while(jr.hasNext()) { - jr.beginObject(); - long longValue = 0; - int intValue = 0; - boolean booleanValue = false; - String stringValue = null; - while(jr.hasNext()) { - String name = jr.nextName(); - if (name.equals("longValue")) { - longValue = jr.nextLong(); - } else if (name.equals("intValue")) { - intValue = jr.nextInt(); - } else if (name.equals("booleanValue")) { - booleanValue = jr.nextBoolean(); - } else if (name.equals("stringValue")) { - stringValue = jr.nextString(); - } else { - throw new IOException("Unexpected name: " + name); - } - } - jr.endObject(); - bags.add(new BagOfPrimitives(longValue, intValue, booleanValue, stringValue)); - } - jr.endArray(); - } - } - - /** - * This benchmark measures the ideal Gson performance: the cost of parsing a JSON stream and - * setting object values by reflection. We should strive to reduce the discrepancy between this - * and {@link #timeCollectionsDefault(int)} . - */ - public void timeCollectionsReflectionStreaming(int reps) throws Exception { - for (int i=0; i bags = new ArrayList<>(); - while(jr.hasNext()) { - jr.beginObject(); - BagOfPrimitives bag = new BagOfPrimitives(); - while(jr.hasNext()) { - String name = jr.nextName(); - for (Field field : BagOfPrimitives.class.getDeclaredFields()) { - if (field.getName().equals(name)) { - Class fieldType = field.getType(); - if (fieldType.equals(long.class)) { - field.setLong(bag, jr.nextLong()); - } else if (fieldType.equals(int.class)) { - field.setInt(bag, jr.nextInt()); - } else if (fieldType.equals(boolean.class)) { - field.setBoolean(bag, jr.nextBoolean()); - } else if (fieldType.equals(String.class)) { - field.set(bag, jr.nextString()); - } else { - throw new RuntimeException("Unexpected: type: " + fieldType + ", name: " + name); - } - } - } - } - jr.endObject(); - bags.add(bag); - } - jr.endArray(); - } - } -} diff --git a/metrics/src/main/java/com/google/gson/metrics/NonUploadingCaliperRunner.java b/metrics/src/main/java/com/google/gson/metrics/NonUploadingCaliperRunner.java deleted file mode 100644 index 80633e1408..0000000000 --- a/metrics/src/main/java/com/google/gson/metrics/NonUploadingCaliperRunner.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.google.gson.metrics; - -import com.google.caliper.runner.CaliperMain; - -class NonUploadingCaliperRunner { - private static String[] concat(String first, String... others) { - if (others.length == 0) { - return new String[] { first }; - } else { - String[] result = new String[others.length + 1]; - result[0] = first; - System.arraycopy(others, 0, result, 1, others.length); - return result; - } - } - - public static void run(Class c, String[] args) { - // Disable result upload; Caliper uploads results to webapp by default, see https://github.com/google/caliper/issues/356 - CaliperMain.main(c, concat("-Cresults.upload.options.url=", args)); - } -} diff --git a/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java b/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java deleted file mode 100644 index e6a4530239..0000000000 --- a/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java +++ /dev/null @@ -1,428 +0,0 @@ -/* - * 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.metrics; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonFactoryBuilder; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.google.caliper.BeforeExperiment; -import com.google.caliper.Param; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParser; -import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import java.io.CharArrayReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.lang.reflect.Type; -import java.net.URL; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * Measure Gson and Jackson parsing and binding performance. - * - *

This benchmark requires that ParseBenchmarkData.zip is on the classpath. - * That file contains Twitter feed data, which is representative of what - * applications will be parsing. - */ -public final class ParseBenchmark { - @Param Document document; - @Param Api api; - - private enum Document { - TWEETS(new TypeToken>() {}, new TypeReference>() {}), - READER_SHORT(new TypeToken() {}, new TypeReference() {}), - READER_LONG(new TypeToken() {}, new TypeReference() {}); - - private final TypeToken gsonType; - private final TypeReference jacksonType; - - private Document(TypeToken typeToken, TypeReference typeReference) { - this.gsonType = typeToken; - this.jacksonType = typeReference; - } - } - - private enum Api { - JACKSON_STREAM { - @Override Parser newParser() { - return new JacksonStreamParser(); - } - }, - JACKSON_BIND { - @Override Parser newParser() { - return new JacksonBindParser(); - } - }, - GSON_STREAM { - @Override Parser newParser() { - return new GsonStreamParser(); - } - }, - GSON_SKIP { - @Override Parser newParser() { - return new GsonSkipParser(); - } - }, - GSON_DOM { - @Override Parser newParser() { - return new GsonDomParser(); - } - }, - GSON_BIND { - @Override Parser newParser() { - return new GsonBindParser(); - } - }; - abstract Parser newParser(); - } - - private char[] text; - private Parser parser; - - @BeforeExperiment - void setUp() throws Exception { - text = resourceToString(document.name() + ".json").toCharArray(); - parser = api.newParser(); - } - - public void timeParse(int reps) throws Exception { - for (int i = 0; i < reps; i++) { - parser.parse(text, document); - } - } - - private static File getResourceFile(String path) throws Exception { - URL url = ParseBenchmark.class.getResource(path); - if (url == null) { - throw new IllegalArgumentException("Resource " + path + " does not exist"); - } - File file = new File(url.toURI()); - if (!file.isFile()) { - throw new IllegalArgumentException("Resource " + path + " is not a file"); - } - return file; - } - - private static String resourceToString(String fileName) throws Exception { - ZipFile zipFile = new ZipFile(getResourceFile("/ParseBenchmarkData.zip")); - try { - ZipEntry zipEntry = zipFile.getEntry(fileName); - Reader reader = new InputStreamReader(zipFile.getInputStream(zipEntry)); - char[] buffer = new char[8192]; - StringWriter writer = new StringWriter(); - int count; - while ((count = reader.read(buffer)) != -1) { - writer.write(buffer, 0, count); - } - reader.close(); - return writer.toString(); - - } finally { - zipFile.close(); - } - } - - public static void main(String[] args) throws Exception { - NonUploadingCaliperRunner.run(ParseBenchmark.class, args); - } - - interface Parser { - void parse(char[] data, Document document) throws Exception; - } - - private static class GsonStreamParser implements Parser { - @Override - public void parse(char[] data, Document document) throws Exception { - com.google.gson.stream.JsonReader jsonReader - = new com.google.gson.stream.JsonReader(new CharArrayReader(data)); - readToken(jsonReader); - jsonReader.close(); - } - - private void readToken(com.google.gson.stream.JsonReader reader) throws IOException { - while (true) { - switch (reader.peek()) { - case BEGIN_ARRAY: - reader.beginArray(); - break; - case END_ARRAY: - reader.endArray(); - break; - case BEGIN_OBJECT: - reader.beginObject(); - break; - case END_OBJECT: - reader.endObject(); - break; - case NAME: - reader.nextName(); - break; - case BOOLEAN: - reader.nextBoolean(); - break; - case NULL: - reader.nextNull(); - break; - case NUMBER: - reader.nextLong(); - break; - case STRING: - reader.nextString(); - break; - case END_DOCUMENT: - return; - default: - throw new IllegalArgumentException("Unexpected token" + reader.peek()); - } - } - } - } - - private static class GsonSkipParser implements Parser { - @Override - public void parse(char[] data, Document document) throws Exception { - com.google.gson.stream.JsonReader jsonReader - = new com.google.gson.stream.JsonReader(new CharArrayReader(data)); - jsonReader.skipValue(); - jsonReader.close(); - } - } - - private static class JacksonStreamParser implements Parser { - @Override - public void parse(char[] data, Document document) throws Exception { - JsonFactory jsonFactory = new JsonFactoryBuilder().configure(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES, false).build(); - com.fasterxml.jackson.core.JsonParser jp = jsonFactory.createParser(new CharArrayReader(data)); - int depth = 0; - do { - JsonToken token = jp.nextToken(); - switch (token) { - case START_OBJECT: - case START_ARRAY: - depth++; - break; - case END_OBJECT: - case END_ARRAY: - depth--; - break; - case FIELD_NAME: - jp.getCurrentName(); - break; - case VALUE_STRING: - jp.getText(); - break; - case VALUE_NUMBER_INT: - case VALUE_NUMBER_FLOAT: - jp.getLongValue(); - break; - case VALUE_TRUE: - case VALUE_FALSE: - jp.getBooleanValue(); - break; - case VALUE_NULL: - // Do nothing; nextToken() will advance in stream - break; - default: - throw new IllegalArgumentException("Unexpected token " + token); - } - } while (depth > 0); - jp.close(); - } - } - - private static class GsonDomParser implements Parser { - @Override - public void parse(char[] data, Document document) throws Exception { - JsonParser.parseReader(new CharArrayReader(data)); - } - } - - private static class GsonBindParser implements Parser { - private static Gson gson = new GsonBuilder() - .setDateFormat("EEE MMM dd HH:mm:ss Z yyyy") - .create(); - - @Override - public void parse(char[] data, Document document) throws Exception { - gson.fromJson(new CharArrayReader(data), document.gsonType); - } - } - - private static class JacksonBindParser implements Parser { - private static final ObjectMapper mapper; - - static { - mapper = JsonMapper.builder() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(MapperFeature.AUTO_DETECT_FIELDS, true) - .build(); - mapper.setDateFormat(new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.ENGLISH)); - } - - @Override - public void parse(char[] data, Document document) throws Exception { - mapper.readValue(new CharArrayReader(data), document.jacksonType); - } - } - - static class Tweet { - @JsonProperty String coordinates; - @JsonProperty boolean favorited; - @JsonProperty Date created_at; - @JsonProperty boolean truncated; - @JsonProperty Tweet retweeted_status; - @JsonProperty String id_str; - @JsonProperty String in_reply_to_id_str; - @JsonProperty String contributors; - @JsonProperty String text; - @JsonProperty long id; - @JsonProperty String retweet_count; - @JsonProperty String in_reply_to_status_id_str; - @JsonProperty Object geo; - @JsonProperty boolean retweeted; - @JsonProperty String in_reply_to_user_id; - @JsonProperty String in_reply_to_screen_name; - @JsonProperty Object place; - @JsonProperty User user; - @JsonProperty String source; - @JsonProperty String in_reply_to_user_id_str; - } - - static class User { - @JsonProperty String name; - @JsonProperty String profile_sidebar_border_color; - @JsonProperty boolean profile_background_tile; - @JsonProperty String profile_sidebar_fill_color; - @JsonProperty Date created_at; - @JsonProperty String location; - @JsonProperty String profile_image_url; - @JsonProperty boolean follow_request_sent; - @JsonProperty String profile_link_color; - @JsonProperty boolean is_translator; - @JsonProperty String id_str; - @JsonProperty int favourites_count; - @JsonProperty boolean contributors_enabled; - @JsonProperty String url; - @JsonProperty boolean default_profile; - @JsonProperty long utc_offset; - @JsonProperty long id; - @JsonProperty boolean profile_use_background_image; - @JsonProperty int listed_count; - @JsonProperty String lang; - @JsonProperty("protected") @SerializedName("protected") boolean isProtected; - @JsonProperty int followers_count; - @JsonProperty String profile_text_color; - @JsonProperty String profile_background_color; - @JsonProperty String time_zone; - @JsonProperty String description; - @JsonProperty boolean notifications; - @JsonProperty boolean geo_enabled; - @JsonProperty boolean verified; - @JsonProperty String profile_background_image_url; - @JsonProperty boolean defalut_profile_image; - @JsonProperty int friends_count; - @JsonProperty int statuses_count; - @JsonProperty String screen_name; - @JsonProperty boolean following; - @JsonProperty boolean show_all_inline_media; - } - - static class Feed { - @JsonProperty String id; - @JsonProperty String title; - @JsonProperty String description; - @JsonProperty("alternate") @SerializedName("alternate") List alternates; - @JsonProperty long updated; - @JsonProperty List items; - - @Override public String toString() { - StringBuilder result = new StringBuilder() - .append(id) - .append("\n").append(title) - .append("\n").append(description) - .append("\n").append(alternates) - .append("\n").append(updated); - int i = 1; - for (Item item : items) { - result.append(i++).append(": ").append(item).append("\n\n"); - } - return result.toString(); - } - } - - static class Link { - @JsonProperty String href; - - @Override public String toString() { - return href; - } - } - - static class Item { - @JsonProperty List categories; - @JsonProperty String title; - @JsonProperty long published; - @JsonProperty long updated; - @JsonProperty("alternate") @SerializedName("alternate") List alternates; - @JsonProperty Content content; - @JsonProperty String author; - @JsonProperty List likingUsers; - - @Override public String toString() { - return title - + "\nauthor: " + author - + "\npublished: " + published - + "\nupdated: " + updated - + "\n" + content - + "\nliking users: " + likingUsers - + "\nalternates: " + alternates - + "\ncategories: " + categories; - } - } - - static class Content { - @JsonProperty String content; - - @Override public String toString() { - return content; - } - } - - static class ReaderUser { - @JsonProperty String userId; - - @Override public String toString() { - return userId; - } - } -} diff --git a/metrics/src/main/java/com/google/gson/metrics/SerializationBenchmark.java b/metrics/src/main/java/com/google/gson/metrics/SerializationBenchmark.java deleted file mode 100644 index bf67a66b05..0000000000 --- a/metrics/src/main/java/com/google/gson/metrics/SerializationBenchmark.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.metrics; - -import com.google.caliper.BeforeExperiment; -import com.google.caliper.Param; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -/** - * Caliper based micro benchmarks for Gson serialization - * - * @author Inderjeet Singh - * @author Jesse Wilson - * @author Joel Leitch - */ -public class SerializationBenchmark { - - private Gson gson; - private BagOfPrimitives bag; - @Param - private boolean pretty; - - public static void main(String[] args) { - NonUploadingCaliperRunner.run(SerializationBenchmark.class, args); - } - - @BeforeExperiment - void setUp() throws Exception { - this.gson = pretty ? new GsonBuilder().setPrettyPrinting().create() : new Gson(); - this.bag = new BagOfPrimitives(10L, 1, false, "foo"); - } - - public void timeObjectSerialization(int reps) { - for (int i=0; i gson - extras - metrics - proto diff --git a/proto/.gitignore b/proto/.gitignore deleted file mode 100644 index f44578ac2b..0000000000 --- a/proto/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src/main/java/com/google/gson/protobuf/generated/ diff --git a/proto/README.md b/proto/README.md deleted file mode 100644 index c6f7906a68..0000000000 --- a/proto/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# proto - -This Maven module contains the source code for a JSON serializer and deserializer for -[Protocol Buffers (protobuf)](https://developers.google.com/protocol-buffers/docs/javatutorial) -messages. - -The artifacts created by this module are currently not deployed to Maven Central. diff --git a/proto/pom.xml b/proto/pom.xml deleted file mode 100644 index 74ab60c8ed..0000000000 --- a/proto/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - 4.0.0 - - com.google.code.gson - gson-parent - 2.10.1 - - - proto - Gson Protobuf Support - Gson support for Protobufs - - - - Apache-2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - - com.google.code.gson - gson - ${project.parent.version} - - - - com.google.protobuf - protobuf-java - 4.0.0-rc-2 - - - - com.google.guava - guava - 31.1-jre - - - - junit - junit - test - - - - com.google.truth - truth - 1.1.3 - test - - - - - gson-proto - - - - kr.motd.maven - os-maven-plugin - 1.7.1 - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.6.1 - - com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier} - - - - - test-compile - - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - - true - - - - - - - - - Inderjeet Singh - Google Inc. - - - diff --git a/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java b/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java deleted file mode 100644 index 9aa166fc68..0000000000 --- a/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2010 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.protobuf; - -import static java.util.Objects.requireNonNull; - -import com.google.common.base.CaseFormat; -import com.google.common.collect.MapMaker; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.google.protobuf.DescriptorProtos.EnumValueOptions; -import com.google.protobuf.DescriptorProtos.FieldOptions; -import com.google.protobuf.Descriptors.Descriptor; -import com.google.protobuf.Descriptors.EnumDescriptor; -import com.google.protobuf.Descriptors.EnumValueDescriptor; -import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.DynamicMessage; -import com.google.protobuf.Extension; -import com.google.protobuf.Message; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; - -/** - * GSON type adapter for protocol buffers that knows how to serialize enums either by using their - * values or their names, and also supports custom proto field names. - *

- * You can specify which case representation is used for the proto fields when writing/reading the - * JSON payload by calling {@link Builder#setFieldNameSerializationFormat(CaseFormat, CaseFormat)}. - *

- * An example of default serialization/deserialization using custom proto field names is shown - * below: - * - *

- * message MyMessage {
- *   // Will be serialized as 'osBuildID' instead of the default 'osBuildId'.
- *   string os_build_id = 1 [(serialized_name) = "osBuildID"];
- * }
- * 
- * - * @author Inderjeet Singh - * @author Emmanuel Cron - * @author Stanley Wang - */ -public class ProtoTypeAdapter - implements JsonSerializer, JsonDeserializer { - /** - * Determines how enum values should be serialized. - */ - public enum EnumSerialization { - /** - * Serializes and deserializes enum values using their number. When this is used, custom - * value names set on enums are ignored. - */ - NUMBER, - /** Serializes and deserializes enum values using their name. */ - NAME; - } - - /** - * Builder for {@link ProtoTypeAdapter}s. - */ - public static class Builder { - private final Set> serializedNameExtensions; - private final Set> serializedEnumValueExtensions; - private EnumSerialization enumSerialization; - private CaseFormat protoFormat; - private CaseFormat jsonFormat; - - private Builder(EnumSerialization enumSerialization, CaseFormat fromFieldNameFormat, - CaseFormat toFieldNameFormat) { - this.serializedNameExtensions = new HashSet<>(); - this.serializedEnumValueExtensions = new HashSet<>(); - setEnumSerialization(enumSerialization); - setFieldNameSerializationFormat(fromFieldNameFormat, toFieldNameFormat); - } - - public Builder setEnumSerialization(EnumSerialization enumSerialization) { - this.enumSerialization = requireNonNull(enumSerialization); - return this; - } - - /** - * Sets the field names serialization format. The first parameter defines how to read the format - * of the proto field names you are converting to JSON. The second parameter defines which - * format to use when serializing them. - *

- * For example, if you use the following parameters: {@link CaseFormat#LOWER_UNDERSCORE}, - * {@link CaseFormat#LOWER_CAMEL}, the following conversion will occur: - * - *

{@code
-     * PROTO     <->  JSON
-     * my_field       myField
-     * foo            foo
-     * n__id_ct       nIdCt
-     * }
- */ - public Builder setFieldNameSerializationFormat(CaseFormat fromFieldNameFormat, - CaseFormat toFieldNameFormat) { - this.protoFormat = fromFieldNameFormat; - this.jsonFormat = toFieldNameFormat; - return this; - } - - /** - * Adds a field proto annotation that, when set, overrides the default field name - * serialization/deserialization. For example, if you add the '{@code serialized_name}' - * annotation and you define a field in your proto like the one below: - * - *
-     * string client_app_id = 1 [(serialized_name) = "appId"];
-     * 
- * - * ...the adapter will serialize the field using '{@code appId}' instead of the default ' - * {@code clientAppId}'. This lets you customize the name serialization of any proto field. - */ - public Builder addSerializedNameExtension( - Extension serializedNameExtension) { - serializedNameExtensions.add(requireNonNull(serializedNameExtension)); - return this; - } - - /** - * Adds an enum value proto annotation that, when set, overrides the default enum value - * serialization/deserialization of this adapter. For example, if you add the ' - * {@code serialized_value}' annotation and you define an enum in your proto like the one below: - * - *
-     * enum MyEnum {
-     *   UNKNOWN = 0;
-     *   CLIENT_APP_ID = 1 [(serialized_value) = "APP_ID"];
-     *   TWO = 2 [(serialized_value) = "2"];
-     * }
-     * 
- * - * ...the adapter will serialize the value {@code CLIENT_APP_ID} as "{@code APP_ID}" and the - * value {@code TWO} as "{@code 2}". This works for both serialization and deserialization. - *

- * Note that you need to set the enum serialization of this adapter to - * {@link EnumSerialization#NAME}, otherwise these annotations will be ignored. - */ - public Builder addSerializedEnumValueExtension( - Extension serializedEnumValueExtension) { - serializedEnumValueExtensions.add(requireNonNull(serializedEnumValueExtension)); - return this; - } - - public ProtoTypeAdapter build() { - return new ProtoTypeAdapter(enumSerialization, protoFormat, jsonFormat, - serializedNameExtensions, serializedEnumValueExtensions); - } - } - - /** - * Creates a new {@link ProtoTypeAdapter} builder, defaulting enum serialization to - * {@link EnumSerialization#NAME} and converting field serialization from - * {@link CaseFormat#LOWER_UNDERSCORE} to {@link CaseFormat#LOWER_CAMEL}. - */ - public static Builder newBuilder() { - return new Builder(EnumSerialization.NAME, CaseFormat.LOWER_UNDERSCORE, CaseFormat.LOWER_CAMEL); - } - - private static final com.google.protobuf.Descriptors.FieldDescriptor.Type ENUM_TYPE = - com.google.protobuf.Descriptors.FieldDescriptor.Type.ENUM; - - private static final ConcurrentMap, Method>> mapOfMapOfMethods = - new MapMaker().makeMap(); - - private final EnumSerialization enumSerialization; - private final CaseFormat protoFormat; - private final CaseFormat jsonFormat; - private final Set> serializedNameExtensions; - private final Set> serializedEnumValueExtensions; - - private ProtoTypeAdapter(EnumSerialization enumSerialization, - CaseFormat protoFormat, - CaseFormat jsonFormat, - Set> serializedNameExtensions, - Set> serializedEnumValueExtensions) { - this.enumSerialization = enumSerialization; - this.protoFormat = protoFormat; - this.jsonFormat = jsonFormat; - this.serializedNameExtensions = serializedNameExtensions; - this.serializedEnumValueExtensions = serializedEnumValueExtensions; - } - - @Override - public JsonElement serialize(Message src, Type typeOfSrc, - JsonSerializationContext context) { - JsonObject ret = new JsonObject(); - final Map fields = src.getAllFields(); - - for (Map.Entry fieldPair : fields.entrySet()) { - final FieldDescriptor desc = fieldPair.getKey(); - String name = getCustSerializedName(desc.getOptions(), desc.getName()); - - if (desc.getType() == ENUM_TYPE) { - // Enum collections are also returned as ENUM_TYPE - if (fieldPair.getValue() instanceof Collection) { - // Build the array to avoid infinite loop - JsonArray array = new JsonArray(); - @SuppressWarnings("unchecked") - Collection enumDescs = - (Collection) fieldPair.getValue(); - for (EnumValueDescriptor enumDesc : enumDescs) { - array.add(context.serialize(getEnumValue(enumDesc))); - ret.add(name, array); - } - } else { - EnumValueDescriptor enumDesc = ((EnumValueDescriptor) fieldPair.getValue()); - ret.add(name, context.serialize(getEnumValue(enumDesc))); - } - } else { - ret.add(name, context.serialize(fieldPair.getValue())); - } - } - return ret; - } - - @Override - public Message deserialize(JsonElement json, Type typeOfT, - JsonDeserializationContext context) throws JsonParseException { - try { - JsonObject jsonObject = json.getAsJsonObject(); - @SuppressWarnings("unchecked") - Class protoClass = (Class) typeOfT; - - if (DynamicMessage.class.isAssignableFrom(protoClass)) { - throw new IllegalStateException("only generated messages are supported"); - } - - try { - // Invoke the ProtoClass.newBuilder() method - Message.Builder protoBuilder = - (Message.Builder) getCachedMethod(protoClass, "newBuilder").invoke(null); - - Message defaultInstance = - (Message) getCachedMethod(protoClass, "getDefaultInstance").invoke(null); - - Descriptor protoDescriptor = - (Descriptor) getCachedMethod(protoClass, "getDescriptor").invoke(null); - // Call setters on all of the available fields - for (FieldDescriptor fieldDescriptor : protoDescriptor.getFields()) { - String jsonFieldName = - getCustSerializedName(fieldDescriptor.getOptions(), fieldDescriptor.getName()); - - JsonElement jsonElement = jsonObject.get(jsonFieldName); - if (jsonElement != null && !jsonElement.isJsonNull()) { - // Do not reuse jsonFieldName here, it might have a custom value - Object fieldValue; - if (fieldDescriptor.getType() == ENUM_TYPE) { - if (jsonElement.isJsonArray()) { - // Handling array - Collection enumCollection = - new ArrayList<>(jsonElement.getAsJsonArray().size()); - for (JsonElement element : jsonElement.getAsJsonArray()) { - enumCollection.add( - findValueByNameAndExtension(fieldDescriptor.getEnumType(), element)); - } - fieldValue = enumCollection; - } else { - // No array, just a plain value - fieldValue = - findValueByNameAndExtension(fieldDescriptor.getEnumType(), jsonElement); - } - protoBuilder.setField(fieldDescriptor, fieldValue); - } else if (fieldDescriptor.isRepeated()) { - // If the type is an array, then we have to grab the type from the class. - // protobuf java field names are always lower camel case - String protoArrayFieldName = - protoFormat.to(CaseFormat.LOWER_CAMEL, fieldDescriptor.getName()) + "_"; - Field protoArrayField = protoClass.getDeclaredField(protoArrayFieldName); - Type protoArrayFieldType = protoArrayField.getGenericType(); - fieldValue = context.deserialize(jsonElement, protoArrayFieldType); - protoBuilder.setField(fieldDescriptor, fieldValue); - } else { - Object field = defaultInstance.getField(fieldDescriptor); - fieldValue = context.deserialize(jsonElement, field.getClass()); - protoBuilder.setField(fieldDescriptor, fieldValue); - } - } - } - return protoBuilder.build(); - } catch (SecurityException e) { - throw new JsonParseException(e); - } catch (NoSuchMethodException e) { - throw new JsonParseException(e); - } catch (IllegalArgumentException e) { - throw new JsonParseException(e); - } catch (IllegalAccessException e) { - throw new JsonParseException(e); - } catch (InvocationTargetException e) { - throw new JsonParseException(e); - } - } catch (Exception e) { - throw new JsonParseException("Error while parsing proto", e); - } - } - - /** - * Retrieves the custom field name from the given options, and if not found, returns the specified - * default name. - */ - private String getCustSerializedName(FieldOptions options, String defaultName) { - for (Extension extension : serializedNameExtensions) { - if (options.hasExtension(extension)) { - return options.getExtension(extension); - } - } - return protoFormat.to(jsonFormat, defaultName); - } - - /** - * Retrieves the custom enum value name from the given options, and if not found, returns the - * specified default value. - */ - private String getCustSerializedEnumValue(EnumValueOptions options, String defaultValue) { - for (Extension extension : serializedEnumValueExtensions) { - if (options.hasExtension(extension)) { - return options.getExtension(extension); - } - } - return defaultValue; - } - - /** - * Returns the enum value to use for serialization, depending on the value of - * {@link EnumSerialization} that was given to this adapter. - */ - private Object getEnumValue(EnumValueDescriptor enumDesc) { - if (enumSerialization == EnumSerialization.NAME) { - return getCustSerializedEnumValue(enumDesc.getOptions(), enumDesc.getName()); - } else { - return enumDesc.getNumber(); - } - } - - /** - * Finds an enum value in the given {@link EnumDescriptor} that matches the given JSON element, - * either by name if the current adapter is using {@link EnumSerialization#NAME}, otherwise by - * number. If matching by name, it uses the extension value if it is defined, otherwise it uses - * its default value. - * - * @throws IllegalArgumentException if a matching name/number was not found - */ - private EnumValueDescriptor findValueByNameAndExtension(EnumDescriptor desc, - JsonElement jsonElement) { - if (enumSerialization == EnumSerialization.NAME) { - // With enum name - for (EnumValueDescriptor enumDesc : desc.getValues()) { - String enumValue = getCustSerializedEnumValue(enumDesc.getOptions(), enumDesc.getName()); - if (enumValue.equals(jsonElement.getAsString())) { - return enumDesc; - } - } - throw new IllegalArgumentException( - String.format("Unrecognized enum name: %s", jsonElement.getAsString())); - } else { - // With enum value - EnumValueDescriptor fieldValue = desc.findValueByNumber(jsonElement.getAsInt()); - if (fieldValue == null) { - throw new IllegalArgumentException( - String.format("Unrecognized enum value: %d", jsonElement.getAsInt())); - } - return fieldValue; - } - } - - private static Method getCachedMethod(Class clazz, String methodName, - Class... methodParamTypes) throws NoSuchMethodException { - ConcurrentMap, Method> mapOfMethods = mapOfMapOfMethods.get(methodName); - if (mapOfMethods == null) { - mapOfMethods = new MapMaker().makeMap(); - ConcurrentMap, Method> previous = - mapOfMapOfMethods.putIfAbsent(methodName, mapOfMethods); - mapOfMethods = previous == null ? mapOfMethods : previous; - } - - Method method = mapOfMethods.get(clazz); - if (method == null) { - method = clazz.getMethod(methodName, methodParamTypes); - mapOfMethods.putIfAbsent(clazz, method); - // NB: it doesn't matter which method we return in the event of a race. - } - return method; - } - -} diff --git a/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithAnnotationsTest.java b/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithAnnotationsTest.java deleted file mode 100644 index 7fb92e89b4..0000000000 --- a/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithAnnotationsTest.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2010 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.protobuf.functional; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - -import com.google.common.base.CaseFormat; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; -import com.google.gson.protobuf.ProtoTypeAdapter; -import com.google.gson.protobuf.ProtoTypeAdapter.EnumSerialization; -import com.google.gson.protobuf.generated.Annotations; -import com.google.gson.protobuf.generated.Bag.OuterMessage; -import com.google.gson.protobuf.generated.Bag.ProtoWithAnnotations; -import com.google.gson.protobuf.generated.Bag.ProtoWithAnnotations.InnerMessage; -import com.google.gson.protobuf.generated.Bag.ProtoWithAnnotations.InnerMessage.Data; -import com.google.gson.protobuf.generated.Bag.ProtoWithAnnotations.InnerMessage.Type; -import com.google.protobuf.GeneratedMessageV3; -import org.junit.Before; -import org.junit.Test; - -/** - * Functional tests for protocol buffers using annotations for field names and enum values. - * - * @author Emmanuel Cron - */ -public class ProtosWithAnnotationsTest { - private Gson gson; - private Gson gsonWithEnumNumbers; - private Gson gsonWithLowerHyphen; - - @Before - public void setUp() throws Exception { - ProtoTypeAdapter.Builder protoTypeAdapter = ProtoTypeAdapter.newBuilder() - .setEnumSerialization(EnumSerialization.NAME) - .addSerializedNameExtension(Annotations.serializedName) - .addSerializedEnumValueExtension(Annotations.serializedValue); - gson = new GsonBuilder() - .registerTypeHierarchyAdapter(GeneratedMessageV3.class, protoTypeAdapter.build()) - .create(); - gsonWithEnumNumbers = new GsonBuilder() - .registerTypeHierarchyAdapter(GeneratedMessageV3.class, protoTypeAdapter - .setEnumSerialization(EnumSerialization.NUMBER) - .build()) - .create(); - gsonWithLowerHyphen = new GsonBuilder() - .registerTypeHierarchyAdapter(GeneratedMessageV3.class, protoTypeAdapter - .setFieldNameSerializationFormat(CaseFormat.LOWER_UNDERSCORE, CaseFormat.LOWER_HYPHEN) - .build()) - .create(); - } - - @Test - public void testProtoWithAnnotations_deserialize() { - String json = String.format("{ %n" - + " \"id\":\"41e5e7fd6065d101b97018a465ffff01\",%n" - + " \"expiration_date\":{ %n" - + " \"month\":\"12\",%n" - + " \"year\":\"2017\",%n" - + " \"timeStamp\":\"9864653135687\",%n" - + " \"countryCode5f55\":\"en_US\"%n" - + " },%n" - // Don't define innerMessage1 - + " \"innerMessage2\":{ %n" - // Set a number as a string; it should work - + " \"nIdCt\":\"98798465\",%n" - + " \"content\":\"text/plain\",%n" - + " \"$binary_data$\":[ %n" - + " { %n" - + " \"data\":\"OFIN8e9fhwoeh8((⁹8efywoih\",%n" - // Don't define width - + " \"height\":665%n" - + " },%n" - + " { %n" - // Define as an int; it should work - + " \"data\":65,%n" - + " \"width\":-56684%n" - // Don't define height - + " }%n" - + " ]%n" - + " },%n" - // Define a bunch of non recognizable data - + " \"non_existing\":\"foobar\",%n" - + " \"stillNot\":{ %n" - + " \"bunch\":\"of_useless data\"%n" - + " }%n" - + "}"); - ProtoWithAnnotations proto = gson.fromJson(json, ProtoWithAnnotations.class); - assertThat(proto.getId()).isEqualTo("41e5e7fd6065d101b97018a465ffff01"); - assertThat(proto.getOuterMessage()).isEqualTo(OuterMessage.newBuilder() - .setMonth(12) - .setYear(2017) - .setLongTimestamp(9864653135687L) - .setCountryCode5F55("en_US") - .build()); - assertThat(proto.hasInnerMessage1()).isFalse(); - assertThat(proto.getInnerMessage2()).isEqualTo(InnerMessage.newBuilder() - .setNIdCt(98798465) - .setContent(Type.TEXT) - .addData(Data.newBuilder() - .setData("OFIN8e9fhwoeh8((⁹8efywoih") - .setHeight(665)) - .addData(Data.newBuilder() - .setData("65") - .setWidth(-56684)) - .build()); - - String rebuilt = gson.toJson(proto); - assertThat(rebuilt).isEqualTo("{" - + "\"id\":\"41e5e7fd6065d101b97018a465ffff01\"," - + "\"expiration_date\":{" - + "\"month\":12," - + "\"year\":2017," - + "\"timeStamp\":9864653135687," - + "\"countryCode5f55\":\"en_US\"" - + "}," - + "\"innerMessage2\":{" - + "\"nIdCt\":98798465," - + "\"content\":\"text/plain\"," - + "\"$binary_data$\":[" - + "{" - + "\"data\":\"OFIN8e9fhwoeh8((⁹8efywoih\"," - + "\"height\":665" - + "}," - + "{" - + "\"data\":\"65\"," - + "\"width\":-56684" - + "}]}}"); - } - - @Test - public void testProtoWithAnnotations_deserializeUnknownEnumValue() { - String json = String.format("{ %n" - + " \"content\":\"UNKNOWN\"%n" - + "}"); - InnerMessage proto = gson.fromJson(json, InnerMessage.class); - assertThat(proto.getContent()).isEqualTo(Type.UNKNOWN); - } - - @Test - public void testProtoWithAnnotations_deserializeUnrecognizedEnumValue() { - String json = String.format("{ %n" - + " \"content\":\"UNRECOGNIZED\"%n" - + "}"); - try { - gson.fromJson(json, InnerMessage.class); - assertWithMessage("Should have thrown").fail(); - } catch (JsonParseException e) { - // expected - } - } - - @Test - public void testProtoWithAnnotations_deserializeWithEnumNumbers() { - String json = String.format("{ %n" - + " \"content\":\"0\"%n" - + "}"); - InnerMessage proto = gsonWithEnumNumbers.fromJson(json, InnerMessage.class); - assertThat(proto.getContent()).isEqualTo(Type.UNKNOWN); - String rebuilt = gsonWithEnumNumbers.toJson(proto); - assertThat(rebuilt).isEqualTo("{\"content\":0}"); - - json = String.format("{ %n" - + " \"content\":\"2\"%n" - + "}"); - proto = gsonWithEnumNumbers.fromJson(json, InnerMessage.class); - assertThat(proto.getContent()).isEqualTo(Type.IMAGE); - rebuilt = gsonWithEnumNumbers.toJson(proto); - assertThat(rebuilt).isEqualTo("{\"content\":2}"); - } - - @Test - public void testProtoWithAnnotations_serialize() { - ProtoWithAnnotations proto = ProtoWithAnnotations.newBuilder() - .setId("09f3j20839h032y0329hf30932h0nffn") - .setOuterMessage(OuterMessage.newBuilder() - .setMonth(14) - .setYear(6650) - .setLongTimestamp(468406876880768L)) - .setInnerMessage1(InnerMessage.newBuilder() - .setNIdCt(12) - .setContent(Type.IMAGE) - .addData(Data.newBuilder() - .setData("data$$") - .setWidth(200)) - .addData(Data.newBuilder() - .setHeight(56))) - .build(); - - String json = gsonWithLowerHyphen.toJson(proto); - assertThat(json).isEqualTo( - "{\"id\":\"09f3j20839h032y0329hf30932h0nffn\"," - + "\"expiration_date\":{" - + "\"month\":14," - + "\"year\":6650," - + "\"timeStamp\":468406876880768" - + "}," - // This field should be using hyphens - + "\"inner-message-1\":{" - + "\"n--id-ct\":12," - + "\"content\":2," - + "\"$binary_data$\":[" - + "{" - + "\"data\":\"data$$\"," - + "\"width\":200" - + "}," - + "{" - + "\"height\":56" - + "}]" - + "}" - + "}"); - - ProtoWithAnnotations rebuilt = gsonWithLowerHyphen.fromJson(json, ProtoWithAnnotations.class); - assertThat(rebuilt).isEqualTo(proto); - } -} diff --git a/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithComplexAndRepeatedFieldsTest.java b/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithComplexAndRepeatedFieldsTest.java deleted file mode 100644 index 53b1fe368b..0000000000 --- a/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithComplexAndRepeatedFieldsTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2010 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.protobuf.functional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import com.google.common.base.CaseFormat; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import com.google.gson.protobuf.ProtoTypeAdapter; -import com.google.gson.protobuf.ProtoTypeAdapter.EnumSerialization; -import com.google.gson.protobuf.generated.Bag.ProtoWithDifferentCaseFormat; -import com.google.gson.protobuf.generated.Bag.ProtoWithRepeatedFields; -import com.google.gson.protobuf.generated.Bag.SimpleProto; -import com.google.protobuf.GeneratedMessageV3; -import org.junit.Before; -import org.junit.Test; - -/** - * Functional tests for protocol buffers using complex and repeated fields - * - * @author Inderjeet Singh - */ -public class ProtosWithComplexAndRepeatedFieldsTest { - private Gson gson; - private Gson upperCamelGson; - - @Before - public void setUp() throws Exception { - gson = - new GsonBuilder() - .registerTypeHierarchyAdapter(GeneratedMessageV3.class, - ProtoTypeAdapter.newBuilder() - .setEnumSerialization(EnumSerialization.NUMBER) - .build()) - .create(); - upperCamelGson = - new GsonBuilder() - .registerTypeHierarchyAdapter( - GeneratedMessageV3.class, ProtoTypeAdapter.newBuilder() - .setFieldNameSerializationFormat( - CaseFormat.LOWER_UNDERSCORE, CaseFormat.UPPER_CAMEL) - .build()) - .create(); - } - - @Test - public void testSerializeRepeatedFields() { - ProtoWithRepeatedFields proto = ProtoWithRepeatedFields.newBuilder() - .addNumbers(2) - .addNumbers(3) - .addSimples(SimpleProto.newBuilder().setMsg("foo").build()) - .addSimples(SimpleProto.newBuilder().setCount(3).build()) - .build(); - String json = gson.toJson(proto); - assertTrue(json.contains("[2,3]")); - assertTrue(json.contains("foo")); - assertTrue(json.contains("count")); - } - - @Test - public void testDeserializeRepeatedFieldsProto() { - String json = "{numbers:[4,6],simples:[{msg:'bar'},{count:7}]}"; - ProtoWithRepeatedFields proto = - gson.fromJson(json, ProtoWithRepeatedFields.class); - assertEquals(4, proto.getNumbers(0)); - assertEquals(6, proto.getNumbers(1)); - assertEquals("bar", proto.getSimples(0).getMsg()); - assertEquals(7, proto.getSimples(1).getCount()); - } - - @Test - public void testSerializeDifferentCaseFormat() { - final ProtoWithDifferentCaseFormat proto = - ProtoWithDifferentCaseFormat.newBuilder() - .setAnotherField("foo") - .addNameThatTestsCaseFormat("bar") - .build(); - final JsonObject json = upperCamelGson.toJsonTree(proto).getAsJsonObject(); - assertEquals("foo", json.get("AnotherField").getAsString()); - assertEquals("bar", json.get("NameThatTestsCaseFormat").getAsJsonArray().get(0).getAsString()); - } - - @Test - public void testDeserializeDifferentCaseFormat() { - final String json = "{NameThatTestsCaseFormat:['bar'],AnotherField:'foo'}"; - ProtoWithDifferentCaseFormat proto = - upperCamelGson.fromJson(json, ProtoWithDifferentCaseFormat.class); - assertEquals("foo", proto.getAnotherField()); - assertEquals("bar", proto.getNameThatTestsCaseFormat(0)); - } -} diff --git a/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithPrimitiveTypesTest.java b/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithPrimitiveTypesTest.java deleted file mode 100644 index 0666a6dd00..0000000000 --- a/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithPrimitiveTypesTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2010 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.protobuf.functional; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.protobuf.ProtoTypeAdapter; -import com.google.gson.protobuf.ProtoTypeAdapter.EnumSerialization; -import com.google.gson.protobuf.generated.Bag.SimpleProto; -import com.google.protobuf.Descriptors.Descriptor; -import com.google.protobuf.GeneratedMessageV3; -import org.junit.Before; -import org.junit.Test; - -public class ProtosWithPrimitiveTypesTest { - private Gson gson; - - @Before - public void setUp() throws Exception { - gson = new GsonBuilder().registerTypeHierarchyAdapter( - GeneratedMessageV3.class, ProtoTypeAdapter.newBuilder() - .setEnumSerialization(EnumSerialization.NUMBER) - .build()) - .create(); - } - - @Test - public void testSerializeEmptyProto() { - SimpleProto proto = SimpleProto.newBuilder().build(); - String json = gson.toJson(proto); - assertEquals("{}", json); - } - - @Test - public void testDeserializeEmptyProto() { - SimpleProto proto = gson.fromJson("{}", SimpleProto.class); - assertFalse(proto.hasCount()); - assertFalse(proto.hasMsg()); - } - - @Test - public void testSerializeProto() { - Descriptor descriptor = SimpleProto.getDescriptor(); - SimpleProto proto = SimpleProto.newBuilder() - .setCount(3) - .setMsg("foo") - .build(); - String json = gson.toJson(proto); - assertTrue(json.contains("\"msg\":\"foo\"")); - assertTrue(json.contains("\"count\":3")); - } - - @Test - public void testDeserializeProto() { - SimpleProto proto = gson.fromJson("{msg:'foo',count:3}", SimpleProto.class); - assertEquals("foo", proto.getMsg()); - assertEquals(3, proto.getCount()); - } - - @Test - public void testDeserializeWithExplicitNullValue() { - SimpleProto proto = gson.fromJson("{msg:'foo',count:null}", SimpleProto.class); - assertEquals("foo", proto.getMsg()); - assertEquals(0, proto.getCount()); - } - -} diff --git a/proto/src/test/proto/annotations.proto b/proto/src/test/proto/annotations.proto deleted file mode 100644 index 53b727a666..0000000000 --- a/proto/src/test/proto/annotations.proto +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (C) 2010 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. -// - -syntax = "proto2"; - -package google.gson.protobuf.generated; -option java_package = "com.google.gson.protobuf.generated"; - -import "google/protobuf/descriptor.proto"; - -extend google.protobuf.FieldOptions { - // Indicates a field name that overrides the default for serialization - optional string serialized_name = 92066888; -} - -extend google.protobuf.EnumValueOptions { - // Indicates a field value that overrides the default for serialization - optional string serialized_value = 92066888; -} diff --git a/proto/src/test/proto/bag.proto b/proto/src/test/proto/bag.proto deleted file mode 100644 index 3e4769e2a8..0000000000 --- a/proto/src/test/proto/bag.proto +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright (C) 2010 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. -// - -syntax = "proto2"; - -package google.gson.protobuf.generated; -option java_package = "com.google.gson.protobuf.generated"; - -import "annotations.proto"; - -message SimpleProto { - optional string msg = 1; - optional int32 count = 2; -} - -message ProtoWithDifferentCaseFormat { - repeated string name_that_tests_case_format = 1; - optional string another_field = 2; -} - -message ProtoWithRepeatedFields { - repeated int64 numbers = 1; - repeated SimpleProto simples = 2; - optional string name = 3; -} - -// -- A more complex message with annotations and nested protos - -message OuterMessage { - optional int32 month = 1; - optional int32 year = 2; - optional int64 long_timestamp = 3 [(serialized_name) = "timeStamp"]; - optional string country_code_5f55 = 4; -} - -message ProtoWithAnnotations { - optional string id = 1; - optional OuterMessage outer_message = 2 [(serialized_name) = "expiration_date"]; - - message InnerMessage { - optional int32 n__id_ct = 1; - - enum Type { - UNKNOWN = 0; - TEXT = 1 [(serialized_value) = "text/plain"]; - IMAGE = 2 [(serialized_value) = "image/png"]; - } - optional Type content = 2; - - message Data { - optional string data = 1; - optional int32 width = 2; - optional int32 height = 3; - } - repeated Data data = 3 [(serialized_name) = "$binary_data$"]; - } - optional InnerMessage inner_message_1 = 3; - optional InnerMessage inner_message_2 = 4; -} \ No newline at end of file