Merge "Add jackson behavior for fields"
diff --git a/gson/src/main/java/org/onap/policy/common/gson/JacksonMethodAdapterFactory.java b/gson/src/main/java/org/onap/policy/common/gson/JacksonMethodAdapterFactory.java
new file mode 100644
index 0000000..1c3039f
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/JacksonMethodAdapterFactory.java
@@ -0,0 +1,202 @@
+/*
+ * ============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.Method;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.onap.policy.common.gson.internal.AnyGetterSerializer;
+import org.onap.policy.common.gson.internal.AnySetterDeserializer;
+import org.onap.policy.common.gson.internal.ClassWalker;
+import org.onap.policy.common.gson.internal.Deserializer;
+import org.onap.policy.common.gson.internal.MethodDeserializer;
+import org.onap.policy.common.gson.internal.MethodSerializer;
+import org.onap.policy.common.gson.internal.Serializer;
+
+/**
+ * Factory that serializes/deserializes class methods following the normal behavior of
+ * jackson. Supports the following annotations:
+ * <ul>
+ * <li>GsonJsonIgnore</li>
+ * <li>GsonJsonProperty</li>
+ * <li>GsonJsonAnyGetter</li>
+ * <li>GsonJsonAnySetter</li>
+ * </ul>
+ */
+public class JacksonMethodAdapterFactory 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(Method.class).isEmpty() && data.getOutProps(Method.class).isEmpty()) {
+            if (data.getAnyGetter() == null && data.getAnySetter() == null) {
+                // no methods to serialize
+                return null;
+            }
+        }
+
+        return new JacksonMethodAdapter<>(gson, data, gson.getDelegateAdapter(this, type));
+    }
+
+    /**
+     * Adapter for a single class.
+     *
+     * @param <T> type of class on which the adapter works
+     */
+    private static class JacksonMethodAdapter<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 JacksonMethodAdapter(Gson gson, ClassWalker data, TypeAdapter<T> delegate) {
+            this.delegate = delegate;
+
+            this.elementAdapter = gson.getAdapter(JsonElement.class);
+
+            Set<String> unliftedProps = new HashSet<>();
+            unliftedProps.addAll(data.getInNotIgnored());
+            unliftedProps.addAll(data.getOutNotIgnored());
+
+            // create serializers
+            this.serializers = makeSerializers(gson, data, unliftedProps).toArray(new Serializer[0]);
+
+            // create deserializers
+            this.deserializers = makeDeserializers(gson, data, unliftedProps).toArray(new Deserializer[0]);
+        }
+
+        /**
+         * Creates a complete list of serializers.
+         *
+         * @param gson the associated gson object
+         * @param data data used to configure the serializers
+         * @param unliftedProps properties that should not be lowered by "any-getters"
+         * @return a list of all serializers
+         */
+        private List<Serializer> makeSerializers(Gson gson, ClassWalker data, Set<String> unliftedProps) {
+            List<Serializer> ser = new ArrayList<Serializer>();
+
+            if (data.getAnyGetter() != null) {
+                ser.add(new AnyGetterSerializer(gson, unliftedProps, data.getAnyGetter()));
+            }
+
+            data.getOutProps(Method.class).forEach(method -> ser.add(new MethodSerializer(gson, method)));
+
+            return ser;
+        }
+
+        /**
+         * Creates a complete list of deserializers.
+         *
+         * @param gson the associated gson object
+         * @param data data used to configure the deserializers
+         * @param unliftedProps properties that should not be lifted by "any-setters"
+         * @return a list of all deserializers
+         */
+        private List<Deserializer> makeDeserializers(Gson gson, ClassWalker data, Set<String> unliftedProps) {
+            List<Deserializer> deser = new ArrayList<Deserializer>();
+
+            if (data.getAnySetter() != null) {
+                deser.add(new AnySetterDeserializer(gson, unliftedProps, data.getAnySetter()));
+            }
+
+            data.getInProps(Method.class).forEach(method -> deser.add(new MethodDeserializer(gson, method)));
+
+            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/Adapter.java b/gson/src/main/java/org/onap/policy/common/gson/internal/Adapter.java
index b4ef53f..bb8672b 100644
--- a/gson/src/main/java/org/onap/policy/common/gson/internal/Adapter.java
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/Adapter.java
@@ -86,12 +86,11 @@
      *
      * @param gson Gson object providing type adapters
      * @param accessor method used to access the item from within an object
-     * @param forGetter {@code true} if the name is for a "getter" method, {@code false}
-     *        if for a "setter"
      * @param valueType the class of value on which this operates
      */
-    public Adapter(Gson gson, Method accessor, boolean forGetter, Type valueType) {
-        this.propName = (forGetter ? detmGetterPropName(accessor) : detmSetterPropName(accessor));
+    public Adapter(Gson gson, Method accessor, Type valueType) {
+        boolean forSetter = (accessor.getReturnType() == void.class);
+        this.propName = (forSetter ? detmSetterPropName(accessor) : detmGetterPropName(accessor));
         this.reader = new ConvInfo(TypeToken.get(valueType));
         this.gson = gson;
         this.fullName = getQualifiedName(accessor);
diff --git a/gson/src/main/java/org/onap/policy/common/gson/internal/AnyGetterSerializer.java b/gson/src/main/java/org/onap/policy/common/gson/internal/AnyGetterSerializer.java
new file mode 100644
index 0000000..da9ad17
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/AnyGetterSerializer.java
@@ -0,0 +1,83 @@
+/*
+ * ============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.Method;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Serializer for methods having a JsonAnyGetter annotation.
+ */
+public class AnyGetterSerializer extends Lifter implements Serializer {
+
+    public static final String NOT_AN_OBJECT_ERR = "expecting a JsonObject for ";
+
+    /**
+     * Constructs the object.
+     *
+     * @param gson Gson object providing type adapters
+     * @param propName property name associated with the lifted field
+     * @param unliftedProps property names that should not be lifted
+     * @param getter method used to get the item from within an object
+     */
+    public AnyGetterSerializer(Gson gson, Set<String> unliftedProps, Method getter) {
+        super(gson, unliftedProps, getter, getter.getGenericReturnType());
+    }
+
+    @Override
+    public void addToTree(Object source, JsonObject target) {
+        // get the value from the object
+        Object value = invoke(source);
+        if (value == null) {
+            // nothing to lift
+            return;
+        }
+
+        JsonElement inner = toJsonTree(value);
+        if (!inner.isJsonObject()) {
+            throw new JsonParseException(makeError(NOT_AN_OBJECT_ERR));
+        }
+
+        // lift items from inner into the target
+        copyLiftedItems(inner.getAsJsonObject(), target);
+    }
+
+    /**
+     * Copies lifted items from one tree into another, without removing them from the
+     * source tree.
+     *
+     * @param source tree from which items are to be copied
+     * @param target tree into which items are to be copied
+     */
+    private void copyLiftedItems(JsonObject source, JsonObject target) {
+        for (Entry<String, JsonElement> ent : source.entrySet()) {
+            String name = ent.getKey();
+            if (shouldLift(name)) {
+                target.add(name, ent.getValue());
+            }
+        }
+    }
+}
diff --git a/gson/src/main/java/org/onap/policy/common/gson/internal/AnySetterDeserializer.java b/gson/src/main/java/org/onap/policy/common/gson/internal/AnySetterDeserializer.java
new file mode 100644
index 0000000..85d42df
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/AnySetterDeserializer.java
@@ -0,0 +1,58 @@
+/*
+ * ============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 java.lang.reflect.Method;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * De-serializer for methods having a JsonAnySetter annotation.
+ */
+public class AnySetterDeserializer extends Lifter implements Deserializer {
+
+    /**
+     * Constructs the object.
+     *
+     * @param gson Gson object providing type adapters
+     * @param unliftedProps property names that should not be lifted
+     * @param getter method used to get the item from within an object
+     */
+    public AnySetterDeserializer(Gson gson, Set<String> unliftedProps, Method setter) {
+        super(gson, unliftedProps, setter, setter.getGenericParameterTypes()[1]);
+    }
+
+    @Override
+    public void getFromTree(JsonObject source, Object target) {
+        for (Entry<String, JsonElement> ent : source.entrySet()) {
+            String name = ent.getKey();
+
+            if (shouldLift(name)) {
+                Object value = fromJsonTree(ent.getValue());
+                invoke(target, name, value);
+            }
+        }
+    }
+
+}
diff --git a/gson/src/main/java/org/onap/policy/common/gson/internal/Lifter.java b/gson/src/main/java/org/onap/policy/common/gson/internal/Lifter.java
new file mode 100644
index 0000000..bb8ec32
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/Lifter.java
@@ -0,0 +1,62 @@
+/*
+ * ============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 java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+/**
+ * Super class of serializers and de-serializers that deal with "lifted" data, that is,
+ * data that is lifted from a nested json object into the containing object.
+ */
+public class Lifter extends MethodAdapter {
+
+    /**
+     * Names of the properties that are <i>not</i> to be lifted.
+     */
+    private final Set<String> unliftedProps;
+
+    /**
+     * Constructs the object.
+     *
+     * @param gson Gson object providing type adapters
+     * @param unliftedProps property names that should not be lifted
+     * @param accessor method used to access the item from within an object
+     * @param type the class of value on which this operates
+     */
+    public Lifter(Gson gson, Set<String> unliftedProps, Method accessor, Type type) {
+        super(gson, accessor, type);
+
+        this.unliftedProps = unliftedProps;
+    }
+
+    /**
+     * Determines if a property should be lifted.
+     *
+     * @param propName the name of the property
+     * @return {@code true} if the property should be lifted, {@code false} otherwise
+     */
+    public boolean shouldLift(String propName) {
+        return !unliftedProps.contains(propName);
+    }
+}
diff --git a/gson/src/main/java/org/onap/policy/common/gson/internal/MethodAdapter.java b/gson/src/main/java/org/onap/policy/common/gson/internal/MethodAdapter.java
new file mode 100644
index 0000000..579864d
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/MethodAdapter.java
@@ -0,0 +1,69 @@
+/*
+ * ============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.JsonParseException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+/**
+ * Super class of adapters used to serialize and de-serialize a method.
+ */
+public class MethodAdapter extends Adapter {
+
+    public static final String INVOKE_ERR = "cannot invoke method to serialize/deserialize: ";
+
+    /**
+     * Method used to access the item within an object.
+     */
+    private final Method accessor;
+
+    /**
+     * Constructs the object.
+     *
+     * @param gson Gson object providing type adapters
+     * @param accessor method used to access the item from within an object
+     * @param type the class of value on which this operates
+     */
+    public MethodAdapter(Gson gson, Method accessor, Type type) {
+        super(gson, accessor, type);
+
+        this.accessor = accessor;
+    }
+
+    /**
+     * Invokes the accessor method.
+     *
+     * @param self object on which to invoke the method
+     * @param args arguments to be passed to the method
+     * @return the method's result
+     */
+    public Object invoke(Object self, Object... args) {
+        try {
+            return accessor.invoke(self, args);
+
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new JsonParseException(makeError(INVOKE_ERR), e);
+        }
+    }
+}
diff --git a/gson/src/main/java/org/onap/policy/common/gson/internal/MethodDeserializer.java b/gson/src/main/java/org/onap/policy/common/gson/internal/MethodDeserializer.java
new file mode 100644
index 0000000..bb0724e
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/MethodDeserializer.java
@@ -0,0 +1,53 @@
+/*
+ * ============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 java.lang.reflect.Method;
+
+/**
+ * De-serializer for methods that are exposed.
+ */
+public class MethodDeserializer extends MethodAdapter implements Deserializer {
+
+    /**
+     * Constructs the object.
+     *
+     * @param gson Gson object providing type adapters
+     * @param setter method used to set the item within an object
+     */
+    public MethodDeserializer(Gson gson, Method setter) {
+        super(gson, setter, setter.getGenericParameterTypes()[0]);
+    }
+
+    @Override
+    public void getFromTree(JsonObject source, Object target) {
+        JsonElement jsonEl = source.get(getPropName());
+        if (jsonEl == null || jsonEl.isJsonNull()) {
+            return;
+        }
+
+        invoke(target, fromJsonTree(jsonEl));
+    }
+
+}
diff --git a/gson/src/main/java/org/onap/policy/common/gson/internal/MethodSerializer.java b/gson/src/main/java/org/onap/policy/common/gson/internal/MethodSerializer.java
new file mode 100644
index 0000000..ab54461
--- /dev/null
+++ b/gson/src/main/java/org/onap/policy/common/gson/internal/MethodSerializer.java
@@ -0,0 +1,47 @@
+/*
+ * ============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.JsonObject;
+import java.lang.reflect.Method;
+
+/**
+ * Serializer for methods that are exposed.
+ */
+public class MethodSerializer extends MethodAdapter implements Serializer {
+
+    /**
+     * Constructs the object.
+     *
+     * @param gson Gson object providing type adapters
+     * @param getter method used to get the item from within an object
+     */
+    public MethodSerializer(Gson gson, Method getter) {
+        super(gson, getter, getter.getGenericReturnType());
+    }
+
+    @Override
+    public void addToTree(Object source, JsonObject target) {
+        Object value = invoke(source);
+        target.add(getPropName(), (value == null ? null : toJsonTree(value)));
+    }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/JacksonMethodAdapterFactoryTest.java b/gson/src/test/java/org/onap/policy/common/gson/JacksonMethodAdapterFactoryTest.java
new file mode 100644
index 0000000..47cf4b3
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/JacksonMethodAdapterFactoryTest.java
@@ -0,0 +1,311 @@
+/*
+ * ============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 static org.junit.Assert.assertTrue;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+import java.util.Map;
+import java.util.TreeMap;
+import org.junit.Test;
+import org.onap.policy.common.gson.JacksonExclusionStrategy;
+import org.onap.policy.common.gson.JacksonMethodAdapterFactory;
+import org.onap.policy.common.gson.annotation.GsonJsonAnyGetter;
+import org.onap.policy.common.gson.annotation.GsonJsonAnySetter;
+import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
+import org.onap.policy.common.gson.annotation.GsonJsonProperty;
+
+public class JacksonMethodAdapterFactoryTest {
+
+    private static JacksonMethodAdapterFactory factory = new JacksonMethodAdapterFactory();
+
+    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)));
+        assertNotNull(factory.create(gson, TypeToken.get(OnlyGetters.class)));
+        assertNotNull(factory.create(gson, TypeToken.get(OnlySetters.class)));
+        assertNotNull(factory.create(gson, TypeToken.get(OnlyAnyGetter.class)));
+        assertNotNull(factory.create(gson, TypeToken.get(OnlyAnySetter.class)));
+
+        // unhandled type
+
+        Data data = new Data();
+        data.id = 10;
+        data.text = "some text";
+
+        String result = gson.toJson(data);
+        Data data2 = gson.fromJson(result, Data.class);
+        assertEquals(data.toString(), data2.toString());
+
+        Derived der = new Derived();
+        der.setId(20);
+        der.setText("hello");
+        der.text = "world";
+        der.map = new TreeMap<>();
+        der.map.put("mapA", "valA");
+        der.map.put("mapB", "valB");
+
+        result = gson.toJson(der);
+
+        // should not contain the unserialized fields
+        assertFalse(result.contains("hello"));
+        assertFalse(result.contains("world"));
+
+        // null out unserialized fields
+        der.text = null;
+
+        // null out overridden field
+        der.setText(null);
+
+        Derived der2 = gson.fromJson(result, Derived.class);
+
+        assertEquals(der.toString(), der2.toString());
+
+        // override of AnyGetter
+        AnyGetterOverride dblget = new AnyGetterOverride();
+        dblget.setMap(der.map);
+        dblget.overMap = new TreeMap<>();
+        dblget.overMap.put("getA", 100);
+        dblget.overMap.put("getB", 110);
+
+        String result2 = gson.toJson(dblget);
+        dblget.overMap.keySet().forEach(key -> assertTrue("over contains " + key, result2.contains(key)));
+        der.map.keySet().forEach(key -> assertFalse("sub contains " + key, result2.contains(key)));
+
+        // override of AnySetter
+        Map<String, Integer> map = new TreeMap<>();
+        map.put("setA", 200);
+        map.put("setB", 210);
+        AnySetterOverride dblset = gson.fromJson(gson.toJson(map), AnySetterOverride.class);
+        assertEquals(map.toString(), dblset.overMap.toString());
+        assertNull(dblset.getTheMap());
+
+        // non-static nested class - can serialize, but not de-serialize
+        Container cont = new Container(500, "bye bye");
+        result = gson.toJson(cont);
+        assertEquals("{'id':500,'nested':{'value':'bye bye'}}".replace('\'', '"'), result);
+    }
+
+    protected static class Data {
+        private int id;
+        private String text;
+
+        public int getId() {
+            return id;
+        }
+
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        // not public, but property provided
+        @GsonJsonProperty("text")
+        protected String getText() {
+            return text;
+        }
+
+        public void setText(String text) {
+            this.text = text;
+        }
+
+        public void unused(String text) {
+            // do nothing
+        }
+
+        @Override
+        public String toString() {
+            return "Data [id=" + id + ", text=" + text + "]";
+        }
+    }
+
+    protected static class Derived extends Data {
+
+        // overrides private field from Data
+        public String text;
+
+        private Map<String, String> map;
+
+        @GsonJsonAnyGetter
+        public Map<String, String> getTheMap() {
+            return map;
+        }
+
+        @GsonJsonIgnore
+        public void setMap(Map<String, String> map) {
+            this.map = map;
+        }
+
+        @GsonJsonAnySetter
+        public void setMapValue(String key, String value) {
+            if (map == null) {
+                map = new TreeMap<>();
+            }
+
+            map.put(key, value);
+        }
+
+        @Override
+        public String toString() {
+            return "Derived [text=" + text + ", map=" + map + ", toString()=" + super.toString() + "]";
+        }
+    }
+
+    /**
+     * Has {@link GsonJsonAnyGetter} method that overrides the super class' method.
+     */
+    private static class AnyGetterOverride extends Derived {
+        private Map<String, Integer> overMap;
+
+        @GsonJsonAnyGetter
+        private Map<String, Integer> getOverride() {
+            return overMap;
+        }
+    }
+
+    /**
+     * Has {@link GsonJsonAnySetter} method that overrides the super class' method.
+     */
+    private static class AnySetterOverride extends Derived {
+        private Map<String, Integer> overMap;
+
+        @GsonJsonAnySetter
+        private void setOverride(String key, int value) {
+            if (overMap == null) {
+                overMap = new TreeMap<>();
+            }
+
+            overMap.put(key, value);
+        }
+    }
+
+    /**
+     * Has nothing to serialize.
+     */
+    protected static class NothingToSerialize {
+        // not serialized
+        protected String unserialized;
+    }
+
+    /**
+     * Only has getters.
+     */
+    protected static class OnlyGetters {
+        public int getId() {
+            return 1010;
+        }
+    }
+
+    /**
+     * Only has setters.
+     */
+    protected static class OnlySetters {
+        public void setId(int id) {
+            // do nothing
+        }
+    }
+
+    /**
+     * Only has {@link GsonJsonAnyGetter}.
+     */
+    private static class OnlyAnyGetter {
+        @GsonJsonAnyGetter
+        public Map<String, Integer> getOverride() {
+            return null;
+        }
+    }
+
+    /**
+     * Only has {@link GsonJsonAnySetter}.
+     */
+    private static class OnlyAnySetter {
+        @GsonJsonAnySetter
+        public void setOverride(String key, int value) {
+            // do nothing
+        }
+    }
+
+    /**
+     * Used to test serialization of non-static nested classes.
+     */
+    protected static class Container {
+        private int id;
+        private Nested nested;
+
+        public Container() {
+            super();
+        }
+
+        public Container(int id, String value) {
+            this.id = id;
+            this.nested = new Nested(value);
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        public Nested getNested() {
+            return nested;
+        }
+
+        @Override
+        public String toString() {
+            return "Container [id=" + id + ", nested=" + nested + "]";
+        }
+
+
+        protected class Nested {
+            private String value;
+
+            public Nested(String val) {
+                value = val;
+            }
+
+            public String getValue() {
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                return "Nested [value=" + value + "]";
+            }
+        }
+    }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/AdapterTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/AdapterTest.java
index fcb0d9a..04f3716 100644
--- a/gson/src/test/java/org/onap/policy/common/gson/internal/AdapterTest.java
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/AdapterTest.java
@@ -140,7 +140,7 @@
 
         Method getter = mget("getMyList");
 
-        Adapter aget = new Adapter(gson, getter, true, getter.getReturnType());
+        Adapter aget = new Adapter(gson, getter, getter.getReturnType());
 
         dataAdapter.reset();
         JsonElement tree = aget.toJsonTree(listField);
@@ -148,7 +148,7 @@
         assertEquals(DataAdapterFactory.ENCODED_LIST, tree.toString());
 
         Method setter = AdapterTest.class.getDeclaredMethod("setMyList", List.class);
-        Adapter aset = new Adapter(gson, setter, true, setter.getGenericParameterTypes()[0]);
+        Adapter aset = new Adapter(gson, setter, setter.getGenericParameterTypes()[0]);
 
         dataAdapter.reset();
         @SuppressWarnings("unchecked")
@@ -168,7 +168,7 @@
 
 
         // test getter
-        adapter = new Adapter(gson, mget(GET_VALUE_NAME), true, String.class);
+        adapter = new Adapter(gson, mget(GET_VALUE_NAME), String.class);
 
         assertEquals(VALUE_NAME, adapter.getPropName());
         assertEquals(MY_NAME + ".getValue", adapter.getFullName());
@@ -177,7 +177,7 @@
 
 
         // test setter
-        adapter = new Adapter(gson, mset("setValue"), false, String.class);
+        adapter = new Adapter(gson, mset("setValue"), String.class);
 
         assertEquals(VALUE_NAME, adapter.getPropName());
         assertEquals(MY_NAME + ".setValue", adapter.getFullName());
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/AnyGetterSerializerTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/AnyGetterSerializerTest.java
new file mode 100644
index 0000000..7512842
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/AnyGetterSerializerTest.java
@@ -0,0 +1,134 @@
+/*
+ * ============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.JsonObject;
+import com.google.gson.JsonParseException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.gson.JacksonExclusionStrategy;
+import org.onap.policy.common.gson.internal.AnyGetterSerializer;
+import org.onap.policy.common.gson.internal.DataAdapterFactory.Data;
+
+public class AnyGetterSerializerTest {
+
+    private static DataAdapterFactory dataAdapter = new DataAdapterFactory();
+
+    private static Gson gson = new GsonBuilder().registerTypeAdapterFactory(dataAdapter)
+                    .setExclusionStrategies(new JacksonExclusionStrategy()).create();
+
+    private Set<String> set;
+    private AnyGetterSerializer ser;
+
+    /**
+     * Set up.
+     *
+     * @throws Exception if an error occurs
+     */
+    @Before
+    public void setUp() throws Exception {
+        set = new HashSet<>(Arrays.asList("id", "value"));
+        ser = new AnyGetterSerializer(gson, set, MapData.class.getDeclaredMethod("getTheMap"));
+    }
+
+    @Test
+    public void testAddToTree_testCopyLiftedItems() throws Exception {
+        JsonObject tree = new JsonObject();
+        tree.addProperty("hello", "world");
+
+        MapData data = new MapData();
+
+        data.map = DataAdapterFactory.makeMap();
+
+        // this should not be copied because it is in the "set"
+        data.map.put("value", Arrays.asList(new Data(1000)));
+
+        dataAdapter.reset();
+        JsonObject tree2 = tree.deepCopy();
+        ser.addToTree(data, tree2);
+
+        assertTrue(dataAdapter.isDataWritten());
+
+        DataAdapterFactory.addToObject(tree);
+
+        assertEquals(tree.toString(), tree2.toString());
+    }
+
+    @Test
+    public void testAddToTree_NullMap() throws Exception {
+        JsonObject tree = new JsonObject();
+        tree.addProperty("hello", "world");
+
+        MapData data = new MapData();
+
+        // leave "map" unset
+
+        JsonObject tree2 = tree.deepCopy();
+        ser.addToTree(data, tree2);
+
+        assertEquals(tree.toString(), tree2.toString());
+    }
+
+    @Test
+    public void testAddToTree_NotAnObject() throws Exception {
+        ser = new AnyGetterSerializer(gson, set, NotAnObject.class.getDeclaredMethod("getNonMap"));
+
+        JsonObject tree = new JsonObject();
+
+        NotAnObject data = new NotAnObject();
+        data.text = "bye bye";
+
+        assertThatThrownBy(() -> ser.addToTree(data, tree)).isInstanceOf(JsonParseException.class)
+                        .hasMessage(AnyGetterSerializer.NOT_AN_OBJECT_ERR + NotAnObject.class.getName() + ".getNonMap");
+    }
+
+    public static class MapData {
+        protected int id;
+        protected String value;
+        protected Map<String, List<Data>> map;
+
+        protected Map<String, List<Data>> getTheMap() {
+            return map;
+        }
+    }
+
+    /**
+     * The "lifted" property is not a JsonObject so it should throw an exception.
+     */
+    public static class NotAnObject {
+        protected String text;
+
+        public String getNonMap() {
+            return text;
+        }
+    }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/AnySetterDeserializerTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/AnySetterDeserializerTest.java
new file mode 100644
index 0000000..b180605
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/AnySetterDeserializerTest.java
@@ -0,0 +1,94 @@
+/*
+ * ============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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.gson.JacksonExclusionStrategy;
+import org.onap.policy.common.gson.internal.AnySetterDeserializer;
+import org.onap.policy.common.gson.internal.DataAdapterFactory.Data;
+
+public class AnySetterDeserializerTest {
+
+    private static DataAdapterFactory dataAdapter = new DataAdapterFactory();
+
+    private static Gson gson = new GsonBuilder().registerTypeAdapterFactory(dataAdapter)
+                    .setExclusionStrategies(new JacksonExclusionStrategy()).create();
+
+    private Set<String> set;
+    private AnySetterDeserializer deser;
+
+    /**
+     * Set up.
+     *
+     * @throws Exception if an error occurs
+     */
+    @Before
+    public void setUp() throws Exception {
+        set = new HashSet<>(Arrays.asList("id", "value"));
+        deser = new AnySetterDeserializer(gson, set,
+                        MapData.class.getDeclaredMethod("setItem", String.class, List.class));
+    }
+
+    @Test
+    public void testAnySetterDeserializer() {
+        JsonObject json = new JsonObject();
+
+        // these should not be copied
+        json.addProperty("id", 10);
+        json.addProperty("value", "the-value");
+
+        // these should be copied
+        DataAdapterFactory.addToObject(json);
+
+        MapData data = new MapData();
+        data.map = new TreeMap<>();
+
+        dataAdapter.reset();
+        deser.getFromTree(json, data);
+
+        assertTrue(dataAdapter.isDataRead());
+        assertNotNull(data.map);
+        assertEquals(DataAdapterFactory.makeMap().toString(), data.map.toString());
+    }
+
+    public static class MapData {
+        protected Map<String, List<Data>> map;
+
+        protected void setItem(String key, List<Data> value) {
+            map.put(key, value);
+        }
+    }
+
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/LifterTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/LifterTest.java
new file mode 100644
index 0000000..55a3b67
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/LifterTest.java
@@ -0,0 +1,55 @@
+/*
+ * ============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.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.Gson;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.onap.policy.common.gson.internal.Lifter;
+
+public class LifterTest {
+
+    private static Gson gson = new Gson();
+
+    @Test
+    public void testLifter_testShouldLift() throws Exception {
+        Set<String> set = new HashSet<>(Arrays.asList("abc", "def"));
+        Lifter lifter = new Lifter(gson, set, LifterTest.class.getDeclaredMethod("getValue"), String.class);
+
+        // should not lift these
+        assertFalse(lifter.shouldLift("abc"));
+        assertFalse(lifter.shouldLift("def"));
+
+        // should lift anything else
+        assertTrue(lifter.shouldLift("hello"));
+        assertTrue(lifter.shouldLift("world"));
+    }
+
+    public String getValue() {
+        return "";
+    }
+
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/MethodAdapterTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/MethodAdapterTest.java
new file mode 100644
index 0000000..9f39e3c
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/MethodAdapterTest.java
@@ -0,0 +1,58 @@
+/*
+ * ============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 com.google.gson.Gson;
+import com.google.gson.JsonParseException;
+import org.junit.Test;
+import org.onap.policy.common.gson.internal.MethodAdapter;
+
+public class MethodAdapterTest {
+    private static final Gson gson = new Gson();
+
+    private String saved;
+
+    @Test
+    public void testMethodAdapter_testInvoke() throws Exception {
+        MethodAdapter adapter =
+                        new MethodAdapter(gson, MethodAdapterTest.class.getDeclaredMethod("getValue"), String.class);
+        assertEquals("hello", adapter.invoke(this));
+
+        MethodAdapter adapter2 = new MethodAdapter(gson,
+                        MethodAdapterTest.class.getDeclaredMethod("setValue", String.class), String.class);
+        adapter2.invoke(this, "world");
+        assertEquals("world", saved);
+
+        assertThatThrownBy(() -> adapter2.invoke(this, 100)).isInstanceOf(JsonParseException.class)
+                        .hasMessage(MethodAdapter.INVOKE_ERR + MethodAdapterTest.class.getName() + ".setValue");
+    }
+
+    public String getValue() {
+        return "hello";
+    }
+
+    public void setValue(String val) {
+        saved = val;
+    }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/MethodDeserializerTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/MethodDeserializerTest.java
new file mode 100644
index 0000000..338644a
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/MethodDeserializerTest.java
@@ -0,0 +1,100 @@
+/*
+ * ============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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+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.MethodDeserializer;
+
+public class MethodDeserializerTest {
+    private static final String PROP_NAME = "text";
+    private static final String METHOD_NAME = "setText";
+    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 MethodDeserializer deser;
+
+    private String text;
+
+    private List<Data> listField;
+
+    @Test
+    public void testGetFromTree() throws Exception {
+        deser = new MethodDeserializer(gson, MethodDeserializerTest.class.getDeclaredMethod(METHOD_NAME, String.class));
+
+        // non-existent value - should not overwrite
+        text = INITIAL_VALUE;
+        JsonObject json = new JsonObject();
+        deser.getFromTree(json, this);
+        assertEquals(INITIAL_VALUE, text);
+
+        // null value - should not overwrite
+        text = INITIAL_VALUE;
+        json.add(PROP_NAME, JsonNull.INSTANCE);
+        deser.getFromTree(json, this);
+        assertEquals(INITIAL_VALUE, text);
+
+        // has a value - should store it
+        text = INITIAL_VALUE;
+        json.addProperty(PROP_NAME, NEW_VALUE);
+        deser.getFromTree(json, this);
+        assertEquals(NEW_VALUE, text);
+
+        /*
+         * check list field
+         */
+        deser = new MethodDeserializer(gson, MethodDeserializerTest.class.getDeclaredMethod("setTheList", List.class));
+
+        json = new JsonObject();
+        json.add("theList", DataAdapterFactory.makeArray());
+
+        dataAdapter.reset();
+        listField = null;
+        deser.getFromTree(json, this);
+
+        assertTrue(dataAdapter.isDataRead());
+        assertNotNull(listField);
+        assertEquals(DataAdapterFactory.makeList().toString(), listField.toString());
+    }
+
+    protected void setText(String text) {
+        this.text = text;
+    }
+
+    protected void setTheList(List<Data> lst) {
+        listField = lst;
+    }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/MethodSerializerTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/MethodSerializerTest.java
new file mode 100644
index 0000000..586bf54
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/MethodSerializerTest.java
@@ -0,0 +1,88 @@
+/*
+ * ============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.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 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.MethodSerializer;
+
+public class MethodSerializerTest {
+    private static final String PROP_NAME = "text";
+    private static final String METHOD_NAME = "getText";
+
+    private static DataAdapterFactory dataAdapter = new DataAdapterFactory();
+
+    private static Gson gson = new GsonBuilder().registerTypeAdapterFactory(dataAdapter)
+                    .setExclusionStrategies(new JacksonExclusionStrategy()).create();
+
+    private MethodSerializer ser;
+
+    private String text;
+
+    private List<Data> listField;
+
+    @Test
+    public void testAddToTree() throws Exception {
+        ser = new MethodSerializer(gson, MethodSerializerTest.class.getDeclaredMethod(METHOD_NAME));
+
+        // serialize null value first
+        text = null;
+
+        JsonObject json = new JsonObject();
+        ser.addToTree(this, json);
+        assertTrue(json.get(PROP_NAME).isJsonNull());
+
+        // serialize an actual value
+        text = "hello";
+        ser.addToTree(this, json);
+        assertEquals("hello", json.get(PROP_NAME).getAsString());
+
+        /*
+         * check list field
+         */
+        listField = DataAdapterFactory.makeList();
+
+        ser = new MethodSerializer(gson, MethodSerializerTest.class.getDeclaredMethod("getTheList"));
+
+        dataAdapter.reset();
+        JsonElement tree = ser.toJsonTree(listField);
+
+        assertTrue(dataAdapter.isDataWritten());
+        assertEquals(DataAdapterFactory.ENCODED_LIST, tree.toString());
+    }
+
+    protected String getText() {
+        return text;
+    }
+
+    protected List<Data> getTheList() {
+        return listField;
+    }
+}
diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/test/PolicyAssert.java b/utils-test/src/main/java/org/onap/policy/common/utils/test/PolicyAssert.java
deleted file mode 100644
index 43bb779..0000000
--- a/utils-test/src/main/java/org/onap/policy/common/utils/test/PolicyAssert.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP
- * ================================================================================
- * Copyright (C) 2018 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.utils.test;
-
-public class PolicyAssert {
-
-    /**
-     * Should not instantiate this.
-     */
-    private PolicyAssert() {
-        // do nothing
-    }
-
-    /**
-     * Invokes a function that is expected to throw an exception.
-     *
-     * @param clazz class of exception that is expected
-     * @param func function
-     * @return the exception that was thrown
-     * @throws AssertionError if the function does not throw an exception or throws the
-     *         wrong type of exception
-     */
-    public static <T extends Throwable> T assertThrows(Class<T> clazz, RunnableWithEx func) {
-        try {
-            func.run();
-
-        } catch (Throwable thrown) {
-            try {
-                return clazz.cast(thrown);
-
-            } catch (ClassCastException thrown2) {
-                throw new AssertionError("incorrect exception type", thrown2);
-            }
-        }
-
-        throw new AssertionError("missing exception");
-    }
-
-    /**
-     * Runnable that may throw an exception.
-     */
-    @FunctionalInterface
-    public static interface RunnableWithEx {
-        public void run() throws Throwable;
-    }
-}
diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/test/PolicyAssertTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/test/PolicyAssertTest.java
deleted file mode 100644
index f41d7b3..0000000
--- a/utils-test/src/test/java/org/onap/policy/common/utils/test/PolicyAssertTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * Common Utils-Test
- * ================================================================================
- * Copyright (C) 2018 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.utils.test;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.onap.policy.common.utils.test.PolicyAssert.assertThrows;
-
-import org.junit.Test;
-
-public class PolicyAssertTest {
-
-    private static final String EXPECTED = "expected exception";
-
-    @Test
-    public void test_ExpectedEx() {
-        // exact type
-        assertThrows(IllegalArgumentException.class, () -> {
-            throw new IllegalArgumentException(EXPECTED);
-        });
-
-        // cast to superclass is supported
-        assertThrows(RuntimeException.class, () -> {
-            throw new IllegalArgumentException(EXPECTED);
-        });
-
-        // supports errors
-        assertThrows(LinkageError.class, () -> {
-            throw new LinkageError(EXPECTED);
-        });
-
-        // supports any throwable
-        assertThrows(Throwable.class, () -> {
-            throw new Throwable(EXPECTED);
-        });
-    }
-
-    @Test
-    public void test_IncorrectEx() {
-        try {
-            assertThrows(IllegalStateException.class, () -> {
-                throw new IllegalArgumentException(EXPECTED);
-            });
-
-        } catch (AssertionError err) {
-            assertTrue(err.getMessage().contains("incorrect exception type"));
-            return;
-        }
-
-        fail("test failed for incorrect exception type");
-    }
-
-    @Test
-    public void test_MissingEx() {
-        try {
-            assertThrows(IllegalArgumentException.class, () -> {
-            });
-
-        } catch (AssertionError err) {
-            assertTrue(err.getMessage().contains("missing exception"));
-            return;
-        }
-
-        fail("test failed for missing exception");
-    }
-
-}