Skip to content

Commit

Permalink
gson polymorhic marshaling and field naming strategy support
Browse files Browse the repository at this point in the history
  • Loading branch information
elucash committed Feb 11, 2015
1 parent 07e2075 commit 119a431
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 30 deletions.
9 changes: 8 additions & 1 deletion gson/pom.xml
Expand Up @@ -7,7 +7,7 @@
<groupId>org.immutables</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<artifactId>gson</artifactId>
<artifactId>immutables-gson</artifactId>

<name>${project.groupId}.${project.artifactId}</name>
<description>Gson integration for Immutables. Also includes optional integration with Jackson to speed-up
Expand Down Expand Up @@ -45,6 +45,13 @@
<version>2.5.0</version>
<optional>true</optional>
</dependency>
<dependency>
<!-- Jackson binding have utility used when using polymorhic marshaling with jackson-core implementation -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.0</version>
<optional>true</optional>
</dependency>
<dependency>
<!-- Message body reader and writer provider implementation, optional dependency on JAX-RS -->
<groupId>javax.ws.rs</groupId>
Expand Down
83 changes: 74 additions & 9 deletions gson/src/org/immutables/gson/Gson.java
@@ -1,42 +1,107 @@
/*
Copyright 2015 Immutables Authors and Contributors
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 org.immutables.gson;

import java.util.Map;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.FieldNamingStrategy;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.immutables.gson.adapter.ExpectedSubtypesAdapter;
import org.immutables.gson.adapter.FieldNamingTranslator;

/**
* The Interface Gson.
*/
@Retention(RetentionPolicy.SOURCE)
public @interface Gson {
/**
* Use on a top level class to generate type adapted factory.
* Use on a top level class to generate type adapted factory supporting directly annotated and all
* nested immutable types.
* <p>
* Type adapter factories are also registered statically as services
* {@code META-INF/services/com.google.gson.TypeAdapterFactory}. Easy way to configure
* {@link com.google.gson.Gson}.
* Type adapter factories are registered statically as services
* {@code META-INF/services/com.google.gson.TypeAdapterFactory}. The most easy way to register all
* such factories {@link com.google.gson.Gson}.
* <p>
* Certain are gson options are supported for immutable objects in deliberate fashion:
* Certain Gson options are supported for immutable objects in deliberate fashion:
* <ul>
* <li>{@link GsonBuilder#serializeNulls()} - When enabled, {@code null} fields and empty array
* fields will be included, otherwise omited</li>
* <li>{@link GsonBuilder#setFieldNamingStrategy(FieldNamingStrategy)} - Naming strategy could be
* used if {@code @TypeAdapters(fieldNamingStrategy=true)}. See {@link #fieldNamingStrategy()} for
* more information.</li>
* </ul>
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface TypeAdapted {}
public @interface TypeAdapters {
/**
* When {@code namingStrategy=true}, somewhat more involved code is generated to apply naming
* strategies extracted from configured {@link com.google.gson.Gson} instance.
* <p>
* <em>This functionality uses runtime support class and requires that this Gson integration module
* jar will be available at runtime. Uses some Gson internals which could
* potentially break in later versions of Gson library. Uses oracle JVM internals.
* If you really want to discuss this functionality to be more portable,
* file a request at {@linkplain "https://github.com/immutables/immutables/issues"}.
* </em>
* @see FieldNamingStrategy
* @see FieldNamingPolicy
* @see GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)
* @see FieldNamingTranslator
* @return {@code true} if enabled, by default is {@code false}
*/
boolean fieldNamingStrategy() default false;

/**
* When {@code emptyAsNulls=true}, empty arrays and objects will be omitted from output as
* if they where {@code null}, both field name and value will be omited.
* <p>
* Note that {@code null} skipping behaviour is controlled by
* {@link GsonBuilder#serializeNulls()}, which forces all nulls and empty arrays/objects to be
* serialized.
* @return {@code true} if enabled, by default is {@code false}
*/
boolean emptyAsNulls() default false;
}

/**
* Expected subclasses for marshaling could be specified on attribute level or an abstract
* supertype directly, however the former declaration site has precedence.
* Expected subtypes for serialization could be specified on attribute level or an abstract
* supertype directly, however the former declaration site has precedence. It enables polymorphic
* marshaling by structure. Subtype that matches JSON value will be returned, for the details
* please see {@link ExpectedSubtypesAdapter}.
* <p>
* Note: when this annotation is used with {@link Map} attribute, it refers to types of values,
* not keys.
* <p>
* <em>This functionality uses runtime support class and requires that this Gson integration module
* jar will be available at runtime.</em>
* @see ExpectedSubtypesAdapter
* @see #value()
* @see Named
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Subtypes {
public @interface ExpectedSubtypes {

/**
* Specifies expected subclasses of an abstract type that is matched during parsing by
Expand Down
16 changes: 7 additions & 9 deletions gson/src/org/immutables/gson/stream/GsonMessageBodyProvider.java
Expand Up @@ -15,14 +15,6 @@
*/
package org.immutables.gson.stream;

import org.immutables.value.Value.Style.ImplementationVisibility;
import java.lang.reflect.Field;
import org.immutables.value.Value;
import java.util.List;
import javax.ws.rs.core.Response;
import com.google.common.base.Throwables;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
Expand All @@ -39,20 +31,26 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.annotation.Nullable;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import org.immutables.metainf.Metainf;
import org.immutables.value.Value;
import org.immutables.value.Value.Style.ImplementationVisibility;

/**
* Gson serialization provider for JAX-RS 1.0 and JAX-RS 2.0.
Expand Down
20 changes: 17 additions & 3 deletions gson/src/org/immutables/gson/stream/JsonGeneratorWriter.java
Expand Up @@ -15,20 +15,20 @@
*/
package org.immutables.gson.stream;

import javax.annotation.concurrent.NotThreadSafe;
import com.fasterxml.jackson.core.JsonGenerator;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.Callable;
import javax.annotation.concurrent.NotThreadSafe;

/**
* {@link JsonWriter} impementation backed by Jackson's {@link JsonGenerator}.
* Provides measurable JSON writing improvements over Gson's native implementation.
* Error reporting is might differ, however.
*/
@NotThreadSafe
public class JsonGeneratorWriter extends JsonWriter {

public class JsonGeneratorWriter extends JsonWriter implements Callable<JsonGenerator> {
private static final Writer UNSUPPORTED_WRITER = new Writer() {
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
Expand All @@ -53,6 +53,10 @@ public JsonGeneratorWriter(JsonGenerator generator) {
this.generator = generator;
}

public JsonGenerator getGenerator() {
return generator;
}

@Override
public JsonWriter beginArray() throws IOException {
generator.writeStartArray();
Expand Down Expand Up @@ -145,4 +149,14 @@ public void close() throws IOException {
public String toString() {
return getClass().getSimpleName();
}

/**
* Implements {@link Callable} mostly as a marker interface.
* Better use {@link #getGenerator()} to get generator.
* @return unwrapped {@link JsonGenerator}
*/
@Override
public JsonGenerator call() throws Exception {
return generator;
}
}
22 changes: 19 additions & 3 deletions gson/src/org/immutables/gson/stream/JsonParserReader.java
Expand Up @@ -15,6 +15,7 @@
*/
package org.immutables.gson.stream;

import java.util.concurrent.Callable;
import com.fasterxml.jackson.core.JsonParser;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
Expand All @@ -27,10 +28,10 @@
/**
* {@link JsonReader} impementation backed by Jackson's {@link JsonParser}.
* Provides measurable JSON parsing improvements over Gson's native implementation.
* Error reporting is might differ, however.
* Error reporting might differ, however.
*/
@NotThreadSafe
public class JsonParserReader extends JsonReader {
public class JsonParserReader extends JsonReader implements Callable<JsonParser> {

private static final Reader UNSUPPORTED_READER = new Reader() {
@Override
Expand All @@ -51,6 +52,10 @@ public JsonParserReader(JsonParser parser) {
this.parser = parser;
}

public JsonParser getParser() {
return parser;
}

@Nullable
private com.fasterxml.jackson.core.JsonToken peek;

Expand Down Expand Up @@ -130,6 +135,7 @@ public String nextString() throws IOException {
clearPeek();
return value;
}

@Override
public boolean nextBoolean() throws IOException {
requirePeek();
Expand Down Expand Up @@ -216,7 +222,17 @@ private static JsonToken toGsonToken(com.fasterxml.jackson.core.JsonToken token)
case VALUE_STRING:
return JsonToken.STRING;
default: // Not semantically equivalent
return JsonToken.END_DOCUMENT;
return JsonToken.NULL;
}
}

/**
* Implements {@link Callable} mostly as a marker interface.
* Better use {@link #getParser()} to get parser.
* @return unwrapped {@link JsonParser}
*/
@Override
public JsonParser call() throws Exception {
return parser;
}
}
@@ -1,4 +1,4 @@
package org.immutables.gson.adapters;
package org.immutables.gson.adapter;

import com.google.common.collect.Multiset;
import com.google.common.collect.SetMultimap;
Expand All @@ -11,7 +11,7 @@

@Value.Immutable(builder = false)
@Value.Nested
@Gson.TypeAdapted
@Gson.TypeAdapters
public interface Adapt {

@Value.Parameter
Expand Down
@@ -1,4 +1,4 @@
package org.immutables.gson.adapters;
package org.immutables.gson.adapter;

import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
Expand Down
@@ -1,4 +1,4 @@
package org.immutables.gson.adapters;
package org.immutables.gson.adapter;

import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multiset;
Expand All @@ -11,7 +11,7 @@

@Value.Immutable(builder = false)
@Value.Nested
@Gson.TypeAdapted
@Gson.TypeAdapters
public interface OtherType {

@Value.Parameter
Expand Down

0 comments on commit 119a431

Please sign in to comment.