Add jackson behavior for fields
Added classes to make gson treat fields as jackson does.
Added check for input OR output fields.
Corrected comment about annotation names.
Removed trailing spaces.
Extracted some string constants.
Fixed checkstyle issue.
Change-Id: I665b31774086d72a5a95bee6cf3486a019b72402
Issue-ID: POLICY-1428
Signed-off-by: Jim Hahn <jrh3@att.com>
diff --git a/gson/src/main/java/org/onap/policy/common/gson/JacksonFieldAdapterFactory.java b/gson/src/main/java/org/onap/policy/common/gson/JacksonFieldAdapterFactory.java
new file mode 100644
index 0000000..67c5144
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/JacksonFieldAdapterFactory.java
@@ -0,0 +1,181 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+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.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import org.onap.policy.common.gson.internal.ClassWalker;
+import org.onap.policy.common.gson.internal.Deserializer;
+import org.onap.policy.common.gson.internal.FieldDeserializer;
+import org.onap.policy.common.gson.internal.FieldSerializer;
+import org.onap.policy.common.gson.internal.Serializer;
+
+/**
+ * Factory that serializes/deserializes class fields following the normal behavior of
+ * jackson. Supports the following annotations:
+ * <ul>
+ * <li>GsonJsonIgnore</li>
+ * <li>GsonJsonProperty</li>
+ * </ul>
+ *
+ * <p>Note: {@link JacksonExclusionStrategy} must also be registered with the gson object.
+ */
+public class JacksonFieldAdapterFactory implements TypeAdapterFactory {
+
+ @Override
+ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+ Class<? super T> clazz = type.getRawType();
+
+ if (!JacksonExclusionStrategy.isManaged(clazz)) {
+ return null;
+ }
+
+ ClassWalker data = new ClassWalker();
+ data.walkClassHierarchy(clazz);
+
+ if (data.getInProps(Field.class).isEmpty() && data.getOutProps(Field.class).isEmpty()) {
+ // no fields to serialize
+ return null;
+ }
+
+ return new JacksonFieldAdapter<>(gson, data, gson.getDelegateAdapter(this, type));
+ }
+
+ /**
+ * Adapter for a single class.
+ *
+ * @param <T> type of class on which the adapter works
+ */
+ private static class JacksonFieldAdapter<T> extends TypeAdapter<T> {
+
+ /**
+ * Used to create an object of the given class.
+ */
+ private final TypeAdapter<T> delegate;
+
+ /**
+ * Used to serialize/deserialize a JsonElement.
+ */
+ private final TypeAdapter<JsonElement> elementAdapter;
+
+ /**
+ * Serializers for each item within the object.
+ */
+ private final Serializer[] serializers;
+
+ /**
+ * Deserializers for each item within the object.
+ */
+ private final Deserializer[] deserializers;
+
+ /**
+ * Constructs the object.
+ *
+ * @param gson the associated gson object
+ * @param data data used to configure the adapter
+ * @param delegate default constructor for the type
+ */
+ public JacksonFieldAdapter(Gson gson, ClassWalker data, TypeAdapter<T> delegate) {
+ this.delegate = delegate;
+
+ this.elementAdapter = gson.getAdapter(JsonElement.class);
+
+ // create serializers
+ this.serializers = makeSerializers(gson, data).toArray(new Serializer[0]);
+
+ // create deserializers
+ this.deserializers = makeDeserializers(gson, data).toArray(new Deserializer[0]);
+ }
+
+ /**
+ * Creates a complete list of serializers.
+ *
+ * @param gson the associated gson object
+ * @param data data used to configure the serializers
+ * @return a list of all serializers
+ */
+ private List<Serializer> makeSerializers(Gson gson, ClassWalker data) {
+ List<Serializer> ser = new ArrayList<Serializer>();
+
+ data.getOutProps(Field.class).forEach(field -> ser.add(new FieldSerializer(gson, field)));
+
+ return ser;
+ }
+
+ /**
+ * Creates a complete list of deserializers.
+ *
+ * @param gson the associated gson object
+ * @param data data used to configure the deserializers
+ * @return a list of all deserializers
+ */
+ private List<Deserializer> makeDeserializers(Gson gson, ClassWalker data) {
+ List<Deserializer> deser = new ArrayList<Deserializer>();
+
+ data.getInProps(Field.class).forEach(field -> deser.add(new FieldDeserializer(gson, field)));
+
+ return deser;
+ }
+
+ @Override
+ public void write(JsonWriter out, T value) throws IOException {
+ JsonElement tree = delegate.toJsonTree(value);
+
+ if (tree.isJsonObject()) {
+ JsonObject jsonObj = tree.getAsJsonObject();
+
+ // serialize each item from the value into the target tree
+ for (Serializer serializer : serializers) {
+ serializer.addToTree(value, jsonObj);
+ }
+ }
+
+ elementAdapter.write(out, tree);
+ }
+
+ @Override
+ public T read(JsonReader in) throws IOException {
+ JsonElement tree = elementAdapter.read(in);
+ T object = delegate.fromJsonTree(tree);
+
+ if (tree.isJsonObject()) {
+ JsonObject jsonObj = tree.getAsJsonObject();
+
+ // deserialize each item from the tree into the target object
+ for (Deserializer dser : deserializers) {
+ dser.getFromTree(jsonObj, object);
+ }
+ }
+
+ return object;
+ }
+ }
+}
diff --git a/gson/src/main/java/org/onap/policy/common/gson/internal/FieldDeserializer.java b/gson/src/main/java/org/onap/policy/common/gson/internal/FieldDeserializer.java
new file mode 100644
index 0000000..14a432d
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/FieldDeserializer.java
@@ -0,0 +1,72 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson.internal;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import java.lang.reflect.Field;
+
+/**
+ * De-serializer for fields that are exposed.
+ */
+public class FieldDeserializer extends Adapter implements Deserializer {
+
+ public static final String SET_ERR = "cannot set field: ";
+
+ /**
+ * Field within the object.
+ */
+ private final Field field;
+
+ /**
+ * Constructs the object.
+ *
+ * @param gson Gson object providing type adapters
+ * @param field field within the object
+ */
+ public FieldDeserializer(Gson gson, Field field) {
+ super(gson, field);
+
+ this.field = field;
+
+ field.setAccessible(true);
+ }
+
+ @Override
+ public void getFromTree(JsonObject source, Object target) {
+ JsonElement jsonEl = source.get(getPropName());
+ if (jsonEl == null || jsonEl.isJsonNull()) {
+ return;
+ }
+
+ Object value = fromJsonTree(jsonEl);
+
+ try {
+ field.set(target, value);
+
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new JsonParseException(makeError(SET_ERR), e);
+ }
+ }
+
+}
diff --git a/gson/src/main/java/org/onap/policy/common/gson/internal/FieldSerializer.java b/gson/src/main/java/org/onap/policy/common/gson/internal/FieldSerializer.java
new file mode 100644
index 0000000..1c9d8b3
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/FieldSerializer.java
@@ -0,0 +1,80 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson.internal;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import java.lang.reflect.Field;
+
+/**
+ * Serializer for fields that are exposed.
+ */
+public class FieldSerializer extends Adapter implements Serializer {
+
+ public static final String GET_ERR = "cannot get field: ";
+
+ /**
+ * Field within the object.
+ */
+ private final Field field;
+
+ /**
+ * Constructs the object.
+ *
+ * @param gson Gson object providing type adapters
+ * @param field field within the object
+ */
+ public FieldSerializer(Gson gson, Field field) {
+ super(gson, field);
+
+ this.field = field;
+
+ field.setAccessible(true);
+ }
+
+ @Override
+ public void addToTree(Object source, JsonObject target) {
+ Object value;
+ try {
+ value = getFromObject(source);
+
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new JsonParseException(makeError(GET_ERR), e);
+ }
+
+ JsonElement jsonEl = (value == null ? JsonNull.INSTANCE : toJsonTree(value));
+ target.add(getPropName(), jsonEl);
+ }
+
+ /**
+ * Gets the field from the source object. Overridden during junit testing.
+ *
+ * @param source object containing the field
+ * @return the field's value
+ * @throws IllegalAccessException if an error occurs
+ */
+ protected Object getFromObject(Object source) throws IllegalAccessException {
+ return field.get(source);
+ }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/JacksonFieldAdapterFactoryTest.java b/gson/src/test/java/org/onap/policy/common/gson/JacksonFieldAdapterFactoryTest.java
new file mode 100644
index 0000000..9d4b438
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/JacksonFieldAdapterFactoryTest.java
@@ -0,0 +1,214 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.onap.policy.common.gson.JacksonExclusionStrategy;
+import org.onap.policy.common.gson.JacksonFieldAdapterFactory;
+import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
+import org.onap.policy.common.gson.annotation.GsonJsonProperty;
+
+public class JacksonFieldAdapterFactoryTest {
+
+ private static JacksonFieldAdapterFactory factory = new JacksonFieldAdapterFactory();
+
+ private static Gson gson = new GsonBuilder().setExclusionStrategies(new JacksonExclusionStrategy())
+ .registerTypeAdapterFactory(factory).create();
+
+ @Test
+ public void testCreate() {
+ // unhandled types
+ assertNull(factory.create(gson, TypeToken.get(JsonElement.class)));
+ assertNull(factory.create(gson, TypeToken.get(NothingToSerialize.class)));
+
+ assertNotNull(factory.create(gson, TypeToken.get(Data.class)));
+ assertNotNull(factory.create(gson, TypeToken.get(Derived.class)));
+
+ Data data = new Data();
+
+ // deserialize using fields that aren't in the Data object
+ Data data2 = gson.fromJson("{\"abc\":100}", Data.class);
+ assertEquals(data.toString(), data2.toString());
+
+ // now work with valid fields
+ data.id = 10;
+ data.text = "hello";
+
+ String result = gson.toJson(data);
+ data2 = gson.fromJson(result, Data.class);
+ assertEquals(data.toString(), data2.toString());
+
+ // should also work with derived types
+ Derived der = new Derived();
+ der.setId(20);
+ der.text = "world";
+ der.unserialized = "abc";
+
+ result = gson.toJson(der);
+
+ // should not contain the unserialized field
+ assertFalse(result.contains("abc"));
+
+ Derived der2 = gson.fromJson(result, Derived.class);
+ der.unserialized = null;
+ assertEquals(der.toString(), der2.toString());
+ }
+
+ @Test
+ public void testCreate_Lists() {
+ DataList lst = new DataList();
+ lst.theList = new ArrayList<>();
+ lst.theList.add(new Data(200, "text 20"));
+ lst.theList.add(new Data(210, "text 21"));
+
+ String result = gson.toJson(lst);
+ assertEquals("{'theList':[{'my-id':200,'text':'text 20'},{'my-id':210,'text':'text 21'}]}".replace('\'', '"'),
+ result);
+
+ DataList lst2 = gson.fromJson(result, DataList.class);
+ assertEquals(stripIdent(lst.toString()), stripIdent(lst2.toString()));
+ assertEquals(Data.class, lst2.theList.get(0).getClass());
+ }
+
+ @Test
+ public void testCreate_OnlyOutProps() {
+ InFieldIgnored data = new InFieldIgnored();
+ data.value = "out only";
+
+ // field should be serialized
+ String result = gson.toJson(data);
+ assertEquals("{'value':'out only'}".replace('\'', '"'), result);
+
+ // field should NOT be deserialized
+ data = gson.fromJson(result, InFieldIgnored.class);
+ assertNull(data.value);
+ }
+
+ @Test
+ public void testCreate_OnlyInProps() {
+ OutFieldIgnored data = new OutFieldIgnored();
+ data.value = "in only";
+
+ // field should NOT be serialized
+ String result = gson.toJson(data);
+ assertEquals("{}", result);
+
+ // field should NOT be deserialized
+ data = gson.fromJson("{'value':'in only'}".replace('\'', '"'), OutFieldIgnored.class);
+ assertEquals("in only", data.value);
+ }
+
+ /**
+ * Object identifiers may change with each execution, so this method is used to strip
+ * the identifier from the text string so that the strings will still match across
+ * different runs.
+ *
+ * @param text text from which to strip the identifier
+ * @return the text, without the identifier
+ */
+ private String stripIdent(String text) {
+ return text.replaceFirst("@\\w+", "@");
+ }
+
+ private static class Data {
+ @GsonJsonProperty("my-id")
+ private int id;
+
+ public String text;
+
+ public Data() {
+ super();
+ }
+
+ public Data(int id, String text) {
+ this.id = id;
+ this.text = text;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public String toString() {
+ return "Data [id=" + id + ", text=" + text + "]";
+ }
+ }
+
+ private static class Derived extends Data {
+ // not serialized
+ private String unserialized;
+
+ @Override
+ public String toString() {
+ return "Derived [unserialized=" + unserialized + ", toString()=" + super.toString() + "]";
+ }
+ }
+
+ private static class DataList {
+ @GsonJsonProperty
+ private List<Data> theList;
+ }
+
+ protected static class NothingToSerialize {
+ // not serialized
+ protected String unserialized;
+ }
+
+ /**
+ * This has a field that should show up in the "output" list, but not in the "input"
+ * list, because the method will override it.
+ */
+ private static class InFieldIgnored {
+ @GsonJsonProperty("value")
+ private String value;
+
+ @GsonJsonIgnore
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+
+ /**
+ * This has a field that should show up in the "input" list, but not in the "output"
+ * list, because the method will override it.
+ */
+ private static class OutFieldIgnored {
+ @GsonJsonProperty("value")
+ private String value;
+
+ @GsonJsonIgnore
+ public String getValue() {
+ return value;
+ }
+ }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/FieldDeserializerTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/FieldDeserializerTest.java
new file mode 100644
index 0000000..62bab23
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/FieldDeserializerTest.java
@@ -0,0 +1,109 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson.internal;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import java.util.List;
+import org.junit.Test;
+import org.onap.policy.common.gson.JacksonExclusionStrategy;
+import org.onap.policy.common.gson.internal.DataAdapterFactory.Data;
+import org.onap.policy.common.gson.internal.FieldDeserializer;
+
+public class FieldDeserializerTest {
+ private static final String TEXT_FIELD_NAME = "text";
+ private static final String LIST_FIELD_NAME = "listField";
+ private static final String INITIAL_VALUE = "initial value";
+ private static final String NEW_VALUE = "new value";
+
+ private static DataAdapterFactory dataAdapter = new DataAdapterFactory();
+
+ private static Gson gson = new GsonBuilder().registerTypeAdapterFactory(dataAdapter)
+ .setExclusionStrategies(new JacksonExclusionStrategy()).create();
+
+ private FieldDeserializer deser;
+
+ private String text;
+
+ private List<Data> listField;
+
+ @Test
+ public void testGetFromTree() throws Exception {
+ deser = new FieldDeserializer(gson, FieldDeserializerTest.class.getDeclaredField(TEXT_FIELD_NAME));
+
+ JsonObject json = new JsonObject();
+
+ // no value in tree - text remains unchanged
+ text = INITIAL_VALUE;
+ deser.getFromTree(json, this);
+ assertEquals(text, INITIAL_VALUE);
+
+ // null value in tree - text remains unchanged
+ json.add(TEXT_FIELD_NAME, JsonNull.INSTANCE);
+ deser.getFromTree(json, this);
+ assertEquals(text, INITIAL_VALUE);
+
+ // now assign a value - text should be changed now
+ json.addProperty(TEXT_FIELD_NAME, NEW_VALUE);
+
+ deser.getFromTree(json, this);
+ assertEquals(text, NEW_VALUE);
+
+ /*
+ * check list field
+ */
+ deser = new FieldDeserializer(gson, FieldDeserializerTest.class.getDeclaredField(LIST_FIELD_NAME));
+
+ json.add(LIST_FIELD_NAME, DataAdapterFactory.makeArray());
+
+ dataAdapter.reset();
+ listField = null;
+ deser.getFromTree(json, this);
+
+ assertTrue(dataAdapter.isDataRead());
+ assertEquals(DataAdapterFactory.makeList().toString(), listField.toString());
+ }
+
+ @Test
+ public void testGetFromTree_SetEx() throws Exception {
+ deser = new FieldDeserializer(gson, FieldDeserializerTest.class.getDeclaredField(TEXT_FIELD_NAME)) {
+ @Override
+ public Object fromJsonTree(JsonElement tree) {
+ // return an int, which won't fit in a String - cause an exception
+ return 10;
+ }
+ };
+
+ JsonObject json = new JsonObject();
+ json.addProperty(TEXT_FIELD_NAME, NEW_VALUE);
+
+ assertThatThrownBy(() -> deser.getFromTree(json, this)).isInstanceOf(JsonParseException.class)
+ .hasMessage(FieldDeserializer.SET_ERR + FieldDeserializerTest.class.getName() + ".text");
+ }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/FieldSerializerTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/FieldSerializerTest.java
new file mode 100644
index 0000000..18167fc
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/FieldSerializerTest.java
@@ -0,0 +1,96 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson.internal;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import java.util.List;
+import org.junit.Test;
+import org.onap.policy.common.gson.JacksonExclusionStrategy;
+import org.onap.policy.common.gson.internal.DataAdapterFactory.Data;
+import org.onap.policy.common.gson.internal.FieldSerializer;
+
+public class FieldSerializerTest {
+ private static final String TEXT_FIELD_NAME = "text";
+ private static final String LIST_FIELD_NAME = "listField";
+
+ private static DataAdapterFactory dataAdapter = new DataAdapterFactory();
+
+ private static Gson gson = new GsonBuilder().registerTypeAdapterFactory(dataAdapter)
+ .setExclusionStrategies(new JacksonExclusionStrategy()).create();
+
+ private FieldSerializer ser;
+
+ protected String text;
+
+ private List<Data> listField;
+
+ @Test
+ public void testAddToTree() throws Exception {
+ ser = new FieldSerializer(gson, FieldSerializerTest.class.getDeclaredField(TEXT_FIELD_NAME));
+
+ // serialize null value first
+ text = null;
+
+ JsonObject json = new JsonObject();
+ ser.addToTree(this, json);
+ assertTrue(json.get(TEXT_FIELD_NAME).isJsonNull());
+
+ // serialize an actual value
+ text = "hello";
+ ser.addToTree(this, json);
+ assertEquals("hello", json.get(TEXT_FIELD_NAME).getAsString());
+
+ /*
+ * check list field
+ */
+ listField = DataAdapterFactory.makeList();
+
+ ser = new FieldSerializer(gson, FieldSerializerTest.class.getDeclaredField(LIST_FIELD_NAME));
+
+ dataAdapter.reset();
+ JsonElement tree = ser.toJsonTree(listField);
+ assertTrue(dataAdapter.isDataWritten());
+ assertEquals(DataAdapterFactory.ENCODED_LIST, tree.toString());
+ }
+
+ @Test
+ public void testAddToTree_GetEx() throws Exception {
+ ser = new FieldSerializer(gson, FieldSerializerTest.class.getDeclaredField(TEXT_FIELD_NAME)) {
+ @Override
+ protected Object getFromObject(Object source) throws IllegalAccessException {
+ throw new IllegalAccessException("expected exception");
+ }
+ };
+
+ text = "world";
+
+ assertThatThrownBy(() -> ser.addToTree(this, new JsonObject())).isInstanceOf(JsonParseException.class)
+ .hasMessage(FieldSerializer.GET_ERR + FieldSerializerTest.class.getName() + ".text");
+ }
+}